簡單爬蟲的通用步驟

本文首發:ZKeeer』s Blog——簡單爬蟲的通用步驟

代碼基於 python3.5

多圖預警,長文預警

知識點很多,適合小白,大神繞路

文章不詳細的地方,以後會補充。

歡迎署名和不署名轉載

目錄:

1.獲取數據

爬蟲,就是要千方百計地裝成瀏覽器從網站騙數據。——我說的

1.1從requests.get()說起

最開始一個簡單的爬蟲就是調用python的requests模塊,使用get函數。(為了不禍害別人網站,我以自己的網站為例)

import requestsnurl_response = requests.get("http://zkeeer.space")nprint(url_response.status_code, url_response.text)n

這裡get函數從給出的URL獲取數據,列印出狀態碼和獲取的內容看看。

200 <!DOCTYPE html>n<html lang="zh-CN">n <head>......n

狀態碼200,說明平穩落地。後面是獲取到的網頁。

這裡要說明一點,url_response.text 和 url_response.content的區別:

.text返回的是Unicode類型,.content返回的是bytes型也就是傳說的二進位的數據。當需要的數據是文本時,最好用.text,當你需要下載圖片時,要用.content

上面是.text返回的值,下面列印出來.content的值讓大家看看。

200 b<!DOCTYPE html>rn<html lang="zh-CN">rn <head>n

看到前面的小b以及後面赤裸裸的rn了么?

我的博客挺簡單沒有那麼大訪問量,也不需要限制訪問量,也不需要嚴查你的IP,UserAgent,Cookie等。當你需要大量,高頻次訪問,而且訪問的還是淘寶這樣的商業網站,這時候你就需要偽裝了,不能只是赤裸裸的用個get加個url,就向網站大喊:「我!瀏覽器!給數據!」 也就我的博客這麼好心給你,淘寶早就會「淘寶不想理你,並向你扔了個大創可貼」。

1.2學會使用火狐瀏覽器開發者工具

如何偽裝一個瀏覽器?

學習當然都是從模仿開始——也是我說的!

這裡使用的是火狐瀏覽器開發者工具,別聽這麼高大上,其實就是打開火狐瀏覽器按F12!

第一步輸入網址進入我的博客,zkeeer.space 然後按F12,找到網路這一欄。它會提示你重新載入,那就按一下F5,刷新一下。

注意以下幾欄。然後找到並點開我們需要的,也就是第一個

右側會出來對應的詳細信息。包括:消息頭,Cookie,參數,響應,耗時,堆棧跟蹤。

首先requests.get(url, params=None, **kwargs),下面的順序按照參數順序,一一來。

1.3requests.get()參數一:url

消息頭這一欄給出的請求方法是GET,即請求時使用requests.get(),如果這裡是POST,對應使用requests.post()。get函數的url,即請求頭的Host,這裡是「zkeeer.space」

1.4requests.get()參數二:params

get(url, params=None, **kwargs)中params,是構成網址中一些參數,網址鏈接「?」後面的那些參數。舉個栗子:我的一篇文章鏈接是zkeeer.space/? 那麼後面p=383就是get 的參數(當然你也可以直接訪問zkeeer.space/?,而不需要參數)。

需要把這裡列舉的參數都寫到params裡面,哪怕是該參數沒有值。

那麼一開頭我們那幾行代碼就應該這麼寫了。

import requestsnntar_url = "http://zkeeer.space" # 目標網頁nparam = {"p": 383} # 請求頭的參數nurl_response = requests.get(url=tar_url, params=param)nprint(url_response.status_code, url_response.text)n

這樣獲取到的頁面就是「zkeeer.space/?p=383」對應的文章了。

1.5requests.get()參數三:headers

可能會有疑問,get(url, params=None, **kwargs)並沒有headers這個參數啊。這個包含在**kwargs裡面,同樣還有另一常用的proxies,待會兒會說到。

headers應該寫什麼呢?下圖所示的消息頭中的請求頭即是這裡的headers參數。

跟參數一樣,需要把請求頭的所有信息寫入headers(如果網站不查cookie的話,cookie沒必要寫)。如果對這些參數不了解,可以點擊後面對應的詳細了解,介紹的很詳細。

上面的幾行代碼又要進化了。

import requestsnntar_url = "http://zkeeer.space" # 目標網頁nparam = {"p": 383} # 請求頭的參數nheader = { # 請求頭部n "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0",n "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",n "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",n "Accept-Encoding": "gzip, deflate",n "Referer": "http://zkeeer.space/",n "Connection": "keep-alive",n "Upgrade-Insecure-Requests": "1"n}nurl_response = requests.get(url=tar_url, params=param, headers=header)nprint(url_response.status_code, url_response.text)n

