習(xí)目的:對(duì)檢索的內(nèi)容進(jìn)行模糊匹配,學(xué)習(xí)使用正則表達(dá)式進(jìn)行搜索。
學(xué)習(xí)內(nèi)容:WHERE字句的REGEXP字句。
SELECT [column_name] FROM [table_name] WHERE [column_name] REGEXP '正則表達(dá)式';
--正則表達(dá)式的寫法需要另外學(xué)習(xí),且MySQL支持的正則表達(dá)式也只是其子集。
拓展內(nèi)容:
--正則表達(dá)式是用來匹配文本的特殊的串(字符集合)。如果你想從一個(gè)文本文件中提取電話號(hào)碼,可以使用正則表達(dá)式。如果你需要查找名字中間有數(shù)字的所有文件,可以使用如果你想替換一個(gè)頁面中的所有URL為這些URL的實(shí)際HTML鏈接,也可以使用一個(gè)正則表達(dá)式所有種類的程序設(shè)計(jì)語言、文本編輯器、操作系統(tǒng)等都支持正則表達(dá)式。
--LIKE匹配整個(gè)列。如果被匹配的文本在列值中出現(xiàn),LIKE將不會(huì)找到它,相應(yīng)的行也不被返回(除非使用通配符)。而REGEXP在列值內(nèi)進(jìn)行匹配,如果被匹配的文本在列值中出現(xiàn),REGEXP將會(huì)找到它,相應(yīng)的行將被返回。這是一個(gè)非常重要的差別。那么,REGEXP能不能用來匹配整個(gè)列值(從而起與LIKE相同的作用)?答案是肯定的,使用^和$定位符(anchor)即可。
者 | 不愿透露姓名の網(wǎng)友
出品 | CSDN博客
介紹爬蟲
1.爬蟲:自動(dòng)的抓取互聯(lián)網(wǎng)上信息的腳本文件。
2.爬蟲可以解決的問題:
(1)解決冷啟動(dòng)問題
(2)搜索引擎的根基:做搜索引擎少不了爬蟲
(3)建立知識(shí)圖譜,幫助建立機(jī)器學(xué)習(xí)知識(shí)圖譜
(4)可以制作各種商品的比價(jià)軟件,趨勢(shì)分析。
3.爬蟲分類
(1)通用爬蟲:搜索引擎的主要組成,作用就是將互聯(lián)網(wǎng)的上頁面整體的爬取下來之后,保存到本地。
(2)聚焦爬蟲:聚焦爬蟲在實(shí)施網(wǎng)頁抓取時(shí)會(huì)對(duì)內(nèi)容進(jìn)行處理篩選,盡量保證只抓取與需求相關(guān)的網(wǎng)頁信息。
通用爬蟲和聚焦爬蟲的區(qū)別:聚焦爬蟲在實(shí)施網(wǎng)頁抓取時(shí)會(huì)對(duì)內(nèi)容進(jìn)行處理篩選,盡量保證只抓取與需求相關(guān)的網(wǎng)頁信息。
4.爬蟲遵循的協(xié)議:robot協(xié)議
定義:網(wǎng)絡(luò)爬蟲排除標(biāo)準(zhǔn)。
作用:告訴搜索引擎哪里可以爬,哪里不可以爬。
5.通用爬蟲工作流程
(1)抓取網(wǎng)頁:通過搜索引擎將待爬取的url加入到通用爬蟲的url隊(duì)列中,進(jìn)行網(wǎng)頁內(nèi)容的爬取
(2)數(shù)據(jù)存儲(chǔ):將爬取下來的網(wǎng)頁保存到本地,這個(gè)過程會(huì)有一定的去重操作,如果某個(gè)網(wǎng)頁的內(nèi) 容大部分內(nèi)容都會(huì)重復(fù),搜索引擎可能不會(huì)保存。
(3)預(yù)處理:提取文字,中文分詞,消除噪音(比如版權(quán)聲明文字,導(dǎo)航條,廣告等)。
(4)設(shè)置網(wǎng)站排名,為用戶提供服務(wù)。
6.一些反爬及其應(yīng)對(duì)措施
( 1)通過user-agent來判斷是否是爬蟲。
解決方案:可以通過偽裝請(qǐng)求頭中的user-agent來解決。若user-agent被檢測(cè)到,可以找大量的user-agent,放入列表,然后進(jìn)行更換
(2)將IP進(jìn)行封殺。
解決方案:可以通過代理來偽裝IP。
(3)通過訪問頻率來判斷是否是一個(gè)爬蟲。
解決方案:可以通過設(shè)置請(qǐng)求間隔,和爬取間隔。
(4)當(dāng)一定時(shí)間內(nèi)的總請(qǐng)求數(shù)超過上限,彈出驗(yàn)證碼。
解決方案:對(duì)于簡單的驗(yàn)證碼圖片可以使用tesseract來處理,對(duì)于復(fù)雜的可以去打碼平臺(tái)。
(5)通過JS來獲取頁面數(shù)據(jù)。
解決方案:可以使用selenium+phantomjs來加載JS獲取數(shù)據(jù)。
介紹搜索引擎
1.搜索引擎的主要組成
通用爬蟲:就是將互聯(lián)網(wǎng)的上頁面整體的爬取下來之后,保存到本地。
通用爬蟲要想爬取網(wǎng)頁,需要網(wǎng)站的url.但是搜索引擎是可以搜索所有網(wǎng)頁的。那么通用爬蟲url就要涉及到所有網(wǎng)頁,這個(gè)‘所有’是如何做到的:
新網(wǎng)站向搜索引擎主動(dòng)提交網(wǎng)址;
在其他網(wǎng)站上設(shè)置新網(wǎng)站外鏈;
搜索引擎和DNS解析服務(wù)商(如DNSPod等)合作,新網(wǎng)站域名將被迅速抓取。
2.搜索引擎的工作流程(通用爬蟲的工作流程)
(1)抓取網(wǎng)頁:通過搜索引擎將待爬取的URL加入到通用爬蟲的URL隊(duì)列中,進(jìn)行網(wǎng)頁內(nèi)容的爬取。
(2)數(shù)據(jù)存儲(chǔ):將爬取下來的網(wǎng)頁保存到本地,這個(gè)過程會(huì)有一定的去重操作,如果某個(gè)網(wǎng)頁的內(nèi) 容大部分內(nèi)容都會(huì)重復(fù),搜索引擎可能不會(huì)保存。
(3)預(yù)處理:提取文字,中文分詞,消除噪音(比如版權(quán)聲明文字,導(dǎo)航條,廣告等)。
(4)設(shè)置網(wǎng)站排名,為用戶提供服務(wù)。
3.搜索引擎的局限性
(1)搜索引擎只能爬取原網(wǎng)頁,但是頁面90%內(nèi)容都是無用的。
(2)搜索引擎不能滿足不同行業(yè),不同人的特定需求。
(3)通用搜索引擎只能爬取文字信息,不能對(duì)音頻、圖片等進(jìn)行爬取。
(4)只能基于關(guān)鍵字查詢,無法基于語義查詢。
介紹HTTP
網(wǎng)絡(luò)七層協(xié)議:
1.HTTP協(xié)議特點(diǎn)
HTTP協(xié)議是超文本傳輸協(xié)議;
HTTP協(xié)議是一個(gè)應(yīng)用層協(xié)議;
無連接:每次請(qǐng)求都是獨(dú)立的;
無狀態(tài),表示客戶端每次請(qǐng)求都不能記錄請(qǐng)求狀態(tài),就是兩條請(qǐng)求直接不可通信。
2.HTTP工作過程
地址進(jìn)行DNS解析,將URL解析出對(duì)應(yīng)的內(nèi)容
封裝HTTP請(qǐng)求數(shù)據(jù)包
封裝成TCP包,建立TCP連接(TCP的三次握手)
客戶端發(fā)送請(qǐng)求
服務(wù)器接收請(qǐng)求,發(fā)送響應(yīng)
客戶端接收到響應(yīng),進(jìn)行頁面渲染
服務(wù)器關(guān)閉TCP連接(TCP的四次揮手)
3.HTTP協(xié)議和HTTPS協(xié)議的區(qū)別
HTTP協(xié)議是使用明文數(shù)據(jù)傳輸?shù)木W(wǎng)絡(luò)協(xié)議,明文傳輸會(huì)讓用戶存在一個(gè)非常大的安全隱患。端口80
HTTPS協(xié)議可以理解為HTTP協(xié)議的安全升級(jí)版,就是在HTTP的基礎(chǔ)上增加了數(shù)據(jù)加密。端口443
HTTPS協(xié)議是由 SSL+HTTP 協(xié)議構(gòu)建的可進(jìn)行加密傳輸、身份認(rèn)證的網(wǎng)絡(luò)協(xié)議要比HTTP協(xié)議安全。
4.HTTP通信
HTTP通信由兩部分組成:客戶端請(qǐng)求消息與服務(wù)器響應(yīng)消息。
5.關(guān)于響應(yīng)常見的響應(yīng)碼
6.客戶端請(qǐng)求(Get和Post區(qū)別)
(1)組成:請(qǐng)求行、請(qǐng)求頭部、空行、請(qǐng)求數(shù)據(jù)四個(gè)部分組成
(2)請(qǐng)求方法Get/Post
(3)Get和Post的區(qū)別
GET和POST本質(zhì)上就是TCP鏈接,并無差別。但是由于HTTP的規(guī)定和瀏覽器/服務(wù)器的限制,導(dǎo)致他們?cè)趹?yīng)用過程中體現(xiàn)出一些不同。
(4)常見的請(qǐng)求頭
User-Agent:客戶端請(qǐng)求標(biāo)識(shí)。
Accept:傳輸文件類型。
Referer :請(qǐng)求來源。
cookie (cookie):在做登錄的時(shí)候需要封裝這個(gè)頭。
Content-Type (POST數(shù)據(jù)類型)
7.服務(wù)器響應(yīng)
(1)組成:狀態(tài)行,響應(yīng)頭,空行,響應(yīng)正文。
(2)常見的響應(yīng)頭
Content-Type:text/html;資源文件的類型,還有字符編碼
Content-Length:響應(yīng)長度
Content-Size響應(yīng)大小
Content-Encoding告訴客戶端,服務(wù)端發(fā)送的資源是采用什么編碼的。
Connection:keep-alive這個(gè)字段作為回應(yīng)客戶端的Connection:keep-alive,告訴客戶端服務(wù)器的tcp連接也是一個(gè)長連接,客戶端可以繼續(xù)使用這個(gè)TCP連接發(fā)送HTTP請(qǐng)求
url
統(tǒng)一資源定位符:
基本格式:scheme://host[:port#]/path/…/?query-string
協(xié)議://服務(wù)器ip地址:端口號(hào)/資源路徑/?key1=參數(shù)1&key2=參數(shù)2
scheme:協(xié)議(例如:HTTP、HTTPS、FTP)
host/IP:服務(wù)器的IP地址或者域名
port:服務(wù)器的端口(如果是走協(xié)議默認(rèn)端口,缺省端口80),用來從互聯(lián)網(wǎng)進(jìn)入電腦
path:訪問資源的路徑,就是為了在電腦中找到對(duì)應(yīng)的資源路徑
query-string:參數(shù),發(fā)送給http服務(wù)器的數(shù)據(jù)
anchor:錨(跳轉(zhuǎn)到網(wǎng)頁的指定錨點(diǎn)位置)
Q:當(dāng)我們?cè)跒g覽器輸入一個(gè)URL,為什么可以加載出一個(gè)頁面?為什么抓包的過程中請(qǐng)求一個(gè)URL,出現(xiàn)很多的資源請(qǐng)求?
當(dāng)我們?cè)跒g覽器輸入一個(gè)URL,客戶端會(huì)發(fā)送這個(gè)URL對(duì)應(yīng)的一個(gè)請(qǐng)求到服務(wù)器獲取內(nèi)容。服務(wù)器收到這個(gè)請(qǐng)求,解析出對(duì)應(yīng)內(nèi)容,之后將內(nèi)容封裝到響應(yīng)里發(fā)送到客戶端
當(dāng)客戶端拿到這個(gè)HTML頁面,會(huì)查看這個(gè)頁面中是否有CSS、JS、image等URL,如果有,在分別進(jìn)行請(qǐng)求,獲取到這些資源。
客戶端會(huì)通過HTML的語法,將獲取到的所有內(nèi)容完美的顯示出來。
Cookie和Session
產(chǎn)生原因:由于HTTP是一個(gè)無狀態(tài)的協(xié)議,每次請(qǐng)求如果需要之前的一些信息,無法記錄,因此為了解決這個(gè)問題,產(chǎn)生了一種記錄狀態(tài)技術(shù),Cookie和Session。
Cookie指某些網(wǎng)站為了辨別用戶身份,進(jìn)行會(huì)話跟蹤而存儲(chǔ)在用戶本地終端上的數(shù)據(jù),種類有會(huì)話Cookie和持久Cookie。
(1)會(huì)話Cookie指存在瀏覽器內(nèi)存的Cookie,當(dāng)瀏覽器關(guān)閉,會(huì)話Cookie會(huì)失效;
(2)持久Cookie是保存在硬盤上的Cookie。
Session用來存儲(chǔ)特定的用戶會(huì)話所需的屬性及其配置信息。
Cookie是在客戶端記錄狀態(tài),Session是在服務(wù)端記錄狀態(tài)。
聯(lián)系:當(dāng)客戶端發(fā)送一個(gè)Cookie,服務(wù)器會(huì)從這個(gè)Cookie中找到sessionID,再查找出相應(yīng)的Session信息返回給客戶端,來進(jìn)行用戶頁面的流轉(zhuǎn)。如果通過sessionID來查找Session的時(shí)候,發(fā)現(xiàn)沒有Session(一般第一次登陸或者清空了瀏覽器),那么就會(huì)創(chuàng)建一個(gè)Session。
hashlib密碼加密
def get_hex(value):
md5_=hashlib.md5
md5_.update(value.encode('utf-8'))
return md5_.hexdigest
關(guān)于response.text亂碼問題
response的常用屬性:
1.獲取字符串類型的響應(yīng)正文:response.text
2.獲取bytes類型的響應(yīng)正文:response.content
3.響應(yīng)正文字符串編碼:response.encoding
4.狀態(tài)碼:response.status_code
5.響應(yīng)頭:response.headers
response.text亂碼問題:
#方法一:轉(zhuǎn)換成utf-8格式
response.encoding='utf-8'
print(response.text)
#方法二:解碼為utf-8 :
with open('index.html','w',encoding='utf-8') as fp: fp.write(response.content.decode('utf-8'))
代理
代理的作用:
1、突破自身IP 訪問限制, 訪問一些平時(shí)不能訪問的站點(diǎn)。
2、訪問一些單位或團(tuán)體內(nèi)部資源:比如使用教育網(wǎng)內(nèi)地址段免費(fèi)代理服務(wù)器, 就可以用于對(duì)教育網(wǎng)開放的各類FTP 下載上傳, 以及各類資料查詢共享等服務(wù)。
3、提高訪問速度:通常代理服務(wù)器都設(shè)置一個(gè)較大的硬盤緩沖區(qū),當(dāng)有外界的信息通過時(shí), 同時(shí)也將其保存到緩沖區(qū)中,當(dāng)其他用戶再訪問相同的信息時(shí),則直接由緩沖區(qū)中取屮信息傳給用戶,以提高訪問速度。
4、隱藏真實(shí)IP :上網(wǎng)者也可以通過這種方法隱藏自己的IP , 免受攻擊。對(duì)于爬蟲來說, 我們用代理就是為了隱藏自身IP , 防止自身的被封鎖。
代理根據(jù)匿名程度的分類:
JSON數(shù)據(jù)
數(shù)據(jù)的分類:
JSON的本質(zhì):是一個(gè)字符串,JSON是對(duì)JS對(duì)象的字符串表達(dá)式,它使用文本形式表示一個(gè)JS對(duì)象的信息。
JSON使用:
(1)json.dumps(Python的list或者dict),將Python的list或者dict返回為一個(gè)JSON字符串;
(2)json.loads(json字符串),將JSON字符串返回為Python的list或者dict;
(3)json.dump(list/dict,fp),將Python的list或者dict轉(zhuǎn)為一個(gè)JSON字符串,保存到文件中;
(4)json.load(fp) ,從JSON文件中讀出JSON數(shù)據(jù),并轉(zhuǎn)換為Python的list或者dict。
正則表達(dá)式
1、貪婪和非貪婪
(1)正則默認(rèn)是貪婪模式,所以數(shù)量控制符默認(rèn)是取最大值,也是貪婪。例如*
(2)非貪婪是用?來控制,盡量匹配最少的次數(shù),0次或一次。
2、Python使用格式:
pattern=re.compile('正則表達(dá)式')
print(pattern.match(字符串,start,end))#默認(rèn)從頭開始匹配,只匹配一次,返回一個(gè)match對(duì)象
print(pattern.search(字符串,start,end))#從任意位置開始匹配,只匹配一次,返回一個(gè)match對(duì)象
print(pattern.findall(字符串,start,end))#全文多次匹配,將匹配到的結(jié)果放到一個(gè)list返回給我們
print(pattern.finditer(字符串,start,end))#全文多次匹配,將匹配到的結(jié)果放到一個(gè)match對(duì)象的迭代器返回
3、常見的正則題目
XML
XML的基本知識(shí):
1.定義:XML稱為可拓展性標(biāo)記語言,類似 HTML;
2.特點(diǎn):XML具有自描述特性,是一種半結(jié)構(gòu)化數(shù)據(jù);
3.作用:XML的設(shè)計(jì)宗旨是傳輸數(shù)據(jù),而非顯示數(shù)據(jù)。可以作為配置文件使用來記錄一些重要信息;
4.XML的標(biāo)簽需要我們自行定義。
HTML和XML 區(qū)別:
語法方面:
在HTML中不區(qū)分大小寫,在XML中嚴(yán)格區(qū)分大小寫
在HTML中,在某些情況可以省略閉合標(biāo)簽。在XML中,絕對(duì)不能省略任何標(biāo)記。
在XML中,單標(biāo)簽結(jié)尾處需要加’/’。
XML文檔中,空白部分不會(huì)被解析器自動(dòng)刪除,但是HTML是過濾掉空格的
在XML中,屬性值必須封裝在引號(hào)中。在HTML中,引號(hào)可用可不用。
在HTML中屬性名可以不帶屬性值,XML必須帶屬性值而且不能為空。
標(biāo)記不同:
HTML使用固有的標(biāo)記,XML沒有固有標(biāo)記。
作用不同:
XML主要用來傳輸數(shù)據(jù),HTML主要用來顯示數(shù)據(jù)。
Selenium+PhantomJS
Selenium:Web自動(dòng)測(cè)試工具。
PantomJS:是一個(gè)無界面的瀏覽器,所以他可以運(yùn)行JS代碼,幫我們拿到頁面數(shù)據(jù)。
特點(diǎn):功能強(qiáng)大,幾乎可以對(duì)付任何網(wǎng)頁,但會(huì)導(dǎo)致代碼效率低。
Selenium 是一個(gè) Web 的自動(dòng)化測(cè)試工具,可以根據(jù)我們的指令,讓瀏覽器自動(dòng)加載頁面,獲取需要的數(shù)據(jù),甚至頁面截屏,或者判斷網(wǎng)站上某些動(dòng)作是否發(fā)生。Selenium 自己不帶瀏覽器,不支持瀏覽器的功能,它需要與第三方瀏覽器結(jié)合在一起才能使用。但是我們有時(shí)候需要讓它內(nèi)嵌在代碼中運(yùn)行, 所以我們可以用一個(gè)PhantomJS 的工具代替真實(shí)的瀏覽器。Selenium 庫里有個(gè)叫 WebDriver 的 API。WebDriver 有點(diǎn)兒像可以加載網(wǎng)站的瀏覽器,但是它也可以查找頁面元素,與頁面上的元 素進(jìn)行交互 (發(fā)送文本、點(diǎn)擊等),以及執(zhí)行其他動(dòng)作來運(yùn)行網(wǎng)絡(luò)爬蟲。
PhantomJS 是一個(gè)基于 Webkit 的“無界面”瀏覽器,它會(huì)把網(wǎng)站加載到內(nèi)存并執(zhí)行頁面上的 JavaScript,因?yàn)椴粫?huì)展示圖形界面,所以運(yùn)行起來比完整的瀏覽器要高效。相比傳統(tǒng)的 Chrome或 Firefox 瀏覽器等,資源消耗會(huì)更少。如果我們把 Selenium 和 PhantomJS 結(jié)合在一起,就可以運(yùn)行一個(gè)非常強(qiáng)大的網(wǎng)絡(luò)爬蟲了,這個(gè)爬蟲可以處理 JavaScript、Cookie、headers,以及任 何我們真實(shí)用戶需要做的事情。主程序退出后,selenium 不保證 phantomJS 也成功退出,最好手動(dòng)關(guān)閉 phantomJS 進(jìn)程。(有可能會(huì)導(dǎo)致多個(gè) phantomJS 進(jìn)程運(yùn)行,占用內(nèi)存)。WebDriverWait 雖然可能會(huì)減少延時(shí),但是目前存在 bug(各種報(bào)錯(cuò)),這種 情況可以采用 sleep。phantomJS 爬數(shù)據(jù)比較慢,可以選擇多線程。如果運(yùn)行的時(shí)候發(fā)現(xiàn)有的可 以運(yùn)行,有的不能,可以嘗試將 phantomJS 改成 Chrome。
實(shí)現(xiàn)模擬登錄的方式有哪些
1.直接使用已知的Cookie訪問
先用瀏覽器登錄,獲取瀏覽器里的cookie字符串,然后封裝至請(qǐng)求頭。
2.模擬登錄后用session保持登錄狀態(tài)
使用session模擬登陸后,就會(huì)自動(dòng)存儲(chǔ)一個(gè)cookie次從而保持住登錄狀態(tài)。
3.使用Selenium+PhantomJS訪問
Selenium庫提供了find_element(s)_by_xxx的方法來找到網(wǎng)頁中的輸入框、按鈕等元素。其中xxx可以是id、name、tag_name(標(biāo)簽名)、class_name(class),也可以是xpath(xpath表達(dá)式)等等。當(dāng)然還是要具體分析網(wǎng)頁源代碼。
線程+進(jìn)程+多線程
關(guān)系:一個(gè)程序至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程至少有一個(gè)線程。
線程的順序:
新建:線程創(chuàng)建(t=threading.Thread(target=方法名)或者線程類)
就緒:當(dāng)啟動(dòng)線程后,線程就進(jìn)入就緒狀態(tài),就緒狀態(tài)的線程會(huì)被放到一個(gè)CPU調(diào)度隊(duì)列里面,cpu會(huì)負(fù)責(zé)讓其中的線程運(yùn)行,變?yōu)檫\(yùn)行狀態(tài)。
運(yùn)行狀態(tài):CPU調(diào)度一個(gè)就緒狀態(tài)的線程,該線程就變?yōu)檫\(yùn)行狀態(tài)。
阻塞狀態(tài):當(dāng)運(yùn)行狀態(tài)的線程被阻塞變?yōu)樽枞麪顟B(tài),阻塞狀態(tài)的線程就會(huì)重新變?yōu)榫途w狀態(tài)才能繼續(xù)運(yùn)行。
死亡狀態(tài):線程執(zhí)行完畢。
多線程和多進(jìn)程優(yōu)缺點(diǎn)
多線程的優(yōu)點(diǎn):
程序邏輯和控制方式復(fù)雜;
所有線程可以直接共享內(nèi)存和變量;
線程方式消耗的總資源比進(jìn)程方式好。
多線程缺點(diǎn):
每個(gè)線程與主程序共用地址空間,受限于2GB地址空間;
線程之間的同步和加鎖控制比較麻煩;
一個(gè)線程的崩潰可能影響到整個(gè)程序的穩(wěn)定性;
多進(jìn)程優(yōu)點(diǎn):
每個(gè)進(jìn)程互相獨(dú)立,不影響主程序的穩(wěn)定性,子進(jìn)程崩潰沒關(guān)系;
通過增加CPU,就可以容易擴(kuò)充性能;
每個(gè)子進(jìn)程都有2GB地址空間和相關(guān)資源,總體能夠達(dá)到的性能上限非常大 。
多進(jìn)程缺點(diǎn):
邏輯控制復(fù)雜,需要和主程序交互;
需要跨進(jìn)程邊界,如果有大數(shù)據(jù)量傳送,就不太好,適合小數(shù)據(jù)量傳送、密集運(yùn)算 多進(jìn)程調(diào)度開銷比較大。
在實(shí)際開發(fā)中,選擇多線程和多進(jìn)程應(yīng)該從具體實(shí)際開發(fā)來進(jìn)行選擇。最好是多進(jìn)程和多線程結(jié)合。
互斥鎖和死鎖和GLF鎖
1.互斥鎖
定義:當(dāng)多個(gè)線程幾乎同時(shí)修改某一個(gè)共享數(shù)據(jù)的時(shí)候,需要進(jìn)行同步控制。線程同步能夠保證多個(gè)線程安全訪問“競爭資源”,最簡單的同步機(jī)制就是引用互斥鎖。
由于線程共享進(jìn)程的內(nèi)存空間和數(shù)據(jù),因此在某個(gè)線程要共享數(shù)據(jù)時(shí),先將其鎖定,此時(shí)資源的狀態(tài)為“鎖定”,其他線程不能更改;直到該線程釋放資源,將資源的狀態(tài)變成“非鎖定”,其他的線程才能再次鎖定該資源。互斥鎖保證了每次只有一個(gè)線程進(jìn)入寫入操作,從而保證了多線程情況下數(shù)據(jù)的正確性。互斥鎖體現(xiàn)的就是一個(gè)同步的機(jī)制,即該線程釋放資源就是一個(gè)條件,條件完成,才能執(zhí)行下一步操作。是微觀的操作。
鎖的好處:
(1)確定了某段代碼只能由一個(gè)線程從頭到尾完整地執(zhí)行。
(2)全局變量的安全
鎖的壞處:
(1)阻止了多線程的并發(fā)執(zhí)行,包含鎖的某段代碼實(shí)際上只能以單線程模塊執(zhí)行,效率降低
(2)由于可以存在多個(gè)鎖,不同的線程持有不同的鎖,并試圖獲取對(duì)方持有的鎖的時(shí),可能會(huì)造成“死鎖”。
2.死鎖
(1)同一個(gè)線程先后兩次調(diào)用lock,在第二次調(diào)用時(shí),由于鎖已經(jīng)被自己占用,該線程會(huì)掛起等待自己釋放鎖,由于該線程已被掛起而沒有機(jī)會(huì)釋放鎖,因此 它將一直處于掛起等待狀態(tài),變?yōu)樗梨i;
(2)線程A獲得了鎖1,線程B獲得了鎖2,這時(shí)線程A調(diào)用lock試圖獲得鎖2,結(jié)果是需要掛起等待線程B釋放鎖2,而這時(shí)線程B也調(diào)用lock試圖獲得鎖1,結(jié)果是需要掛起等待線程A釋放鎖1,于是線程A和B都在等待對(duì)方釋放自己才釋放,從而造成兩個(gè)都永遠(yuǎn)處于掛起狀態(tài),造成死鎖。
3.有GLF鎖為什么還要有互斥鎖
(1)為什么設(shè)計(jì)GLF鎖?
Python 在設(shè)計(jì)之初就考慮到要在解釋器的主循環(huán)中,同時(shí)只有一個(gè)線程在執(zhí)行,即在任意時(shí)刻,只有一個(gè)線程在解釋器中運(yùn)行。對(duì)Python 虛擬機(jī)的訪問由全局解釋器鎖(GIL)來控制,正是GIL鎖能保證同一時(shí)刻只有一個(gè)線程在運(yùn)行。
(2)宏觀微觀考慮GLF鎖?
GIL是宏觀的操作。比如在一個(gè)4核的環(huán)境下,只有一個(gè)核是運(yùn)行著線程,而其他三個(gè)核是空的。GIL是線程鎖,針對(duì)線程,而不是進(jìn)程。
MongoDB
MongoDB和傳統(tǒng)的關(guān)系型數(shù)據(jù)庫區(qū)別:
1.傳統(tǒng)數(shù)據(jù)庫特點(diǎn)是存儲(chǔ)結(jié)構(gòu)化數(shù)據(jù),數(shù)據(jù)以行為單位,每行的數(shù)據(jù)結(jié)構(gòu)和類型相同。
2.MongoDB存儲(chǔ)的是文檔,每個(gè)文檔得結(jié)構(gòu)可以不相同,能夠更便捷的獲取數(shù)據(jù)。
3.MongoDB的集合不需要提前創(chuàng)建,可隱式創(chuàng)建,而關(guān)系型數(shù)據(jù)庫的表需要提前定義。
4.MongoDB第三方支持豐富。(這是與其他的NoSQL相比,MongoDB也具有的優(yōu)勢(shì))。
5.MongoDB不支持事務(wù)操作。
6.MongoDB支持大容量的存儲(chǔ),占用空間過大。
MongoDB和SQL的對(duì)比:
游標(biāo):
定義:由于MongoDB底層是JS編寫,因此可以使用一些JS代碼,通俗的說游標(biāo)不是查詢結(jié)果,而是查詢的返回資源或者接口,就像Python中的生成器,通過這個(gè)生成器,可以一次一次的獲取每一個(gè)資源。
持久化:日志和快照對(duì)比
快照和日志兩者如何選擇?
推薦兩種共同使用:
1.如果Redis僅僅是用來做為緩存服務(wù)器的話,我們可以不使用任何的持久化。
2.一般情況下我們會(huì)將兩種持久化的方式都開啟。redis優(yōu)先加載AOF文件來回復(fù)數(shù)據(jù)。RDB的好處是快速。
3.在主從節(jié)點(diǎn)中,RDB作為我們的備份數(shù)據(jù),只在salve(從節(jié)點(diǎn))上啟動(dòng),同步時(shí)間可以設(shè)置的長一點(diǎn),只留(save 900 1)這條規(guī)則就可以了。
4.開啟AOF的情況下,主從同步是時(shí)候必然會(huì)帶來IO的性能影響,此時(shí)我們可以調(diào)大auto-aof-rewrite-min-size的值,比如5GB。來減少IO的頻率
5.不開啟AOF的情況下,可以節(jié)省IO的性能影響,這是主從間通過RDB持久化同步,但如果主從都掛掉,影響較大。
在dump rdb過程中,aof如果停止同步,會(huì)不會(huì)丟失?
答: 不會(huì),所有的操作緩存在內(nèi)存的隊(duì)列里,dump完成后,統(tǒng)一操作。
aof重寫是指什么?
答:由于日志保存的是所有操作命令,導(dǎo)致存的日志會(huì)過大,而且數(shù)據(jù)庫中有可能數(shù)據(jù)進(jìn)行過刪除,因此日志中的一些命令就相當(dāng)于無效,因此日志先會(huì)刪除,然后內(nèi)存中的數(shù)據(jù)會(huì)逆化成命令,再重新寫入到日志文件中,以解決 aof日志過大的問。
如果rdb文件和aof文件都存在,優(yōu)先用誰來恢復(fù)數(shù)據(jù)?
答: 在這種情況下,當(dāng)Redis重啟的時(shí)候會(huì)優(yōu)先載入AOF文件來恢復(fù)原始的數(shù)據(jù),因?yàn)樵谕ǔG闆r下AOF文件保存的數(shù)據(jù)集要比RDB文件完整。
恢復(fù)時(shí)rdb和aof哪個(gè)恢復(fù)的快?
答: rdb快,因?yàn)槠涫菙?shù)據(jù)的內(nèi)存映射,直接載入到內(nèi)存,而aof是命令,需要逐條執(zhí)行。
Scrapy和Scrapy-Redis
什么是Scrapy?
Scrapy 是一個(gè)快速、高層次的基于 Python 的 Web 爬蟲構(gòu)架,它用于抓取Web 站點(diǎn)并從頁面中提取結(jié)構(gòu)化的數(shù)據(jù)。Scrapy 使用了 Twisted異步網(wǎng)絡(luò)庫來處理網(wǎng)絡(luò)通訊。
Scrapy 常應(yīng)用在包括數(shù)據(jù)挖掘,信息處理或存儲(chǔ)歷史數(shù)據(jù)等一系列的程序中。
Scrapy優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
Scrapy 是異步的(Scrapy 框架的異步機(jī)制是基于 twisted 異步網(wǎng)絡(luò)框架處理的,在 settings.py 文件里可以設(shè)置具體的并發(fā)量數(shù)值(默認(rèn)是并發(fā)量 16))
采取可讀性更強(qiáng)的xpath代替正則
強(qiáng)大的統(tǒng)計(jì)和log系統(tǒng)
同時(shí)在不同的url上爬行
支持shell方式,方便獨(dú)立調(diào)試
寫middleware,方便寫一些統(tǒng)一的過濾器
通過管道的方式存入數(shù)據(jù)庫
缺點(diǎn):
基于Python的爬蟲框架,擴(kuò)展性比較差
基于twisted框架,運(yùn)行中的exception是不會(huì)干掉reactor,并且異步框架出錯(cuò)后是不會(huì)停掉其他任務(wù)的,數(shù)據(jù)出錯(cuò)后難以察覺。
Scrapy的組件:
Scrapy和scrapy-redis的區(qū)別:
Scrapy是一個(gè)Python爬蟲框架,爬取效率極高,具有高度定制性,但是不支持分布式。而scrapy-redis一套基于redis數(shù)據(jù)庫、運(yùn)行在Scrapy框架之上的組件,可以讓scrapy支持分布式策略,Slaver端共享Master端Redis數(shù)據(jù)庫里的item隊(duì)列、請(qǐng)求隊(duì)列和請(qǐng)求指紋集合
為什么選擇Redis數(shù)據(jù)庫,因?yàn)镽edis支持主從同步,而且數(shù)據(jù)都是緩存在內(nèi)存中的,所以基于Redis的分布式爬蟲,對(duì)請(qǐng)求和數(shù)據(jù)的高頻讀取效率非常高。
Redis的優(yōu)點(diǎn):
1.數(shù)據(jù)讀取快,因?yàn)閿?shù)據(jù)都放在內(nèi)存上
2.支持事務(wù)watch
3.數(shù)據(jù)持久化,支持快照和日志,方便恢復(fù)數(shù)據(jù)
4.擁有豐富的數(shù)據(jù)類型:list,string,set,qset,hash
5.支持主從復(fù)制,可以進(jìn)行數(shù)據(jù)備份
6.豐富的特性:可以作為緩存,消息隊(duì)列,設(shè)置過期時(shí)間,到期自動(dòng)刪除
分布式爬蟲主要解決什么問題?
IP、帶寬、CPU、io
Scrapy如何實(shí)現(xiàn)分布式抓取?
可以借助scrapy_redis類庫來實(shí)現(xiàn)。
在分布式爬取時(shí),會(huì)有master機(jī)器和slave機(jī)器,其中,master為核心服務(wù)器,slave為具體的爬蟲服務(wù)器。
我們?cè)趍aster服務(wù)器上搭建一個(gè)Redis數(shù)據(jù)庫,并將要抓取的url存放到redis數(shù)據(jù)庫中,所有的slave爬蟲服務(wù)器在抓取的時(shí)候從redis數(shù)據(jù)庫中去鏈接,由于scrapy_redis自身的隊(duì)列機(jī)制,slave獲取的url不會(huì)相互沖突,然后抓取的結(jié)果最后都存儲(chǔ)到數(shù)據(jù)庫中。master的redis數(shù)據(jù)庫中還會(huì)將抓取過的url的指紋存儲(chǔ)起來,用來去重。相關(guān)代碼在dupefilter.py文件中的request_seen方法中可以找到。
定時(shí)任務(wù):
1.可以設(shè)計(jì)一個(gè)定時(shí)的類,使用時(shí)間模塊(time,datetime),比如爬取時(shí)候獲取當(dāng)前時(shí)間,再當(dāng)前時(shí)間多久后再爬取
2.使用linux的crontab的計(jì)劃任務(wù)
crontab -l 列出所有的定時(shí)任務(wù) 看不到配置文件中寫的定時(shí)任務(wù)
crontab -e 新增計(jì)劃任務(wù) 跟上面的區(qū)別在于 沒有用戶名
crontab -r 清空計(jì)劃任務(wù)
分 時(shí) 日 月 周 命令
舉例使用:
* * * * * 命令 每分每時(shí)每天每月每周 執(zhí)行這個(gè)命令
0-59 0-23 1-31 1-12 0-6 0是 周天 1-6 周一到周六
0 2 * * * mysqldump 每天的2點(diǎn)備份數(shù)據(jù)庫
0 2 * * 2 sync 每個(gè)周二的2點(diǎn)做數(shù)據(jù)同步
0 8 15 * * /home/jsgz.py 每個(gè)月15號(hào)的八點(diǎn)給大家算工資
0 */2 * * * /home/camera.py 每隔2個(gè)小時(shí)執(zhí)行一次查看攝像頭
0 8,12,18 * * 1-5 kq.py 每周1-5 的 8點(diǎn) 12點(diǎn) 18點(diǎn) 執(zhí)行打卡
0 8 * * * * spider.sh 每天 8點(diǎn)爬蟲
爬蟲框架、模塊
Python自帶:urllib,urllib2。
urllib和urllib2模塊都做與請(qǐng)求URL相關(guān)的操作,但他們提供不同的功能。
urllib2.:urllib2.urlopen可以接受一個(gè)Request對(duì)象或者url,(在接受Request對(duì)象時(shí)候,并以此可以來設(shè)置一個(gè)URL 的headers),urllib.urlopen只接收一個(gè)url。
urllib 有urlencode,urllib2沒有,因此總是urllib,urllib2常會(huì)一起使用的原因
第 三 方:Requests
Requests是一個(gè)HTTP庫, 它只是用來,進(jìn)行請(qǐng)求,對(duì)于HTTP請(qǐng)求,他是一個(gè)強(qiáng)大的庫,下載,解析全部自己處理,靈活性更高,高并發(fā)與分布式部署也非常靈活,對(duì)于功能可以更好實(shí)現(xiàn).
框 架:Scrapy
Scrapy是封裝起來的框架,他包含了下載器,解析器,日志及異常處理,基于多線程, twisted的方式處理,對(duì)于固定單個(gè)網(wǎng)站的爬取開發(fā),有優(yōu)勢(shì),但是對(duì)于多網(wǎng)站爬取 100個(gè)網(wǎng)站,并發(fā)及分布式處理方面,不夠靈活,不便調(diào)整與括展。
MyISAM 與 InnoDB
MyISAM 與 InnoDB 兩個(gè)引擎有什么區(qū)別?
InnoDB 支持事務(wù),MyISAM 不支持,這一點(diǎn)是非常之重要。事務(wù)是一種高級(jí)的處理方式,如果在一些列增刪改中只要哪個(gè)出錯(cuò)還可以回滾還原,而 MyISAM就不可以了。
MyISAM 適合查詢以及插入為主的應(yīng)用,InnoDB 適合頻繁修改以及涉及到安全性較高的應(yīng)用。
InnoDB 支持外鍵,MyISAM 不支持。
MyISAM 是默認(rèn)引擎,InnoDB 需要指定。
InnoDB 不支持 FULLTEXT 類型的索引。
InnoDB 中不保存表的行數(shù),如 select count( * ) from table 時(shí),InnoDB;需要掃描一遍整個(gè)表來計(jì)算有多少行,但是 MyISAM 只要簡單的讀出保存好的行數(shù)即 可。注意的是,當(dāng) count( * )語句包含 where 條件時(shí) MyISAM 也需要掃描整個(gè)表。
對(duì)于自增長的字段,InnoDB 中必須包含只有該字段的索引,但是在 MyISAM表中可以和其他字段一起建立聯(lián)合索引。
清空整個(gè)表時(shí),InnoDB 是一行一行的刪除,效率非常慢。MyISAM 則會(huì)重建表。
寫爬蟲是用多進(jìn)程好?還是多線程好? 為什么?
IO密集型情況使用多線程;
計(jì)算密集型情況下使用多進(jìn)程;
IO 密集型代碼(文件處理、網(wǎng)絡(luò)爬蟲等),多線程能夠有效提升效率(單線程下有 IO 操作會(huì)進(jìn)行 IO 等待,造成不必要的時(shí)間浪費(fèi),而開啟多線程能在線程 A 等待時(shí),自動(dòng)切換到線程 B,可以不浪費(fèi) CPU 的資源,從而能提升程序執(zhí)行效率)。在實(shí)際的數(shù)據(jù)采集過程中,既考慮網(wǎng)速和響應(yīng)的問題,也需要考慮自身 機(jī)器的硬件情況,來設(shè)置多進(jìn)程或多線程。
數(shù)據(jù)庫的優(yōu)化
為什么要優(yōu)化?
一個(gè)應(yīng)用吞吐量瓶頸往往出現(xiàn)在數(shù)據(jù)庫的處理速度上,隨著應(yīng)用程序的使用和業(yè)務(wù)的拓展,數(shù)據(jù)庫數(shù)據(jù)量逐漸增多,數(shù)據(jù)庫處理壓力逐漸增大,關(guān)系型數(shù)據(jù)庫數(shù)據(jù)存放在磁盤上的,讀寫速度較慢(與內(nèi)存中的數(shù)據(jù)相比)。
優(yōu)化方式:
設(shè)計(jì)表的時(shí)候嚴(yán)格根據(jù)數(shù)據(jù)庫的設(shè)計(jì)范式來設(shè)計(jì)數(shù)據(jù)庫;
select 后盡量不使用*;
盡量不使用嵌套查詢,使用連接查詢或者where查詢;
sql關(guān)鍵詞盡量使用大寫;
盡量使用邏輯外交不使用物理外鍵;
給查詢頻繁的字段添加索引,并且遵循最左原則(盡量將首選關(guān)鍵字段放在最前邊);
垂直分庫分表:把一些不經(jīng)常讀的數(shù)據(jù)或者結(jié)果復(fù)雜的表拆分成多張表,較少磁盤I/O操作;
水平分庫分表:于數(shù)據(jù)量龐大的表,使用水平分庫分表;
使用緩存,把經(jīng)常訪問到的數(shù)據(jù)而且不需要經(jīng)常變化的數(shù)據(jù)放在緩存中,能節(jié)約磁盤IO;
優(yōu)化硬件;
主從分離讀寫;采用主從復(fù)制把數(shù)據(jù)庫的讀操作和寫入操作分離開來。
使用過的解析數(shù)據(jù)包:Xpath、正則
怎么監(jiān)控爬蟲的狀態(tài)
(1)使用 Python 的 STMP 包將爬蟲的狀態(tài)信心發(fā)送到指定的郵箱。
(2)Scrapyd、pyspider
(3)引入日志
panads讀取數(shù)據(jù)
import pandas as pd
# 加載detail
detail=pd.read_excel("./meal_order_detail.xlsx")
# 1、直接查看方式--先后索引
# 先列 后行的方式
# 獲取dishes_id counts amounts dishes_name 多列數(shù)據(jù)
res=detail[["dishes_id","counts","amounts","dishes_name"]]
# 獲取多列的前n行
res1=detail[["dishes_id", "counts", "amounts", "dishes_name"]][:20]
res2=detail[["dishes_id", "counts", "amounts", "dishes_name"]].head(20)
# res=detail[["dishes_id", "counts", "amounts", "dishes_name"]][[0,1,2,3,4]] # # 不可以的
# res=detail["dishes_id"][[0,1,2,3,4]] # 可以的
# print("獲取多列的結(jié)果:\n", res)
# 2、同時(shí)索引
# loc 只能使用名稱 ---注意:可以使用名稱切片---包含收尾
# iloc 只能使用下標(biāo)
res3=detail.loc[:,"dishes_id":"counts"]
# 3、ix ---同時(shí)索引-----混合索引
# 既可以使用名稱,也可以使用下標(biāo)
# res=detail.ix[0:4, "dishes_id":"counts"]
# res=detail.ix[0:4, 0:2] # 前面行使用名稱,列使用下標(biāo)
#
# res=detail.ix[0:4, 0:"amounts"] # 錯(cuò)誤的,不能混搭
# print("res:\n", res)
# 直接索引方式 # 速度最快
# loc iloc #速度適中 # 某些大的平臺(tái)上只封裝loc iloc方式 --推薦
# ix # 速度最慢 --在最新的版本中提示,將要被刪除掉
Nginx的正向代理和反向代理
Web 開發(fā)中,部署方式大致類似。簡單來說,使用 Nginx 主要是為了實(shí)現(xiàn)分流、轉(zhuǎn)發(fā)、負(fù)載均衡, 以及分擔(dān)服務(wù)器的壓力。Nginx 部署簡單,內(nèi)存消耗少,成本低。Nginx 既可以做正向代理,也可以做反向代理。
正向代理:請(qǐng)求經(jīng)過代理服務(wù)器從局域網(wǎng)發(fā)出,然后到達(dá)互聯(lián)網(wǎng)上的服務(wù)器。特點(diǎn):服務(wù)端并不知道真正的客戶端是誰。
反向代理:請(qǐng)求從互聯(lián)網(wǎng)發(fā)出,先進(jìn)入代理服務(wù)器,再轉(zhuǎn)發(fā)給局域網(wǎng)內(nèi)的服務(wù)器。特點(diǎn):客戶端并不知道真正的服務(wù)端是誰。
區(qū)別:正向代理的對(duì)象是客戶端。反向代理的對(duì)象是服務(wù)端。
緩存穿透、緩存擊穿、緩存雪崩
Redis技術(shù)就是NoSQL技術(shù)中的一種,但是引入Redis又有可能出現(xiàn)緩存穿透,緩存擊穿,緩存雪崩等問題。
1.緩存穿透:
(1)產(chǎn)生原因:key對(duì)應(yīng)的數(shù)據(jù)在數(shù)據(jù)源并不存在,每次針對(duì)此key的請(qǐng)求從緩存獲取不到,請(qǐng)求都會(huì)到數(shù)據(jù)源,從而可能壓垮數(shù)據(jù)源。比如用一個(gè)不存在的用戶id獲取用戶信息,不論緩存還是數(shù)據(jù)庫都沒有,若黑客利用此漏洞進(jìn)行攻擊可能壓垮數(shù)據(jù)庫。
(2)解決方案:
①最常見的則是采用布隆過濾器,將所有可能存在的數(shù)據(jù)哈希到一個(gè)足夠大的bitmap中,一個(gè)一定不存在的數(shù)據(jù)會(huì)被這個(gè)bitmap攔截掉,從而避免了對(duì)底層存儲(chǔ)系統(tǒng)的查詢壓力。
②另外也有一個(gè)更為簡單粗暴的方法(我們采用的就是這種),如果一個(gè)查詢返回的數(shù)據(jù)為空(不管是數(shù)據(jù)不存在,還是系統(tǒng)故障),我們?nèi)匀话堰@個(gè)空結(jié)果進(jìn)行緩存,但它的過期時(shí)間會(huì)很短,最長不超過五分鐘。
2.緩存擊穿:
(1)產(chǎn)生原因:key對(duì)應(yīng)的數(shù)據(jù)存在,但在redis中過期,此時(shí)若有大量并發(fā)請(qǐng)求過來,這些請(qǐng)求發(fā)現(xiàn)緩存過期一般都會(huì)從后端DB加載數(shù)據(jù)并回設(shè)到緩存,這個(gè)時(shí)候大并發(fā)的請(qǐng)求可能會(huì)瞬間把后端DB壓垮。
(2)解決方案:業(yè)界比較常用的做法,是使用mutex。簡單地來說,就是在緩存失效的時(shí)候(判斷拿出來的值為空),不是立即去load db,而是先使用緩存工具的某些帶成功操作返回值的操作(比如Redis的SETNX或者M(jìn)emcache的ADD)去set一個(gè)mutex key,當(dāng)操作返回成功時(shí),再進(jìn)行l(wèi)oad db的操作并回設(shè)緩存;否則,就重試整個(gè)get緩存的方法。
3.緩存雪崩:
(1)產(chǎn)生原因:當(dāng)緩存服務(wù)器重啟或者大量緩存集中在某一個(gè)時(shí)間段失效,這樣在失效的時(shí)候,也會(huì)給后端系統(tǒng)(比如DB)帶來很大壓力。
(2)解決方案:
①緩存失效時(shí)的雪崩效應(yīng)對(duì)底層系統(tǒng)的沖擊非常可怕!大多數(shù)系統(tǒng)設(shè)計(jì)者考慮用加鎖或者隊(duì)列的方式保證來保證不會(huì)有大量的線程對(duì)數(shù)據(jù)庫一次性進(jìn)行讀寫,從而避免失效時(shí)大量的并發(fā)請(qǐng)求落到底層存儲(chǔ)系統(tǒng)上。
②簡單方案就時(shí)講緩存失效時(shí)間分散開,比如我們可以在原有的失效時(shí)間基礎(chǔ)上增加一個(gè)隨機(jī)值,比如1-5分鐘隨機(jī),這樣每一個(gè)緩存的過期時(shí)間的重復(fù)率就會(huì)降低,就很難引發(fā)集體失效的事件。
Python垃圾回收機(jī)制
引用計(jì)數(shù)是一種垃圾收集機(jī)制,而且也是一種最直觀,最簡單的垃圾收集技術(shù)。當(dāng) Python 的某個(gè)對(duì)象的引用計(jì)數(shù)降為 0 時(shí),說明沒有任何引用指向該對(duì)象,該對(duì)象就成為要被回收的垃圾了。比如某個(gè)新建對(duì)象,它被分配給某個(gè)引用,對(duì)象的引用計(jì)數(shù)變?yōu)?1。如果引用被刪除,對(duì)象的引用計(jì)數(shù)為 0,那么該對(duì)象就可以被垃圾回收。不過如果出現(xiàn)循環(huán)引用的話,引用計(jì)數(shù)機(jī)制就不再起有效的作用了。
一般是類中的 __del方法進(jìn)行的。
版權(quán)聲明:本文為CSDN博主「不愿透露姓名の網(wǎng)友」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_40558166/article/details/103044923
金三銀四招聘季,你還有哪些面試干貨文章呢?分享或者查看干貨文章可戳:
鈕點(diǎn)擊追蹤可能是網(wǎng)站運(yùn)營中最常見的需求之一。現(xiàn)在,通過GA4 和 Google 跟蹤代碼管理器(GTM)的配合,我們可以輕易實(shí)現(xiàn)追蹤,甚至可以導(dǎo)入到google ads,輔助廣告優(yōu)化。
具體實(shí)現(xiàn)方法有很多,本文主要介紹使用GTM的方法。
首先,我們需要?jiǎng)?chuàng)建一個(gè)觸發(fā)器,或更具體地說,創(chuàng)建一個(gè)通用點(diǎn)擊觸發(fā)器。這將允許 GTM 監(jiān)聽與點(diǎn)擊相關(guān)的事件。
在 Google 跟蹤代碼管理器中,我們可以選擇 2 個(gè)點(diǎn)擊觸發(fā)選項(xiàng):
? All Elements
? Just links
Just links是一種常見的方式,因?yàn)樵S多按鈕本質(zhì)上是用戶點(diǎn)擊的裝飾鏈接。但是,如果您的按鈕恰好不是鏈接,您將無法獲得更多信息來跟蹤。
這就是為什么我們喜歡使用All Elements作為觸發(fā)器類型。此觸發(fā)器類型能夠偵聽任何內(nèi)容(例如圖像、文件、按鈕、空白區(qū)域等)和鏈接上發(fā)生的點(diǎn)擊。它只是更通用、更通用。
創(chuàng)建一個(gè)新觸發(fā)器并選擇All Elements。
現(xiàn)在,GTM 可以識(shí)別何時(shí)發(fā)生點(diǎn)擊,無論是鏈接點(diǎn)擊還是任何元素點(diǎn)擊。
然而,我們可能需要更多的信息,而不僅僅是知道存在點(diǎn)擊交互。
要獲取有關(guān)點(diǎn)擊的信息,請(qǐng)轉(zhuǎn)到 Google 跟蹤代碼管理器并啟用與點(diǎn)擊相關(guān)的所有內(nèi)置變量。這些變量將為我們提供所需的大部分點(diǎn)擊詳細(xì)信息,例如Click Text,它會(huì)顯示按鈕上的文本。
在 GTM 工作區(qū)中,選擇Variables,然后選擇Configure。
啟用所有單擊變量。
該信息將用于創(chuàng)建觸發(fā)我們的標(biāo)簽的條件。
返回我們的產(chǎn)品頁面,讓我們單擊Add to Cart按鈕。(在這里,添加購物車僅僅是示例,你可以把這個(gè)方法應(yīng)用在任何按鈕)
前往 Google 跟蹤代碼管理器的調(diào)試界面。識(shí)別Summary下的Click事件并選擇它。查看之前啟用的變量向我們展示了什么信息。
這里我們需要找到足夠獨(dú)特的數(shù)據(jù)作為觸發(fā)條件。
在“調(diào)試”界面中,選擇左側(cè)面板中的Click事件,然后單擊Variables選項(xiàng)卡。
重點(diǎn)關(guān)注我們已啟用的變量(Click Classes, Click ID, Click Target, Click Text, and Click URL),盡力識(shí)別按鈕特有的數(shù)據(jù)。
通常,許多按鈕都是構(gòu)建為鏈接的,這意味著您的 Click URL 變量值很有可能顯示鏈接。您可以將其用于您的觸發(fā)條件。
但是,很多時(shí)候并非所有維度的數(shù)據(jù)都齊全。在我們的示例中,Click URL沒有值,這時(shí)我們可以使用另外兩條信息:
? Click Classes,值為:single_add_to_cart_button button alt
? Click Text,值為: Add to cart
需要注意的是,Click Classes和Click Text很有可能不是唯一值,該方法只適用于追蹤網(wǎng)站所有同類按鈕。
如果我們僅僅只需要追蹤與 Ninja 產(chǎn)品的添加到購物車按鈕,怎樣才能做到這一點(diǎn)呢?
首先,我們需要確保該按鈕是唯一的,并且在整個(gè)網(wǎng)站中不會(huì)重復(fù)。
為此,我們將使用 Chrome 開發(fā)人員工具。如果您不熟悉,只需按照以下步驟操作即可。
右鍵單擊您的按鈕并選擇檢查。
這將使您能夠訪問瀏覽器的開發(fā)人員工具來查看按鈕的 HTML 和 CSS 源代碼。
單擊鍵盤上的CTRL + F來過濾 HTML 文檔。
復(fù)制并粘貼該類并將其粘貼到過濾器中。我們將復(fù)制粘貼single_add_to_cart_button 按鈕 alt。
過濾器將顯示您的網(wǎng)站上存在多少個(gè)此類。
如果過濾器結(jié)果是1 of 1 ,這意味著該按鈕的類僅有唯一一項(xiàng)。使用與此按鈕相關(guān)的 GTM 點(diǎn)擊類只會(huì)跟蹤此按鈕,而不會(huì)跟蹤另一個(gè)按鈕。
如果結(jié)果超過 1 個(gè),而您只想追蹤特定按鍵,不是同類的所有按鍵,則需要進(jìn)一步過濾。
解決方案是在觸發(fā)器中添加頁面 URL ,以僅跟蹤位于 Flying Ninja 海報(bào)所在位置的“添加到購物車”按鈕。
讓我們回到All Elements觸發(fā)器。
默認(rèn)情況下,我們的觸發(fā)器會(huì)幫助我們檢測(cè)所有用戶點(diǎn)擊。我們需要從All Clicks切換為Some Clicks。
接下來是觸發(fā)條件的設(shè)置,在我們的例子中是:Click Classes → contains → single_add_to_cart_button
重命名您的觸發(fā)器并保存。
首先,我們創(chuàng)建一個(gè)新的 GA4 事件代碼。 Tag Type選擇Google Analytics: GA4 Event,再根據(jù)GA4的衡量ID對(duì)應(yīng)選擇Configuration Tag。如果沒有預(yù)設(shè)的Tag,則選擇None - Manually Set ID,再手動(dòng)填入衡量ID即可。
然后我們需要命名我們的事件。建議button_click。遵從Google 的命名規(guī)則的最佳實(shí)踐是小寫字母加下劃線。
此外,最好不要發(fā)明名稱,而應(yīng)該首先查看 Google 跟蹤的內(nèi)容。增強(qiáng)測(cè)量中會(huì)跟蹤點(diǎn)擊次數(shù),但沒有任何特定于按鈕點(diǎn)擊的內(nèi)容。他們將事件命名為Click,我們也可以使用它。
此時(shí),我們可以像這樣將事件發(fā)送到 GA4。
但是,我們希望向 GA4 發(fā)送有關(guān)此次點(diǎn)擊的更多詳細(xì)信息,因此我們將使用事件參數(shù)。
按鈕的文本和頁面位置對(duì)我們很有價(jià)值,因此我們將添加 2 個(gè)自定義參數(shù)。
我們的設(shè)置如下所示。
? 注意:我們討論的命名事件的方法與命名事件參數(shù)的方法相同。建議使用推薦事件或增強(qiáng)測(cè)量列表中任何適合您需求的事件。如果您選擇為事件參數(shù)使用自定義名稱,并且想要在 GA4 報(bào)告(自定義維度)中使用,則參數(shù)的數(shù)量將被限制為 50 個(gè)。
Google 的推薦事件和增強(qiáng)型測(cè)量沒有與按鈕文本相關(guān)的參數(shù)。我們能找到的最接近的是link_text。link_text參數(shù)用于文件下載 。
你也可以自定義Parameter Name。例如,button_text,甚至click_text。
由于目標(biāo)是顯示按鈕文本的值,因此我們可以從之前啟用的Click Text變量中獲取值。
選擇Click Text。
如果我們還想知道按鈕所在的頁面。只需重復(fù)上述過程并在Value下選擇Page頁面 URL即可。
添加我們之前創(chuàng)建的所有觸發(fā)器。
保存您的標(biāo)簽。
啟用Preview模式并單擊Add to cart按鈕。
您可以看到我們的標(biāo)簽已觸發(fā)。
在 GA4 的 DebugView 中,您將看到即將發(fā)生的事件。
在參數(shù)旁邊,您可以看到我們的link_text和page_location事件參數(shù)及其值。
至此,事件創(chuàng)建已經(jīng)完成。最多不超過48小時(shí),我們就可以在GA4的事件中找到它,也可以用它創(chuàng)建轉(zhuǎn)化和受眾。
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。