大連理工大學(xué)城市學(xué)院 唐 琳 董依萌 何天宇
隨著網(wǎng)絡(luò)的不斷發(fā)展,大量數(shù)據(jù)以文本形式、圖片甚至視頻的形式存儲(chǔ),通過(guò)網(wǎng)絡(luò)爬蟲(chóng)(Crawler)獲取網(wǎng)絡(luò)數(shù)據(jù)是一種非常流行的方法。本文將基于Python的網(wǎng)絡(luò)爬蟲(chóng)技術(shù)的關(guān)鍵性問(wèn)題以及一些相應(yīng)的解決方法進(jìn)行討論。
數(shù)據(jù)爬取任務(wù)通常是基于Robots協(xié)議進(jìn)行,再分析網(wǎng)站DOM樹(shù)爬取所需要的數(shù)據(jù)。在解析過(guò)程中主要使用正則表達(dá)式進(jìn)行篩選和匹配,針對(duì)網(wǎng)站的反爬取機(jī)制采取一些措施和手段。下面分別針對(duì)這些具體知識(shí)點(diǎn)和解決方案進(jìn)行介紹:
Robots協(xié)議的全稱(chēng)是網(wǎng)絡(luò)爬蟲(chóng)排除標(biāo)準(zhǔn)(即Robots Exclusion Protocol),網(wǎng)站通過(guò)Robots協(xié)議告訴搜索引擎哪些頁(yè)面可以抓取,哪些頁(yè)面不能抓取。它是Web站點(diǎn)和搜索引擎爬蟲(chóng)交互的一種方式,并不是一個(gè)規(guī)范,所以并不能保證網(wǎng)站隱私。在互聯(lián)網(wǎng)世界中,每天都有不計(jì)其數(shù)的爬蟲(chóng)在日夜不息地爬取數(shù)據(jù),其中惡意爬蟲(chóng)的數(shù)量甚至高于非惡意爬蟲(chóng)。遵守Robots協(xié)議的爬蟲(chóng)才是好爬蟲(chóng),但是并不是每個(gè)爬蟲(chóng)都會(huì)主動(dòng)遵守Robots協(xié)議。
網(wǎng)站結(jié)構(gòu)分析是進(jìn)行數(shù)據(jù)采集的必備技能,在編寫(xiě)爬蟲(chóng)之前我們需要知道數(shù)據(jù)所在的位置,之后才能通過(guò)遍歷節(jié)點(diǎn)樹(shù)或查找子節(jié)點(diǎn)找到目標(biāo)數(shù)據(jù)。Google瀏覽器的開(kāi)發(fā)者模式就是分析網(wǎng)站結(jié)構(gòu)的強(qiáng)力工具,利用好這個(gè)工具就可以輕松分析出目標(biāo)數(shù)據(jù)的位置,還能獲取渲染內(nèi)容、cookies等信息。
正則表達(dá)式是一種基于規(guī)則進(jìn)行字符串匹配的工具,不同編程語(yǔ)言都有正則表達(dá)式使用的場(chǎng)景。通常我們利用正則表達(dá)式從復(fù)雜的內(nèi)容種,提取出我們想要的部分內(nèi)容。
動(dòng)態(tài)的網(wǎng)站由于不能直接獲得想要數(shù)據(jù),需要得到渲染之后的網(wǎng)頁(yè)等內(nèi)容加載完成之后在進(jìn)行下載。其中,模擬翻頁(yè)可以使用Selenium與PhantomJS模擬瀏覽器方法實(shí)現(xiàn),具體實(shí)現(xiàn)就是通過(guò)模擬鼠標(biāo)點(diǎn)擊頁(yè)碼請(qǐng)求后臺(tái),獲取到新數(shù)據(jù)后重新渲染html的表格部分。
某些網(wǎng)頁(yè)具有反爬取機(jī)制,為了不讓爬蟲(chóng)爬取數(shù)據(jù),有些時(shí)候會(huì)檢查瀏覽器頭Request Header,然后直接斷網(wǎng)從而拒絕爬取。一個(gè)很好的方法就是在Google瀏覽器開(kāi)發(fā)者模式中Network查看生成的請(qǐng)求html文件獲取Request Headers,再通過(guò)Google工具Postman轉(zhuǎn)換為Python代碼。
在爬取網(wǎng)站信息的過(guò)程中,網(wǎng)站可能會(huì)限制每個(gè)IP的訪問(wèn)速度或訪問(wèn)次數(shù)。對(duì)于限制訪問(wèn)速度的情況,通過(guò)time.sleep使程序進(jìn)行短暫休眠后再發(fā)出請(qǐng)求進(jìn)行爬取。對(duì)于限制IP訪問(wèn)次數(shù)的時(shí)候我們需要通過(guò)多個(gè)代理IP構(gòu)建代理IP池輪換訪問(wèn)。
多線程與多進(jìn)程都能成倍的提高爬蟲(chóng)的速度,進(jìn)程的問(wèn)題是不同進(jìn)程之間的內(nèi)存空間不共享,進(jìn)程之間的通信有操作系統(tǒng)傳遞,導(dǎo)致通訊效率低,切換開(kāi)銷(xiāo)大。而所有線程在同一個(gè)進(jìn)程下,共享內(nèi)存空間,通訊效率高,切換開(kāi)銷(xiāo)小。線程為了保護(hù)內(nèi)存空間的數(shù)據(jù)安全,引入了”互斥鎖”。線程的問(wèn)題是在Python里由于GIL全局解釋器鎖的存在,一個(gè)線程需要執(zhí)行任務(wù)必須獲取GIL,在Python的進(jìn)程里只有一個(gè)GIL,但是,在I/O阻塞的時(shí)候,解釋器會(huì)釋放GIL。所以密集CPU任務(wù),需要充分使用多核CPU資源(服務(wù)器,大量的并行計(jì)算)的時(shí)候,用多進(jìn)程。密集I/O任務(wù)(網(wǎng)絡(luò)I/O,磁盤(pán)I/O,數(shù)據(jù)庫(kù)I/O)使用多線程合適。根據(jù)爬蟲(chóng)自身檢索頁(yè)面(I/O)的行為比較多,還是分析頁(yè)面(CPU)的行為更復(fù)雜來(lái)選擇使用多線程與多進(jìn)程就好了。
在爬蟲(chóng)開(kāi)始之前,首先需要我們掌握一些HTML5的基本知識(shí)??梢酝ㄟ^(guò)在網(wǎng)上或者查閱一些書(shū)籍來(lái)學(xué)習(xí)它??梢钥炊?yè)面的源代碼,掌握頁(yè)面源代碼的基本結(jié)構(gòu)和可以找到想要元素所在的標(biāo)簽位置。然后在python 2.x中可以通過(guò)pip install beautifulsoup4來(lái)安裝beautifulsoup4庫(kù)和pip install requests來(lái)安裝requests庫(kù)。python 3.x可以通過(guò)pip3 install beautifulsoup4來(lái)安裝beautifulsoup4庫(kù)和pip3 install requests來(lái)安裝requests庫(kù)。然后學(xué)習(xí)requests庫(kù)和BeautifulSoup4庫(kù)的使用。
Python中正則表達(dá)式需通過(guò)import re來(lái)導(dǎo)入re庫(kù)來(lái)使用。程序種最為常用的功能函數(shù)。搜索字符串,以列表類(lèi)型返回全部能匹配的子串的findall();在一個(gè)字符串中替換所有匹配正則表達(dá)式的子串,返回替換之后的字符串sub();將一個(gè)字符串按照正則表達(dá)式匹配結(jié)果進(jìn)行分割,返回列表類(lèi)型split()等。
爬取動(dòng)態(tài)網(wǎng)站數(shù)據(jù)還需要使用模擬瀏覽器。一般客戶端常見(jiàn)的瀏覽器需要渲染出圖形,因此執(zhí)行效率較慢。如chrome、firefox。另一種瀏覽器沒(méi)有GUI界面,被稱(chēng)為無(wú)頭瀏覽器,這類(lèi)瀏覽器占用的空間少,執(zhí)行效率高。如PhantomJS。適合linux這種純CLI界面服務(wù)器來(lái)執(zhí)行,所以在爬蟲(chóng)程序中,如果是需要使用模擬瀏覽器則需要使用無(wú)GUI的瀏覽器。
網(wǎng)絡(luò)中數(shù)據(jù)較多時(shí)通常分頁(yè)存儲(chǔ),因此,爬蟲(chóng)代碼中敬仰需要實(shí)現(xiàn)翻頁(yè)功能。常見(jiàn)翻頁(yè)的實(shí)現(xiàn)主要有兩種方法。第一種方法是在每次點(diǎn)擊下一頁(yè)按鈕的時(shí)候,可以看到對(duì)應(yīng)頁(yè)面的url是在不停變化的,也就是靜態(tài)的生成了一個(gè)新的頁(yè)面,并且可以看到每次生成的新的頁(yè)面的url是有規(guī)律可循的。那么我們就可以根據(jù)這一情況,在編寫(xiě)代碼的循環(huán)里面實(shí)現(xiàn)對(duì)url的不停更新,也就實(shí)現(xiàn)了翻頁(yè)的功能。第二種方法就是,每次在點(diǎn)擊下一頁(yè)的時(shí)候,當(dāng)前頁(yè)面的url并沒(méi)有變化,變化的是這個(gè)頁(yè)面的內(nèi)容。也就是在點(diǎn)擊下一頁(yè)的時(shí)候這個(gè)網(wǎng)頁(yè)動(dòng)態(tài)的加載了新的內(nèi)容。這就意味在每次翻頁(yè)的時(shí)候都需要人為的去點(diǎn)擊下一頁(yè)按鈕。為了解決這一問(wèn)題就有了模擬瀏覽器,讓這個(gè)模擬瀏覽器去模擬人為點(diǎn)擊下一頁(yè)按鈕的動(dòng)作,實(shí)現(xiàn)翻頁(yè)的功能,然后就可以繼續(xù)去分析獲取頁(yè)面的內(nèi)容,并得到需要的內(nèi)容。
還有有一些網(wǎng)頁(yè)是隱含分頁(yè)形式顯示的,即在顯示的時(shí)候只顯示一部分,只有在鼠標(biāo)向下滾動(dòng)的時(shí)候才會(huì)繼續(xù)顯示,這樣的話就存在爬蟲(chóng)爬取下來(lái)一個(gè)不完整的內(nèi)容,那么我們就可以讓這個(gè)模擬瀏覽器來(lái)模擬這個(gè)動(dòng)作,從而可以獲得完整的頁(yè)面內(nèi)容。
對(duì)于一些網(wǎng)站,如果不是從瀏覽器發(fā)出的請(qǐng)求,則得不到響應(yīng)。所以,我們需要將爬蟲(chóng)程序發(fā)出的請(qǐng)求偽裝成瀏覽器發(fā)出的正常請(qǐng)求。具體實(shí)現(xiàn):自定義網(wǎng)頁(yè)請(qǐng)求報(bào)頭打開(kāi)工具Fiddler,然后再瀏覽器訪問(wèn)一個(gè)url,在Fiddler左側(cè)訪問(wèn)記錄中,找到“200 HTTPS url”這一條,點(diǎn)擊查看其對(duì)應(yīng)的請(qǐng)求和響應(yīng)報(bào)頭具體內(nèi)容。然后在自己的爬蟲(chóng)代碼中加入這些內(nèi)容。
(1)多線程適合IO密集型程序
IO密集型代碼(文件處理、網(wǎng)絡(luò)爬蟲(chóng)等),多線程能夠有效提升效率(單線程下有IO操作會(huì)進(jìn)行IO等待,造成不必要的時(shí)間浪費(fèi),而開(kāi)啟多線程能在線程A等待時(shí),自動(dòng)切換到線程B,可以不浪費(fèi)CPU的資源,從而能提升程序執(zhí)行效率)。所以python的多線程對(duì)IO密集型代碼比較友好。當(dāng)我們使用多線程爬取時(shí),會(huì)出現(xiàn)爬蟲(chóng)每次都會(huì)把url列表里url在沒(méi)有爬取內(nèi)容的時(shí)候就已經(jīng)生成了一個(gè)線程,就會(huì)出現(xiàn)如果斷網(wǎng)的話,就會(huì)有很多的url其實(shí)還沒(méi)有被爬取,對(duì)存儲(chǔ)文件的處理就變得很麻煩。對(duì)于任務(wù)數(shù)量不斷增加的程序來(lái)說(shuō),每有一個(gè)任務(wù)就要為這些新的鏈接生成新的線程,線程數(shù)量暴漲,最終線程數(shù)量就會(huì)失控。在之后的運(yùn)行中,線程數(shù)量還會(huì)不停的增加,完全無(wú)法控制。所以,對(duì)于任務(wù)數(shù)量不端增加的程序,固定線程數(shù)量的線程池是必要的。
(2)多進(jìn)程適合CPU密集運(yùn)算型程序
因?yàn)槊總€(gè)進(jìn)程都有單獨(dú)的GIL,互不干擾,這樣就可以實(shí)現(xiàn)真正意義上的并行執(zhí)行,所以在python中,多進(jìn)程的執(zhí)行效率優(yōu)于多線程(僅僅針對(duì)多核CPU而言)。在多核下,如果想做并行執(zhí)行提升程序效率,比較常用有效的方法是使用多進(jìn)程。
創(chuàng)建一個(gè)進(jìn)程池,進(jìn)程個(gè)數(shù)為cpu內(nèi)核數(shù)。電腦的cpu有多少內(nèi)核便可以同時(shí)執(zhí)行多少個(gè)進(jìn)程,當(dāng)然也可以填的很多,但是作用不大,并不是進(jìn)程數(shù)越多程序執(zhí)行就會(huì)越快。
通過(guò)以上對(duì)基于python的網(wǎng)絡(luò)爬蟲(chóng)的關(guān)鍵性問(wèn)題的結(jié)束和解決,可以看到在網(wǎng)絡(luò)飛速發(fā)展,信息傳播快速的現(xiàn)在,可以知道網(wǎng)絡(luò)爬蟲(chóng)是我們大量獲取網(wǎng)絡(luò)數(shù)據(jù)的主要手段。為了學(xué)好這一技術(shù),首先我們要學(xué)會(huì)HTML5,通過(guò)一些書(shū)籍和反反復(fù)復(fù)查看一個(gè)網(wǎng)頁(yè)的源代碼結(jié)合來(lái)學(xué)習(xí)這門(mén)語(yǔ)言,掌握它的基本語(yǔ)法和結(jié)構(gòu)。學(xué)會(huì)使用F12:快捷鍵,(更多工具——開(kāi)發(fā)者工具)。接下來(lái)就要學(xué)會(huì)bs4和re結(jié)合使用,bs4可以找到需要的標(biāo)簽,re可以從這個(gè)標(biāo)簽中獲得需要的內(nèi)容,從而可以在頁(yè)面中解析和獲得我們想要的內(nèi)容。在學(xué)習(xí)一個(gè)模擬瀏覽器,實(shí)現(xiàn)翻頁(yè)和加載完畢頁(yè)面內(nèi)容,這樣一個(gè)簡(jiǎn)單的爬蟲(chóng)就實(shí)現(xiàn)了。可以繼續(xù)改進(jìn)代碼結(jié)合數(shù)據(jù)庫(kù)來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的網(wǎng)頁(yè)去重,偽裝一下爬蟲(chóng)的首部,以及通過(guò)多線程或者多進(jìn)程來(lái)提高爬蟲(chóng)的效率。最后要注意爬蟲(chóng)的時(shí)效性,作為一個(gè)網(wǎng)絡(luò)爬蟲(chóng)開(kāi)發(fā)者,我們應(yīng)該隨時(shí)關(guān)注我們正在爬取的網(wǎng)站的網(wǎng)頁(yè)的結(jié)構(gòu),一旦發(fā)生了變化,我們就要及時(shí)更新爬蟲(chóng)的解析規(guī)則,以確保爬蟲(chóng)的正常運(yùn)行。