這樣一來就比較完備了。

但是這樣情況下,高頻次訪問對伺服器造成壓力了,可能會分析哪個UserAgent訪問次數最多,發現是你在狂刷人家網站,這時候就有可能給你禁了這個UserAgent,這時候你需要更多的UserAgent隨機挑選進行訪問。

我在上篇文章《獲取爬蟲所需要的代理IP》中的代碼中就有收集的UserAgent,見Github

可以使用random.choice()隨便挑嘛。

1.6requests.get()參數四:proxies

上面提到了,這個參數是代理,對,是代理。當你使用隨機UserAgent的人家沒法封了。就會查你IP,發現這個IP刷爆了網站,直接就封了。這時候你要使用代理IP。

上面的代碼又進化了呢!

import requestsnntar_url = "http://zkeeer.space" # 目標網頁nparam = {"p": 383} # 請求頭的參數nproxy = {"http": "http://{}:{}".format("221.8.186.249", "80"),n "https": "https://{}:{}".format("221.8.186.249", "80")} #代理IPnheader = { # 請求頭部n "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0",n "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",n "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",n "Accept-Encoding": "gzip, deflate",n "Referer": "http://zkeeer.space/",n "Connection": "keep-alive",n "Upgrade-Insecure-Requests": "1"n}nurl_response = requests.get(url=tar_url, params=param, proxies=proxy, headers=header)nprint(url_response.status_code, url_response.text)n

問題又來了,長時間使用同一個代理IP,也會被封啊,我要獲取大量代理IP,最好還是匿名或者高匿。這些代理IP從哪來的?我該怎麼獲取?上篇文章我寫了如果獲取大量代理IP以及使用:獲取爬蟲所需要的代理IP

如果需要高質量IP可以從代理網站或者淘寶買,大量,不貴。

1.7總結

至此,基本的獲取網頁已經差不多了,大多數網站你都可以暢行。儘管,也不能暴力訪問一個網站,要有公德心嘛,我寫爬蟲還sleep(0.5)呢。

火狐瀏覽器的開發者工具很好用,希望大家能發揮其作用。

愛惜我的博客!多多sleep,狠下心來拿出來練手不容易,別整崩了。

2.提取數據

獲取完網頁接下來應該提取數據了。獲取網頁的數據,我想提取網頁中特定的文字,或者是數據,或者是圖片,這就是網頁主要提取的吧。

2.1提取文字

先說提取文字,強烈推薦正則表達式和BeautifulSoup4,太強大了。簡直就是加特林噠噠噠噠冒藍火的那種;當然我的水平僅限於能用,就不出來獻醜了。大家按照網上的教程來就可以。正則表達式和beautifulsoup這兩種效率比較高,有文章做過對比試驗,我找到了再補充。

2.2提取圖片

以我博客中《獲取爬蟲所需要的代理IP》文章為例,提取其中的圖片。提取網頁中的圖片,我們可以用提取文字的方式,用正則表達式獲取圖片鏈接。這時候,又用的get函數。跟獲取網頁一樣,獲取圖片。記得前面說過的text和content的區別。這裡要使用後者。那麼如何保存一張圖片呢?看下面的代碼示例。

打開火狐瀏覽器開發者工具。這次使用的是查看器,而不是網路。一層層找到上面文章中的圖片所在位置及相關鏈接。

找到了<img>標籤,這時可以右鍵查看網頁源代碼,查找一下看有多少是跟你目標相似的,看看如何區分

我找到了10項,但只有一項是我需要的,於是我把正則式寫成了

<img class="[^"]+" src="([^"]+)"n

這時候可以用 站長工具-正則表達式在線測試 測試下自己寫的正則表達式是否正確

成功了!代碼如下:

import rennimport requestsnntar_url = "http://zkeeer.space" # 目標網頁nparam = {"p": 383} # 請求頭的參數nheader = { # 請求頭部n "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0",n "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",n "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",n "Accept-Encoding": "gzip, deflate",n "Referer": "http://zkeeer.space/",n "Connection": "keep-alive",n "Upgrade-Insecure-Requests": "1"n}nurl_response = requests.get(url=tar_url, params=param, headers=header)nnimg_url = re.findall(r"<img class="[^"]+" src="([^"]+)"", url_response.text)nprint(img_url[0])n

輸出結果:

http://zkeeer.space/wp-content/uploads/2017/08/邏輯圖-1024x576.pngn

這樣有了圖片的鏈接,我們就可以使用鏈接獲取並保存圖片了。

一定要注意使用F12,並仔細查看請求頭和參數!

一定要注意使用F12,並仔細查看請求頭和參數!

一定要注意使用F12,並仔細查看請求頭和參數!

import rennimport requestsnntar_url = "http://zkeeer.space" # 目標網頁nparam = {"p": 383} # 請求頭的參數nheader = { # 請求頭部n "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0",n "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",n "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",n "Accept-Encoding": "gzip, deflate",n "Referer": "http://zkeeer.space/", #防盜鏈n "Connection": "keep-alive",n "Upgrade-Insecure-Requests": "1"n}nurl_response = requests.get(url=tar_url, params=param, headers=header)nnnimg_header = { # 請求頭部n "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0",n "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",n "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",n "Accept-Encoding": "gzip, deflate",n "Referer": "http://zkeeer.space/?p=383",n "Connection": "keep-alive",n "Upgrade-Insecure-Requests": "1"n}n# 獲取圖片鏈接nimg_url = re.findall(r"<img class="[^"]+" src="([^"]+)"", url_response.text)[0]n# 從圖片鏈接中提取圖片名nimg_name = re.findall(r"([^/]+.png)", img_url)[0]n# 請求nurl_response = requests.get(url=img_url, headers=img_header)n# 保存圖片nwith open(img_name, "wb") as fw:n fw.write(url_response.content)n

運行程序查看得到的圖片(我的vps配置特別低,速度特慢,輕噴。給Vultr的VPS打個廣告,有優惠!

好了,到這兒可以獲取基本的網頁信息了。而且高頻次訪問基本上不會被封。

你以為到這就結束了嗎?

2.3提取動態載入的數據

在獲取有些重要數據的時候。這些數據是動態載入的。這也是反爬蟲的一種手段,下面我有提及反爬蟲。

當我只獲取這個網頁的時候,根本不會顯示這些數據。這個時候又要讓你的爬蟲裝一次瀏覽器。當然不能說:我!瀏覽器!給數據! 這個時候你不光要賣萌,還要回答問題。快看快看,我是瀏覽器,我是可愛的瀏覽器,快給我數據啊。

這時候人家就問你,你是從哪兒(Refer)找到我的?你的雞毛信(ID)呢?你從袖筒里排出幾個參數,瀏覽器一看,哎呀,大兄弟,真的是你,快給你數據。這時候不要吭聲,趁他不注意,拿了數據趕緊跑。

獲取方式跟上面獲取網頁一樣。同樣是使用火狐。以淘寶米6商品評論為例,我隨便找的,別說我軟廣:

點擊評論,然後看到網路這一欄,動態載入的內容就出來了,從域名里我找到了rate.taobao.com,對應左邊兩個文件,一個是detailCommon.htm 另一個是freeRateList.htm;我查看了下,分別對應評論中的大家印象和詳細評論。查看哪兒?查看對應的響應內容。

這裡就以「大家印象」為例:按照1.獲取數據的步驟,獲取到了如下結果:

import renimport requestsnntar_url = "https://rate.taobao.com/detailCommon.htm"n# 商品鏈接nrefer = "https://item.taobao.com/item.htm?spm=a230r.1.14.119.76bf523Zih6Ob&id=548765652209&ns=1&abbucket=12"n# 從商品鏈接中提取商品IDnNumId = re.findall(r"id=(d+)&", refer)n# 參數nparam = {"auctionNumId": NumId,n "userNumId": "43440508",n "callback": "json_tbc_rate_summary"}n# 頭部信息nheader = {n Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,n Accept-Encoding: gzip, deflate, compress,n Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,ru;q=0.4,n Cache-Control: no-cache,n Connection: keep-alive,n Upgrade-Insecure-Requests: "1",n Referer: refer,n User-Agent: "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0"n}nntry:n url_content = requests.get(url=tar_url, params=param, headers=header)n print("url_content: ", url_content.text)nexcept BaseException as e:n passn

結果:

json_tbc_rate_summary({"watershed":100,"isShowDefaultSort":true,"askAroundDisabled":false,"sellerRefundCount":6,"skuSelected":true,"data":{"newSearch":false,"count":{"total":4689,"tryReport":0,"goodFull":4622,"additional":243,"correspond":0,"normal":30,"hascontent":0,"good":4622,"pic":588,"bad":37,"totalFull":4689},"spuRatting":[],"correspond":"4.8","impress":[{"title":"手機不錯","count":477,"value":1,"attribute":"620-11","scm":""},{"title":"手機是正品","count":243,"value":1,"attribute":"1020-11","scm":""},{"title":"系統很強大","count":214,"value":1,"attribute":"921-11","scm":""},{"title":"態度不錯","count":144,"value":1,"attribute":"10120-11","scm":""},{"title":"款式好看","count":115,"value":1,"attribute":"121-11","scm":""},{"title":"快遞不錯","count":108,"value":1,"attribute":"420-11","scm":""},{"title":"手感很好","count":90,"value":1,"attribute":"721-11","scm":""},{"title":"性價比很高","count":85,"value":1,"attribute":"20520-11","scm":""},{"title":"性能一般","count":39,"value":-1,"attribute":"921-13","scm":""},{"title":"配件一般","count":35,"value":-1,"attribute":"621-13","scm":""}],"attribute":[{"name":"版本類型","options":[{"name":"中國大陸","value":"中國大陸"}]},{"name":"版本類型","options":[{"name":"中國大陸","value":"中國大陸"}]},{"name":"版本類型","options":[{"name":"中國大陸","value":"中國大陸"}]},{"name":"版本類型","options":[{"name":"中國大陸","value":"中國大陸"}]}]},"skuFull":false,"showPicRadio":true,"isRefundUser":true})n

最外層是個json_tbc_rate_summary()回調函數,它的參數是個詞典,我們詞典拿出來,放到python中看起來就比較方便了。

處理結果:

2.4總結

這樣一來,隨便你怎麼造,基本的數據你都能獲取到了,動態內容你也有了。正則式也有了,隨便搞。看到這裡,基本可以爬取一些網頁了,下面的內容都是在吹牛,不看也罷。

儘管你都能獲取到了,但是效率呢?下面說說怎樣高效抓取。

3.高效抓取數據(多線程/多進程/分散式爬蟲)

從這兒以後的內容我了解的不深,大部分內容我也在學,權當一起聊聊,說錯的地方請及時指出;我儘力說說,重在大家自己的修行。多看書,多學習。

3.1多線程爬蟲

以上篇文章《獲取爬蟲所需要的代理IP》中驗證IP可用性來說,當資料庫和獲取到的數據中有多個IP,假設有1000IP,如果是串列的話,一個個IP去驗證,效率非常低了,更何況遇到不能用的還會超時。這裡使用多線程,明顯提高了效率。

不談多線程的原理/好處,只說說怎麼使用。因為我不會!你來打我呀!

上篇文章中,驗證IP時,將IP全部讀入列表。多個線程從列表中獲取、驗證,然後將可用的IP放入詞典(主要為了去重)中。這裡只是給大家一個思路,還比如,你設置一個或者多個線程專門抓取網頁上的鏈接然後放在列表中,然後多個線程從列表中取/訪問鏈接,這就涉及到生產者消費者問題了。不多說了,大家應該多學學這裡,以後用得著。

線程數量多少依據電腦性能來定。

這裡使用的模塊是threadings,具體怎麼用,網上的例子比我說得好,萬一我跟他想到一塊兒去了,說的一樣好了,那不就來指控我抄襲?參考廖雪峰老師的教程:多線程

代碼:Github 多線程的使用在GetIP.py文件中。

邏輯圖:

這雖然不是串列,效率提高了好多,但這還不夠,我要做你近旁的一株木棉……跑題了,這還不夠,並發效率雖然提高了,但是不能有效的利用CPU的多核,經常是1核有難,7核圍觀。蛋疼的GIL啊。(大家有興趣可以百度「python 多線程能利用多核嗎」)

接下來你需要的是多進程爬蟲。多個CPU一起建設社會主義,大步邁入小康社會!

3.2多進程爬蟲

如果單單是多進程爬蟲,執行效率會提高,再加上協程的話,效率會有明顯提高。邏輯結構同多線程類似,只不過進程之間通信要使用Queue或者Pipes。

多進程的使用參考廖雪峰老師的教程

大體結構如下圖

我把那個獲取代理IP改寫成多進程版本再發出來。

3.3分散式爬蟲

當網路帶寬、埠資源、IP資源、存儲資源滿足不了爬蟲時,應當適用分散式爬蟲,最基本的分散式爬蟲結構如下圖:

每一個從機上都可以再使用多進程。

我還沒寫過分散式。推薦一個Github項目,簡單的分散式爬蟲,爬取知乎用戶:ZhiHu Spider based on Python

3.4總結

提升效率的方法:串列->並發->並行->分散式。

提升效率了也得注意公德,別給人家服務造成太大壓力,適當的sleep。

4.持續抓取數據(增量式爬蟲)

是 指 對 已 下 載 網 頁 采 取 增 量式更新和只爬行新產生的或者已經發生變化網頁的爬蟲,它能夠在一定程度上保證所爬行的頁面是儘可能新的頁面。 和周期性爬行和刷新頁面的網路爬蟲相比,增量式爬蟲只會在需要的時候爬行新產生或發生更新的頁面 ,並不重新下載沒有發生變化的頁面,可有效減少數據下載量,及時更新已爬行的網頁,減小時間和空間上的耗費,但是增加了爬行演算法的複雜度和實現難度。——百度百科

舉例,拿最熱的《戰狼2》來說,我要實時獲取最新的影評,進行一系列操作,但是不抓已經保存的評論,只檢測最新的評論然後獲取/處理。知乎上的大神們提到增量式爬蟲重點在怎麼判斷是否抓取過,從而避免重複抓取;再一個就是數據去重。

推薦Github項目:byrSpider

5.爬蟲和反爬蟲和反反爬蟲

爬蟲(Spider),反爬蟲(Anti-Spider),反反爬蟲(Anti-Anti-Spider),是一場精彩絕倫的戰爭,你方唱罷我登場。

5.1動態載入

關於動態載入,一種是前面在提到的提取動態內容,方法差不多。另外可以使用selenium/phantomjs提取啊。

因為爬蟲不會執行js代碼進行渲染,有的可能會使用js代碼與伺服器通信,回傳一定的數據,若是爬蟲,這段js代碼便不會執行,可以封IP了;或者給你假數據,投毒。

還遇到過,抓取代理IP時,某個網站需要調用API來checkuser,只需要調用,哪怕check失敗了;如果不調用的話,就得不到數據。這就跟上面差不多。

這時候用必要使用selenium/phantomjs了,但是會大大降低效率。

還想到的其他反爬蟲策略也有改動頁面結構。

5.2驗證碼

當網站判斷你是個爬蟲時,經常會跳出幾個驗證碼來打斷你……的腿。處理驗證碼,常用的方法有:

1.頻率不高,數據量不大時,可以手動輸入

2.頻率高,數據量大,考慮接入打碼平台。你們知道嗎,這叫服務,得花錢!

3.當以上行不通時,自己寫程序識別驗證碼啊。我會說我本科畢設做的就是基於深度神經網路的驗證碼識別嗎?會!由於做的不成熟,就不拿出來現眼了。驗證碼識別在知乎和GitHub上都有相關的項目,可以參考做一下。

4.還有哪些非傳統的驗證碼,例如12306的選擇圖片、滑塊驗證、記錄滑鼠軌跡和點擊等等。12306那個可以調用谷歌或者百度的識圖API,滑塊和滑鼠軌跡之類的得藉助phantomjs。等我學會了我再講出來。這裡會耗費精力很大。多多sleep會在很大程度上避免驗證碼。

5.3登錄

還在學習這個,懂得不多,不吹牛皮,推薦Github項目:模擬登錄一些知名的網站,為了方便爬取需要登錄的網站

5.4cookie

大多數不是真心想要反爬蟲的網站,或者保護重要數據,不會查你的cookie。推薦幾篇關於cookie的文章,別的以後想起來再補充。

Find Hao——python爬蟲學習(四)獲取cookie

州的先生——Python爬蟲實戰入門四:使用Cookie模擬登錄——獲取電子書下載鏈接

Jerry——Python爬蟲—破解JS加密的Cookie

5.5總結

其實繞過這些反爬蟲手段才是王道,正面剛得費多少精力。間隔時間大一點兒,並發量小一點兒,不給人家伺服器造成壓力,人家才不會盯上你。出來混都不容易,誰還不是個寶寶。

6.推薦

1.有能力的去閱讀Scrapy源碼

2.推薦書目:《HTTP權威指南》《用python寫網路爬蟲》,前端方面我是小白,就知道本《CSS權威指南》求大家推薦基礎的前端書籍。

3.python相關的: @路人甲 大神的python總結,還有知乎上好多python大牛,多多關注他們

4.我這兒還有一些python的電子書,包括網上的和我自己買的,都貢獻出來

鏈接:pan.baidu.com/s/1skEQeT 密碼:gi91

5.正則表達式相關的,我倒是經常看見這個大哥(國服第一奇葩輔助)回答


推薦閱讀:

跟繁瑣的命令行說拜拜!Gerapy分散式爬蟲管理框架來襲!
156個Python網路爬蟲資源,媽媽再也不用擔心你找不到資源!
Python3爬蟲(3)單網頁簡單爬取文字信息
福布斯系列之數據採集 | Python數據分析項目實戰

TAG:Python | 爬虫计算机网络 | python爬虫 |