Python 爬蟲進階?

現在是剛Python入門,也編寫了一些簡單的爬蟲代碼,如通過正則,多線程的爬蟲,爬取貼吧裡面的圖片,爬取過代理網站的IP,還接觸了scrapy方面的知識。想繼續深入下去,還需要做哪些方面的工作,另外還需要看那些方面的書,以及一些開源項目,求各位知乎大神指點下。。。
謝謝!!!


Python入門網路爬蟲之精華版

Python學習網路爬蟲主要分3個大的版塊:抓取分析存儲

另外,比較常用的爬蟲框架Scrapy,這裡最後也詳細介紹一下。

首先列舉一下本人總結的相關文章,這些覆蓋了入門網路爬蟲需要的基本概念和技巧:寧哥的小站-網路爬蟲

當我們在瀏覽器中輸入一個url後回車,後台會發生什麼?比如說你輸入http://www.lining0806.com/,你就會看到寧哥的小站首頁。

簡單來說這段過程發生了以下四個步驟:

  • 查找域名對應的IP地址。
  • 向IP對應的伺服器發送請求。
  • 伺服器響應請求,發回網頁內容。
  • 瀏覽器解析網頁內容。

網路爬蟲要做的,簡單來說,就是實現瀏覽器的功能。通過指定url,直接返回給用戶所需要的數據,而不需要一步步人工去操縱瀏覽器獲取。

抓取

這一步,你要明確要得到的內容是什麼?是HTML源碼,還是Json格式的字元串等。

1. 最基本的抓取

抓取大多數情況屬於get請求,即直接從對方伺服器上獲取數據。

首先,Python中自帶urllib及urllib2這兩個模塊,基本上能滿足一般的頁面抓取。另外,requests也是非常有用的包,與此類似的,還有httplib2等等。

Requests:
import requests
response = requests.get(url)
content = requests.get(url).content
print "response headers:", response.headers
print "content:", content
Urllib2:
import urllib2
response = urllib2.urlopen(url)
content = urllib2.urlopen(url).read()
print "response headers:", response.headers
print "content:", content
Httplib2:
import httplib2
http = httplib2.Http()
response_headers, content = http.request(url, "GET")
print "response headers:", response_headers
print "content:", content

此外,對於帶有查詢欄位的url,get請求一般會將來請求的數據附在url之後,以?分割url和傳輸數據,多個參數用連接。

data = {"data1":"XXXXX", "data2":"XXXXX"}
Requests:data為dict,json
import requests
response = requests.get(url=url, params=data)
Urllib2:data為string
import urllib, urllib2
data = urllib.urlencode(data)
full_url = url+"?"+data
response = urllib2.urlopen(full_url)

相關參考:網易新聞排行榜抓取回顧

參考項目:網路爬蟲之最基本的爬蟲:爬取網易新聞排行榜

2. 對於登陸情況的處理

2.1 使用表單登陸

這種情況屬於post請求,即先向伺服器發送表單數據,伺服器再將返回的cookie存入本地。

data = {"data1":"XXXXX", "data2":"XXXXX"}
Requests:data為dict,json
import requests
response = requests.post(url=url, data=data)
Urllib2:data為string
import urllib, urllib2
data = urllib.urlencode(data)
req = urllib2.Request(url=url, data=data)
response = urllib2.urlopen(req)

2.2 使用cookie登陸

使用cookie登陸,伺服器會認為你是一個已登陸的用戶,所以就會返回給你一個已登陸的內容。因此,需要驗證碼的情況可以使用帶驗證碼登陸的cookie解決。

import requests
requests_session = requests.session()
response = requests_session.post(url=url_login, data=data)

若存在驗證碼,此時採用response = requests_session.post(url=url_login, data=data)是不行的,做法應該如下:

response_captcha = requests_session.get(url=url_login, cookies=cookies)
response1 = requests.get(url_login) # 未登陸
response2 = requests_session.get(url_login) # 已登陸,因為之前拿到了Response Cookie!
response3 = requests_session.get(url_results) # 已登陸,因為之前拿到了Response Cookie!

相關參考:網路爬蟲-驗證碼登陸

參考項目:網路爬蟲之用戶名密碼及驗證碼登陸:爬取知乎網站

3. 對於反爬蟲機制的處理

3.1 使用代理

適用情況:限制IP地址情況,也可解決由於「頻繁點擊」而需要輸入驗證碼登陸的情況。

這種情況最好的辦法就是維護一個代理IP池,網上有很多免費的代理IP,良莠不齊,可以通過篩選找到能用的。對於「頻繁點擊」的情況,我們還可以通過限制爬蟲訪問網站的頻率來避免被網站禁掉。

proxies = {"http":"http://XX.XX.XX.XX:XXXX"}
Requests:
import requests
response = requests.get(url=url, proxies=proxies)
Urllib2:
import urllib2
proxy_support = urllib2.ProxyHandler(proxies)
opener = urllib2.build_opener(proxy_support, urllib2.HTTPHandler)
urllib2.install_opener(opener) # 安裝opener,此後調用urlopen()時都會使用安裝過的opener對象
response = urllib2.urlopen(url)

3.2 時間設置

適用情況:限制頻率情況。

Requests,Urllib2都可以使用time庫的sleep()函數:

import time
time.sleep(1)

3.3 偽裝成瀏覽器,或者反「反盜鏈」

有些網站會檢查你是不是真的瀏覽器訪問,還是機器自動訪問的。這種情況,加上User-Agent,表明你是瀏覽器訪問即可。有時還會檢查是否帶Referer信息還會檢查你的Referer是否合法,一般再加上Referer。

headers = {"User-Agent":"XXXXX"} # 偽裝成瀏覽器訪問,適用於拒絕爬蟲的網站
headers = {"Referer":"XXXXX"}
headers = {"User-Agent":"XXXXX", "Referer":"XXXXX"}
Requests:
response = requests.get(url=url, headers=headers)
Urllib2:
import urllib, urllib2
req = urllib2.Request(url=url, headers=headers)
response = urllib2.urlopen(req)

4. 對於斷線重連

不多說。

def multi_session(session, *arg):
retryTimes = 20
while retryTimes&>0:
try:
return session.post(*arg)
except:
print ".",
retryTimes -= 1

或者

def multi_open(opener, *arg):
retryTimes = 20
while retryTimes&>0:
try:
return opener.open(*arg)
except:
print ".",
retryTimes -= 1

這樣我們就可以使用multi_session或multi_open對爬蟲抓取的session或opener進行保持。

5. 多進程抓取

這裡針對華爾街見聞進行並行抓取的實驗對比:Python多進程抓取 與 Java單線程和多線程抓取

相關參考:關於Python和Java的多進程多線程計算方法對比

6. 對於Ajax請求的處理

對於「載入更多」情況,使用Ajax來傳輸很多數據。

它的工作原理是:從網頁的url載入網頁的源代碼之後,會在瀏覽器里執行JavaScript程序。這些程序會載入更多的內容,「填充」到網頁里。這就是為什麼如果你直接去爬網頁本身的url,你會找不到頁面的實際內容。

這裡,若使用Google Chrome分析」請求「對應的鏈接(方法:右鍵→審查元素→Network→清空,點擊」載入更多「,出現對應的GET鏈接尋找Type為text/html的,點擊,查看get參數或者複製Request URL),循環過程。

  • 如果「請求」之前有頁面,依據上一步的網址進行分析推導第1頁。以此類推,抓取抓Ajax地址的數據。
  • 對返回的json格式數據(str)進行正則匹配。json格式數據中,需從"uxxxx"形式的unicode_escape編碼轉換成u"uxxxx"的unicode編碼。

7. 自動化測試工具Selenium

Selenium是一款自動化測試工具。它能實現操縱瀏覽器,包括字元填充、滑鼠點擊、獲取元素、頁面切換等一系列操作。總之,凡是瀏覽器能做的事,Selenium都能夠做到。

這裡列出在給定城市列表後,使用selenium來動態抓取去哪兒網的票價信息的代碼。

參考項目:網路爬蟲之Selenium使用代理登陸:爬取去哪兒網站

8. 驗證碼識別

對於網站有驗證碼的情況,我們有三種辦法:

  • 使用代理,更新IP。
  • 使用cookie登陸。
  • 驗證碼識別。

使用代理和使用cookie登陸之前已經講過,下面講一下驗證碼識別。

可以利用開源的Tesseract-OCR系統進行驗證碼圖片的下載及識別,將識別的字元傳到爬蟲系統進行模擬登陸。當然也可以將驗證碼圖片上傳到打碼平台上進行識別。如果不成功,可以再次更新驗證碼識別,直到成功為止。

參考項目:驗證碼識別項目第一版:Captcha1

爬取有兩個需要注意的問題:

  • 如何監控一系列網站的更新情況,也就是說,如何進行增量式爬取?
  • 對於海量數據,如何實現分散式爬取?

分析

抓取之後就是對抓取的內容進行分析,你需要什麼內容,就從中提煉出相關的內容來。

常見的分析工具有正則表達式,BeautifulSoup,lxml等等。

存儲

分析出我們需要的內容之後,接下來就是存儲了。

我們可以選擇存入文本文件,也可以選擇存入MySQL或MongoDB資料庫等。

存儲有兩個需要注意的問題:

  • 如何進行網頁去重?
  • 內容以什麼形式存儲?

Scrapy

Scrapy是一個基於Twisted的開源的Python爬蟲框架,在工業中應用非常廣泛。

相關內容可以參考基於Scrapy網路爬蟲的搭建,同時給出這篇文章介紹的微信搜索爬取的項目代碼,給大家作為學習參考。

參考項目:使用Scrapy或Requests遞歸抓取微信搜索結果


務虛地說說爬蟲進階。

爬蟲寫得多了,就感到有些乏。這個乏,指的並不是乏味,而是更廣一些的,渾身使不上勁的那種乏。從務實的角度看,現有的答案已經回答地非常全面,無可指摘了。
相信大多數人的爬蟲入門都和我類似,先從urllib2 入手,寫一個最簡陋的get,面對一大堆源碼無所適從。
接著開始接觸傳說中給人用的requests,驚呼『這簡直是太棒了』。
在requests 的學習中,我們知道了proxy,知道了user-agent,知道了如何post。
隨後,我們開始放下寫的頭疼的正則表達式(regex),開始了解xpath,BeautifulSoup,又是一陣驚呼。
我們攻克了知乎(曾經),攻克了移動端的微博,卻卡在了網頁版的微博。於是我們知道了selenium, 用上了PhantomJS,好嘛,現在瀏覽器能做的我都能做了。
漸漸地我們不滿足於單線程的慢慢蟲,於是我們開始寫多線程。
漸漸地我們不滿足於把數據放在csv文件中,於是我們開始用上mysql,mongodb,redis。
最終我們發現從頭開始寫一個爬蟲太不划算了,於是我們又撿起了一開始曾接觸但隨即放棄了的scrapy。乖乖,現在我們才發現scrapy的速度那麼快,效率那麼高。我們不禁有些泄氣。

如果你看到這裡,能夠感同身受的話,我們的水平也就差不太多了,如果你覺得上述我所說的只是些小兒科的話,那我的水平有限,接下來的內容你可以隨意瀏覽,權當娛樂。

我想,開頭提到的乏,其實是對自己所做事情的意義不明晰導致的。我們明白,先有數據,後有爬蟲。所有的爬蟲都只能收集數據,分析數據,而不能直接產生數據。是的,即使是那些分析的結果,也必須建立在原始的數據之上。這裡我們不討論涉及黑產的爬蟲(即搜集到的數據本身就可以賣錢)。除去這一類,爬蟲最有價值的一環,正是數據的分析結果。因為爬蟲能拿到的所有數據都是公開的,免費的,所以唯有將這些數據清洗,重塑,並分析之後,你才能得到全新的,屬於你自己的信息。可是,要如何分析收集到的數據呢?你會發現,光是憑藉計算機的知識,是無法做到深入分析這一點的。你能做的,無非是求求和,求求均值方差,畫畫統計圖表,描描趨勢圖。分析結果的呈現方式有很多,有些顯得很low,有些顯得很高大上。但本質上它們沒有太大區別,它們的價值是很有限的。更好一些的分析手段還有機器學習,也許你可以做一個推薦系統,或是做一個聚類分析。可是從根本上來說,想要最大程度地利用通過爬蟲獲得的數據,需要的首先是強大的數學基礎,其次是數據來源相關學科的學科背景,比如搜集的是經濟數據,那麼就要求你有很強的經濟學功底和很好的市場嗅覺,而這已經完全脫離了爬蟲的範圍。
對於大多數像我一樣的票友來講,我們寫爬蟲追求的並不是效率,也不是規模,甚至不是數據本身。我們追求的,是萬千數據中提煉出有價值的那一部分,並把那一部分為自己所用。因此,如果你的志向不是成為一名爬蟲工程師的話,我對於爬蟲進階的建議,從務虛的角度講,應該是修鍊好自己本學科的內功。爬蟲是手段而非目的,或許明白了這一點,你的爬蟲就能更進一步了。


根據題主的描述,我猜測應該是已經根據網上的一些教程、博客寫爬蟲抓取過一些簡單的內容,然後想要繼續深入的時候,發現網上更進一步的學習資源不那麼好找了。會抓取貼吧圖片(猜測是網上教程抓取一個帖子下的圖片而不是全貼吧)、能夠使用多線程、抓取代理 IP、有 scrapy 的經驗,接下來該怎麼做,給你一點建議(這個問題是大半年以前提的,估計你已經不需要我的建議了 ^_^),僅供參考,錯了你也沒法把我怎麼樣~~~

首先,可以多抓取一些代理 IP,並不斷更新,以及有一套完整的校驗代理 IP 可用性和淘汰過期代理的程序,你的代理庫要隨時擁有五位數的可用代理 IP,並保證不可用 IP 會在失效後較短時間被剔除出去。大部分情況下,爬蟲的效率瓶頸並不在你開幾個線程,多大並行。因為大部分商業網站(個人博客、小網站和一些反爬蟲較弱的站除外)都會根據你的訪問頻率限制,所以太快之後分分鐘被封,你需要很多代理 IP, 在被封之後可以迅速切換新的 IP 繼續抓取。

其次,你可以鍛煉自己抓取複雜網頁的能力。你可以嘗試做網站登錄,到前端渲染異常豐富的網站的抓取都是練手的好機會。切記,對於稍微複雜的驗證碼,不要去做所謂破解驗證碼,這需要較強的基礎知識(包括但不限於統計學、圖像識別、機器學習、...),可能還沒有識別出驗證碼,你就先失去了興趣。先手工打碼,做你正在做的事情——爬蟲。先去嘗試登錄豆瓣、人人,然後去嘗試微博,再到 Google 這樣兩步驗證的網站。前端複雜的動態網站,去嘗試微博、QQ空間等等吧。

然後,考慮那種會隨時增加內容的網站,如何增量式抓取數據。比如,58 每天會產生新的招聘信息,如何只是增量式抓取這部分新增數據,而不需要重複抓取已有數據。這需要考慮如何設計存儲。增量式的抓取可以幫你實現在最小化資源的情況下對一個網站進行數據監控。

然後,嘗試抓取大量的數據。大量值得是那種單機基本上搞不定的網站,就算可以搞定,也一定要多搞幾台機器弄成分散式抓取。爬蟲的分散式不同於你想像中的分散式,你僅僅需要控制一個任務生成端、一個任務分發端和一批爬蟲消費任務即可。

最後,嘗試融合上面的內容,就基本可以做到「只要瀏覽器可以打開,我就可以抓取到」的水平了。

---------------------

爬蟲無非三步:

  • 下載源碼
  • 抽取數據
  • 存儲數據

所以,你需要考慮的是:

  • 如何高效的抓取
  • 如何抽取有用的數據
  • 如何設計存儲結構
  • 如何近乎實時的更新
  • 如何判重並減少冗餘數據存儲

---------------------

書籍方面,不負責任地推薦一本:Python網路數據採集 (豆瓣) 。


我是來吐槽最高票的@Leaf Mohanson

雖然學習的確應該追求本質,但是如果一個學習過程太過冗長又沒有實質性進展,很容易讓人失去繼續學習下去的動力。

比如說,驗證碼破解(一般不談黑產鏈的活,下不為例),居然推薦了pandas和numpy。

如果題主沒有相關的基礎知識,那麼題主需要先學習線性代數、統計和概率、圖像識別基礎、機器學習基礎,然後再來看你推薦的這個K近鄰演算法,發現原來還需要一堆訓練集,好不容易折騰完了之後又發現,卧槽,原來這演算法時空複雜度這麼高……

那麼我的推薦是,使用 Google 的 OCR 開源庫 tesseract,對應的Python包是pytesser,如果只是做簡單(沒有數字重疊)的數字識別,那麼僅需調用介面就能完成識別。

然而關鍵在於,這壓根就應該屬於圖像識別而不屬於爬蟲進階嘛!

----------------------------------------------

在我看來,不管用什麼語言寫爬蟲,進階的第一門課一定得是學會自己抓包,分析請求和返回數據。這當中會有一些欄位噁心到你,比如通過base64或者md5加密,在模擬登陸驗證中通常還會遇到RSA演算法。如果你說你懶得學,那麼上大殺器Selenium,但是你要忍受它對系統資源的佔用(往往要啟動瀏覽器和多個標籤頁)和不那麼快速的爬取速度。

針對一些網站的爬取就像是在玩攻防,網站設置了種種反抓取的坑等著你掉進去。這時候你要學會維護好自己的User-Agent,維護好自己的Cookie池,維護好自己的代理IP池,添加恰當的Host和Referer,以讓對方伺服器覺得這一切看起來都跟真的一模一樣,那麼你的爬蟲開發能力,已經入門了。

到此為止,這些知識還和 Python 沒有半毛關係,但你知道了要幹什麼之後,再去搜 Python 相關的工具庫,你就會發現原來 Requests 可以輕鬆構造一個包含自定義 payload 和 headers 的 post 請求;你就會發現原來 Scapy 中可以使用TCP包注入來偽造IP,還能玩SYN FLOOD拒絕服務攻擊(誤)……

所以說,你要做的是爬蟲進階,再用 Python 去尋找一個快捷的實現途徑,然後就會發現,還是 Python 大法好,不愧為黑客第一語言。


進階可以從以下幾個方面著手

1. 花時間系統地理解HTTP協議,爬蟲是模擬瀏覽器獲取數據,本質上是基於HTTP協議發起請求和接收響應的過程,有必要理解HTTP的基本結構、HTTP的各種頭欄位,HTTP代理,以及 HTTPS,了解了這些,能應付很多簡單的反爬蟲策略。

2. 爬大型網站,比如知乎,人家在反爬蟲工作上做了很多事,比如驗證碼限制、比如介面頻繁請求限制,還如賬號異地登陸限制等等,除了出於安全方面的考慮之外,重要的一點就是防爬蟲,據說(只是據說)某網站50%多流量都是來自爬蟲,顯然,這對網站來說是一種極大的資源浪費,所以他們才會花大量的財力去整治爬蟲,道高一尺,魔高一丈,爬蟲與反爬蟲就是一個鬥智斗勇的過程,如果能自動識別驗證碼,如果使用大量代理IP,如果能使用大量Cookie去模擬不同的人不同的地址去請求數據是解決規模爬蟲的關鍵因素之一。驗證碼使用普通的OCR識別顯然不夠了,你需要接觸更新的東西,比如機器學習,通過建模,訓練大量數據才能夠讓識別率更高。當然,做爬蟲的也不是說要不折手段的爬取數據,基本的禮儀還是需要的,盡量做到不影響伺服器的正常使用。

3. 大規模爬蟲,用單線程爬取數據顯示是不行的,用來寫寫小程序小demo差不多,要想規模話,怎麼得上多進程吧,可單機總是有瓶頸,得上分散式爬蟲,一旦遇上分散式,很多問題就來了,比如消息的通信,數據的同步等等,你可能需要接觸消息隊列什麼的。

4. 數據拿回來那只是第一步,作為爬蟲工程師,你的使命已經算基本完成了,但是,如果僅僅只是會爬蟲,天花板很快就夠得著,大數據不是丟那兒就是大數據了,你得把它利用起來,把裡面有價值的信息挖掘出來做分析。於是乎,你可以往數據分析、數據挖掘、數據科學家的道路上走。

6. 做程序員就是一條不歸之路。。。


沒想到一周得了這麼多的贊。那我繼續講講我這一周Python爬蟲進階的路。

首先,在上周五的總結中,表示自己當前在自己寫爬蟲框架,並詢問組長應該以一個怎樣的曲線來提高。

組長讓我現在從線程入手,從當前的單機單線程到單機多線程,再到單機多進程最後到多機多進程。

至於多線程,最基本就是生產者消費者模式。

所以這一周,我在繼續優化自己的框架的同時,引入多線程和隊列,我的思路大概就是主線程就只做調度,涉及到功能用子線程去完成,同時保證一個線程只做一件事,數據用隊列來傳遞。

看著覺得簡單,花了我一周的時間,一共寫了3個版本,到剛剛改完,我還是比較滿意的。

好了,叉個話題,周三的時候,我一個已經運行了半個月的腳本,其中一項spyder一直報錯,我一開始認為這是正常情況,然而接下來每一個都在報錯,好了。

一開始認為對方的運維把我的代理ip給ban了,其實我已經按要求在2次請求的間隔10秒鐘,就困惑人家還ban我ip。然後換了ip還是同樣的問題。那就是腳本的問題。這時候就開始檢查,發現每次post數據過去後拿不到html,這是為什麼呢。那說明是我post的data問題,就開始測試。

因為這個網站在post的欄位大概二十多個,我一開始在調研的時候,發現留下5個就可以拿到html了,然後,挨個挨個加上去,發現也不行,這就有意思了。

因為在post中有個欄位是code,一開始解析請求的時候也會在ajax前有一個載入getcode的頁面,然而問題就在這裡。過程我就不說了,應該是當天該網站修改了js規則,在接收post數據時要驗證code,而code生成後只能用一次。

所以開始來獲取code,該html是一個json,content欄位裡帶著一段混淆加密的js,這個怎麼破解我要感謝一個前段哥們,他研究了一天找出在另一個js里有關的加密方式。因此,在post數據前,要先拿到js,替換一部分內容,然後運行js得到code。

而我呢,在一次刷新後,在控制台里看到code是一個 -99,這我就很奇怪,某種直覺告訴我這是人家的bug,然而我拿著-99放到code里,post也可以拿到數據。後來在對應的js里就看到關於-99的解釋,頁面的某種錯誤就返回-99,而不是一段加密的code。

好了,今天就說了這麼多。

當下,繼續完善框架,其實換個角度就是個通用爬蟲,也買了本 《fluent python》還好我英文過得去。

總之,這周我看到我最明顯的短板就是,基礎! 真的是基礎。

會擼一些腳本,這並不能說明啥,這就是一些套路,我希望弄明白,為啥我這樣寫比較好。

最後,聽組長講,爬蟲工程師入門的門檻很低,但是成長曲線很陡峭,既然要想在爬蟲有所深入,那就要做好準備學習很多思考很多。

=========================分割線=======================

關於爬蟲進階也是我當前遇到的瓶頸。

我想講講自己的爬蟲經歷,去年3月得知自己研究生落榜後,發現自己需要討個生計,自學java一段時間,面試各種碰壁,後來一家在面試前告知主要偏數據方面的公司表示可以要我,我就如抓住救命稻草一般。

因此,第一份公司,在一個數據公司,主要做房地產數據,去了後,就開始接觸他們的爬蟲工具,是用C#寫的,時至今日,爬蟲除了python,java,php主流點外,C# 的爬蟲算是一股清流。當時就是跟著組長發給我他自己錄的使用視頻模仿他設計的腳本,全程不用敲代碼,所有過程都是封裝好了的。我那時候也不懂,人家就是對頁面用xml解析,把每個標籤提取對應的text()提取出來,我們人為的去選擇要提取對應標籤。因為都是爬取一些各地房管局的網站,頁面都比較固定。好了,那時候,竟然不設時延!挺不道德的。不過我也不知道時延這麼一說。

玩會了公司自己的爬蟲工具,同事呢,大學同學告訴搞IT啊不接觸代碼就意味著低工資,我一琢磨也是啊。然後就覺得自己應該去了解了解爬蟲原理。隨後我就知道python這種神奇語言的存在,從此一發不可自拔。

剛開始時,照著《笨辦法學python》來入門,然後開始看《python網路抓取》,並嘗試模仿人家,不過呢,人家都是動不動爬維基百科,公司這邊沒翻牆,實現不了,同時呢,上來就是bs4這個庫,我入門入的很慢。國慶的時候乾脆辭了,決心好好學學爬蟲,此時我接觸到python大概一個多月。

後來花了兩月,一邊玩,一邊看python,從python3變到python2.7,看了很多人的腳本,開始模仿別人,從urllib開始,然後接觸到大殺器requests,驚呼太棒了!頁面內容提取從最開始的bs4到HTMLParse到用lxml,以及regex以至於有段時間沉溺於正則不能自拔,主要是玩的開心。

然後就是scrapy,接觸後,表示完全看不懂,然後依葫蘆畫瓢寫了幾個簡單的覺得並不好用,然後放棄,還是用好requests為主

套路都是一樣的,這時候開始去嘗試爬爬豆瓣top movie250啊,圖書榜啊,我相信很多人入門都是從豆瓣開始的。然後一邊看招聘需求,哦,有要求會nosql,在第一家公司的時候還是學到一些mysql的皮毛,nosql就nosql吧,我學。

畢竟咱們是面向簡歷的編程。

這樣玩了大概兩月,到去年12月,迎來我第二份工作。去了一家創業型公司,[捂臉]加我加老闆加產品大姐加一個在哈佛的博士一共四人。去面的時候,老闆抱著他的mbp過來,一來就問我這個網站能不能抓,我看了看,很挺簡單的,表示能抓,老闆說要是這都不能抓那你白學了。。。好了。。此處省略。。。

好了,第二份工作,一來就開始實際的爬取,我記得第一個網站就是中國教育在線,先是把人家全國高校的數據抓了下來,然後就是各個學校各個專業歷年的分數線 (隨後也抓了大大小小其他的一些有高考信息的網站),當然其過程中也遇到很多問題,無論是我腳本的,還是人家的反爬蟲,都一一解決了,沒辦法只有我一個人做,我的腳本水平還停留在「大一統」的程度,就是一個單例模式。然後到年前,老闆讓我去抓一個競爭對手的數據,並告訴我儘快(過程我就不說了,老闆的預期是半個月搞定,而我兩天就拿到了)

完了後,年後,老闆覺得數據量夠了,我大概40天里抓到清洗後的數據量在4千萬左右。老闆就讓我去做web,別啊,哥們爬蟲玩的正開心呢。後面呢,隨著對產品的理解更加深入,發現,我們在山寨競爭對手的產品,關鍵數據還是人家的,關鍵數據都是我拿的,對於這種侵犯知識產權的事情,我是拒絕的。就打算離職。

然後,我就離職了。第二份到現在這份過渡很平滑,好像是二月二十二號?周一離職,周二投簡歷,周三面試拿offer。可算是到了家大公司,前幾天我也在思索要招爬蟲崗位的公司,首先規模,其次要有持續的數據需求。然後遇到一個大牛組長,也是做python的,恩,感覺會學到很多。

-----------------------------------分割線------------------------

好了,我巴拉巴拉那麼多。

開始講正題了。

我覺得爬蟲進階吧,大家應該有相同的體會,自從我一月份我的爬蟲腳本模式固定後,就沒多少進步,然後我在琢磨該怎麼提高,當然老闆只關心結果。

然後,我就開始琢磨,受scrapy,把一些模塊分離出去,首先是把我那user-agent放到別處去,一個list裡面20多個ua 放在腳本里很佔地方。一開始我僅僅也是做了這麼多。

因為我要抓的是一系列網站,組長也說過要弄分散式啊,通用腳本什麼。所以我也開始大刀闊斧的修改我的腳本,沒開發經驗,特別是對面向對象理解不深,做這個是挺痛苦,不過還好。

因為想到是要抓十幾個網站,我就讓ua被大家用,那我就放 __init__里,逐漸的,我就把各個模塊分散開來,ua啊還有各種固定的參數我都放config里,完了後,因為不同reques所攜帶的內容不同,我需要一個通用的headers,好的,請求頭的構成被分離出去。然後呢,session部分,也分離出去,每一個session都被我重寫一邊,然後頁面解析和spyder也分散出去,最後留下的那就是腳本的調度,好了,然後我就按這個模式敲了一個腳本,現在正跑著,目測要跑一個月。

然後我就覺得,誒,我這不就是在自己寫框架么。

然後看了上面仁兄的發言,scrapy, pyspyder 什麼的太優秀,優秀到會阻礙我們的進步,所以覺得自己寫框架也是好事,不僅對我開發上有幫助,也讓我對爬蟲有比較深刻的理解。

雖然我現在沒有到很深入的層次,比如某些仁兄提到考慮io啊,時間啊,cpu佔用啊等等

保持學習進步嘛,我才半年而已。

一定要設置時延,一定要設置時延,我們在抓取數據的同時,不能影響別人的體驗啊,為我之前敞著跑臉紅一下。

不過我現在一方面繼續打磨自己的框架外,把一些功能封裝起來。也在考慮,因為都是提取一些靜態的數據,對於動態的,就那微博來說,如何動態渲染js啊什麼的,是我需要做的。

好了,巴拉巴拉這麼多,我也感覺並沒說出個啥,嘿嘿


一、gzip/deflate支持

現在的網頁普遍支持gzip壓縮,這往往可以解決大量傳輸時間,以VeryCD的主頁為例,未壓縮版本247K,壓縮了以後45K,為原來的1/5。這就意味著抓取速度會快5倍。

然而python的urllib/urllib2默認都不支持壓縮,要返回壓縮格式,必須在request的header裡面寫明』accept-encoding』,然後讀取response後更要檢查header查看是否有』content-encoding』一項來判斷是否需要解碼,很繁瑣瑣碎。如何讓urllib2自動支持gzip, defalte呢?

其實可以繼承BaseHanlder類,然後build_opener的方式來處理:


import urllib2 from gzip import GzipFile from StringIO import StringIO class ContentEncodingProcessor(urllib2.BaseHandler): """A handler to add gzip capabilities to urllib2 requests """ # add headers to requests def http_request(self, req): req.add_header("Accept-Encoding", "gzip, deflate") return req # decode def http_response(self, req, resp): old_resp = resp # gzip if resp.headers.get("content-encoding") == "gzip": gz = GzipFile( fileobj=StringIO(resp.read()), mode="r" ) resp = urllib2.addinfourl(gz, old_resp.headers, old_resp.url, old_resp.code) resp.msg = old_resp.msg # deflate if resp.headers.get("content-encoding") == "deflate": gz = StringIO( deflate(resp.read()) ) resp = urllib2.addinfourl(gz, old_resp.headers, old_resp.url, old_resp.code) # "class to add info() and resp.msg = old_resp.msg return resp # deflate support import zlib def deflate(data): # zlib only provides the zlib compress format, not the deflate format; try: # so on top of all there"s this workaround: return zlib.decompress(data, -zlib.MAX_WBITS) except zlib.error: return zlib.decompress(data)

然後就簡單了,


encoding_support = ContentEncodingProcessor opener = urllib2.build_opener( encoding_support, urllib2.HTTPHandler ) #直接用opener打開網頁,如果伺服器支持gzip/defalte則自動解壓縮 content = opener.open(url).read()

二、更方便地多線程

總結一文的確提及了一個簡單的多線程模板,但是那個東東真正應用到程序裡面去只會讓程序變得支離破碎,不堪入目。在怎麼更方便地進行多線程方面我也動了一番腦筋。先想想怎麼進行多線程調用最方便呢?

1、用twisted進行非同步I/O抓取

事實上更高效的抓取並非一定要用多線程,也可以使用非同步I/O法:直接用twisted的getPage方法,然後分別加上非同步I/O結束時的callback和errback方法即可。例如可以這麼干:


from twisted.web.client import getPage from twisted.internet import reactor links = [ "http://www.verycd.com/topics/%d/"%i for i in range(5420,5430) ] def parse_page(data,url): print len(data),url def fetch_error(error,url): print error.getErrorMessage(),url # 批量抓取鏈接 for url in links: getPage(url,timeout=5) .addCallback(parse_page,url) #成功則調用parse_page方法 .addErrback(fetch_error,url) #失敗則調用fetch_error方法 reactor.callLater(5, reactor.stop) #5秒鐘後通知reactor結束程序 reactor.run()

twisted人如其名,寫的代碼實在是太扭曲了,非正常人所能接受,雖然這個簡單的例子看上去還好;每次寫twisted的程序整個人都扭曲了,累得不得了,文檔等於沒有,必須得看源碼才知道怎麼整,唉不提了。

如果要支持gzip/deflate,甚至做一些登陸的擴展,就得為twisted寫個新的HTTPClientFactory類諸如此類,我這眉頭真是大皺,遂放棄。有毅力者請自行嘗試。

這篇講怎麼用twisted來進行批量網址處理的文章不錯,由淺入深,深入淺出,可以一看。

2、設計一個簡單的多線程抓取類

還是覺得在urllib之類python「本土」的東東裡面折騰起來更舒服。試想一下,如果有個Fetcher類,你可以這麼調用


f = Fetcher(threads=10) #設定下載線程數為10 for url in urls: f.push(url) #把所有url推入下載隊列 while f.taskleft(): #若還有未完成下載的線程 content = f.pop() #從下載完成隊列中取出結果 do_with(content) # 處理content內容

這麼個多線程調用簡單明了,那麼就這麼設計吧,首先要有兩個隊列,用Queue搞定,多線程的基本架構也和「技巧總結」一文類似,push方法和pop方法都比較好處理,都是直接用Queue的方法,taskleft則是如果有「正在運行的任務」或者」隊列中的任務」則為是,也好辦,於是代碼如下:


import urllib2 from threading import Thread,Lock from Queue import Queue import time class Fetcher: def __init__(self,threads): self.opener = urllib2.build_opener(urllib2.HTTPHandler) self.lock = Lock() #線程鎖 self.q_req = Queue() #任務隊列 self.q_ans = Queue() #完成隊列 self.threads = threads for i in range(threads): t = Thread(target=self.threadget) t.setDaemon(True) t.start() self.running = 0 def __del__(self): #解構時需等待兩個隊列完成 time.sleep(0.5) self.q_req.join() self.q_ans.join() def taskleft(self): return self.q_req.qsize()+self.q_ans.qsize()+self.running def push(self,req): self.q_req.put(req) def pop(self): return self.q_ans.get() def threadget(self): while True: req = self.q_req.get() with self.lock: #要保證該操作的原子性,進入critical area self.running += 1 try: ans = self.opener.open(req).read() except Exception, what: ans = "" print what self.q_ans.put((req,ans)) with self.lock: self.running -= 1 self.q_req.task_done() time.sleep(0.1) # don"t spam if __name__ == "__main__": links = [ "http://www.verycd.com/topics/%d/"%i for i in range(5420,5430) ] f = Fetcher(threads=10) for url in links: f.push(url) while f.taskleft(): url,content = f.pop() print url,len(content)

以上為其他先入之士的內容,歡迎一起學習和交流。


做過一段時間爬蟲 freelancer, 接過5個項目.

0. requests 模塊, beautifulsoup模塊, css選擇器語法, re 正則模塊, http 頭編寫, cookies, json解析等一定要掌握至熟練及以上程度.
1. 爬取重 ajax 頁面, 推薦谷歌優先搜索 phantomjs, 其次selenium.
2. 破解圖片驗證碼, 推薦谷歌開源庫 pytesser (感謝 @simons 的吐槽), 有興趣的同學可以額外學習 caffe, tensorflow, keras 等神經網路框架, 進一步深入請學習&<高等數學-線性代數&>. 神經網路其實不難, 入了 DL 的坑後, 發現半天就能擼一個手寫識別: Mohanson/MNIST
3. 過濾器, 推薦谷歌搜索 布隆過濾器
4. 分散式爬蟲(消息隊列). 推薦谷歌搜索 rabbitmq.
5. 任務調度. 推薦谷歌搜索 schedule.

基本上是這樣一個學習階梯. 另: 反對學習任何爬蟲框架, 尤其 scrapy, pyspider. 原因:這兩個框架太優秀,太全。年邁的程序員可以去看源碼,年輕的程序員還是自己多動手。

-----------------------更新 by Mohanson, 20150919----------------------
1: 關於是否應該學習爬蟲框架: 我個人學過 scrapy, pyspider, 前期用的比較多, 後期基本純手打,用的技術都在上面介紹過了。如果是學web開發,我一定會推薦tornado框架,如果誰跟我說別學框架,我一定打死他。
2. 爬的時候注意素質, 每秒10個請求就差不多了.我見過有人搞500路並發把人家服務搞掛掉的.不要給別人添麻煩, 也不要給自己惹上麻煩.**爬蟲不是性能測試**, **爬蟲不是性能測試**, **爬蟲不是性能測試**.

-----------------------更新 by Mohanson, 20160311----------------------
時隔半年依然陸續有贊收到, 謝謝.目前已經不做爬蟲了, 但最近在做的一些工作可能對大家會有點幫助.如我上面所說,

"每秒10個請求就差不多了"

這個要求看似非常簡單, 但我相信會有少部分程序員在單機情況下無法實現. 下面來介紹下如何在PC上實現這個要求.簡單測試一下(阿里雲伺服器, 1M帶寬)

time curl 百度一下,你就知道

耗時 0.454 秒.
換句話說, 在不考慮數據處理, 數據存儲的情況下, 每秒只夠請求百度兩次.OK, 為什麼?因為等待IO佔用了大部分時間.解決這種占著CPU不拉屎的情況, 可行的方法是線程池與非同步IO或非同步回調.我最近切換到了 python3.5, 非同步網路 io 已經有官方庫幫著做的.請戳下面, 自己學習.
18.5. asyncio a€「 Asynchronous I/O, event loop, coroutines and tasks
線程池更簡單, 直接上官方庫 17.4. concurrent.futures - Launching parallel tasks - Python 3.6.0b4 documentation.


我也想回答。
首先,我是開源爬蟲Mspider的作者,也一直在改進Mspider,只不過後面的版本沒開源而已,我那個爬蟲主要是爬連接,爬文章的話還真不太適合。入門,或者說開拓思路,Mspider還真不錯呢。
manning23/MSpider · GitHub

介紹下我用的庫(需要安裝的):
1,requests
2,re2
3,lxml

由於Mspider是單進程,因此上面三個庫就夠實用了。

下面,說下我覺得比較重要的地方。
1,URL相似度,這個真的很難,很難做到完美,爬多爬少全靠這個。
2,頁面鏈接抓取,單純靠lxml,有些鏈接是無法獲取的。
3,動態爬取,我主推利用子進程玩phantomjs,效果好,但是吃性能。
4,線程分布,這個先前沒人關注過,但是真的很重要。
5,全局變數監控,這個先前也沒人提出過,但也很重要啊。

先寫這麼多~


爬蟲進階會遇到需要登錄的網站,如果需要大量的抓取工作,那就需要研究網站的登錄機制,進行模擬登錄,這樣可以方便的進行多帳號的大規模抓取,@SimonS 說的維護cookie池的問題,@李岩 給出了淘寶的登錄機制,我最近在寫一個關於模擬登錄的小項目 https://github.com/xchaoinfo/fuck-login,目標是把主流的網站都研究一遍。
目前已經解決了
1.知乎
2.126郵箱
3.新浪微博手機版
4.百度


個人覺得有幾個方面可以深入。
一,嘗試自己寫個功能較全的小框架,盡量考慮其通用性。
二,了解分散式爬蟲相關原理,方法,實現等。
三,爬取比較困難的網站,鍛煉自己應對各種情況的經驗,比如淘寶,微博,微信等。
四,深入了解各種框架,工具,包等,比如你提到的那個scrapy,beautifulsoup,lxml等等。
python爬蟲入門極為簡單,甚至網上各種進階教程也都是最基本的知識,cookie、代理、header中的各種參數,反盜鏈等等。但是真到具體業務中,還需要具體業務具體分析。

這裡也引用下我的另外一個答案:

Python爬蟲還算比較簡單的。我學習Python爬蟲的步驟大概是:
1. 學習Python基本語法,並熟練使用
2. 學習Python中關於爬蟲的幾個重要的內置庫:urllib/http/Cookie等
3. 學習正則表達式,beautifulsoup等解析網頁的工具或包
4. 利用上幾步學習的至少爬取比較簡單的網站,比如一些應用市場等等,不需要登陸
5. 學習利用工具分析網頁請求流程、學習模擬登陸,拿新浪微博、知乎等需要登陸的網站進行練習
6. 學習Python中關於多線程、多進程的東西,將以前寫的代碼改為多線程版本,提高效率
7. 學習Python中的爬蟲框架,或者自己寫一個爬蟲框架。
更高級的,還有驗證碼識別、js動態請求、js執行、代理IP等等。
推薦一個我自己的專欄:擼代碼,學知識 - 知乎專欄,裡邊有一些爬蟲的基礎,以及自己嘗試寫的一個小型爬蟲框架。


爬蟲無非分為這幾塊:分析目標、下載頁面、解析頁面、存儲內容,其中下載頁面不提。

1. 分析目標

所謂分析就是首先你要知道你需要抓取的數據來自哪裡?怎麼來?普通的網站一個簡單的POST或者GET請求,不加密不反爬,幾行代碼就能模擬出來,這是最基本的,進階就是學會分析一些複雜的目標,比如說:淘寶、新浪微博登陸以及網易雲的評論信息等等。

2. 解析頁面

解析頁面主要是選擇什麼庫或者那些庫結合能使解析速度更快,可能你一開始你通過種種地方了解到了bs庫,於是你對這個庫很痴迷,以後只要寫爬蟲,總是先寫上:

import requests
from bs4 import BeautifulSoup

當然bs已經很優秀了,但是並不代表可以用正則表達式解析的頁面還需要使用bs,也不代表使用lxml能解決的還要動用bs,所以這些解析庫的速度是你在進階時要考慮的問題。

3. 存儲內容

剛開始學爬蟲,一般爬取的結果只是列印出來,最後把在終端輸出的結果複製粘貼保存就好了;後來發現麻煩會用上xlwt/openpyxl/csv的把存儲內容寫入表格,再後來使用資料庫sqlite/mysql/neo4j只要調用了庫都很簡單,當然這是入門。

進階要開始學習如何選擇合適的資料庫,或者存儲方式。當爬取的內容過千萬的時候,如何設計使存儲速度更快,比如說當既有人物關係又有人物關係的時候,一定會用neo4j來存儲關係,myslq用來存儲用戶信息,這樣分開是因為如果信息全部存入neo4j,後期的存儲速度經十分的慢。

當你每個步驟都能做到很優秀的時候,你應該考慮如何組合這四個步驟,使你的爬蟲達到效率最高,也就是所謂的爬蟲策略問題,爬蟲策略學習不是一朝一夕的事情,建議多看看一些比較優秀的爬蟲的設計方案,比如說Scrapy。

除了爬取策略以外,還有幾點也是必備的:

1. 代理策略以及多用戶策略

代理是爬蟲進階階段必備的技能,與入門階段直接套用代理不同,在進階階段你需要考慮如何設計使用代理策略,什麼時候換代理,代理的作用範圍等等,多用戶的抓取策略考慮的問題基本上與代理策略相同。

2. 增量式抓取以及數據刷新

比如說你抓取的是一個酒店網站關於酒店價格數據信息的,那麼會有這些問題:酒店的房型的價格是每天變動的,酒店網站每天會新增一批酒店,那麼如何進行存儲、如何進行數據刷新都是應該考慮的問題。

3.驗證碼相關的一些問題

有很多人提到驗證碼,我個人認為驗證碼不是爬蟲主要去解決的問題,驗證碼不多的情況考慮下載到本地自己輸入驗證碼,在多的情況下考慮接入打碼平台。

我看到樓上也有很多人提到一些基本的反爬蟲手段,我覺得這些手段是應該在一開始就學到的,而不是在進階的階段。

很多人問我如何學習Python爬蟲,為此我整理編寫了一本Python爬蟲相關的電子書,主要包括Python入門、Python爬蟲入門到進階、Python爬蟲面試總結等等。可以在微信公眾號【一個程序員的日常】後台回復關鍵詞【1】獲取這本電子書。


從入門到精通:如何入門 Python 爬蟲? - 謝科的回答


爬蟲是在沒有(用)API獲取數據的情況下以Hack的方式獲取數據的一種有效手段;進階,就是從爬取簡單頁面逐漸過渡到複雜頁面的過程。針對特定需求,爬取的網站類型不同,可以使用不同的python庫相結合,達到快速抓取數據的目的。但是無論使用什麼庫,第一步分析目標網頁的頁面元素髮現抓取規律總是必不可少的:有些爬蟲是通過訪問固定url前綴拼接不同的後綴進行循環抓取,有些是通過一個起始url作為種子url繼而獲取更多的目標url遞歸抓取;有些網頁是靜態數據可以直接獲取,有些網頁是js渲染數據需要構造二次請求……如果統統都寫下來,一篇文章是不夠的,這裡舉幾個典型的栗子:


1. 頁面url為固定url前綴拼接不同的後綴:

以從OPENISBN網站抓取圖書分類信息為例,我有一批圖書需要入庫,但是圖書信息不全,比如缺少圖書分類,此時需要去"http://openisbn.com/"網站根據ISBN號獲取圖書的分類信息。

如《失控》這本書, ISBN: 7513300712 ,對應url為 "http://openisbn.com/isbn/7513300712/ " ,分析url規律就是以 "http://openisbn.com/isbn/" 作為固定前綴然後拼接ISBN號得到;然後分析頁面元素,Chrome右鍵 —&> 檢查:

我先直接使用urllib2 + re 來獲得「Category:」 信息:

#-*- coding:UTF-8 -*-

import re
import urllib2

isbn = "7513300712"
url = "http://openisbn.com/isbn/{0}/".format(isbn)
category_pattern = re.compile(r"Category: *.*, ")
html = urllib2.urlopen(url).read()
category_info = category_pattern.findall(html)

if len(category_info) &> 0 :
print category_info[0]
else:
print "get category failed."

輸出:

Category: 現當代小說, 小說,


2.選擇合適的定位元素:

由於頁面中只有一行「Category:」 信息,正則表達式提取就行,如果有很多行的話就需要縮小查找範圍了,BeautifulSoup庫就可以用來定位查找範圍。通過分析可知,包含所需「Category:」 最近一層的div 是 &,仔細觀察,外層還有一個 &,而 & 也是一樣,這樣如果使用它們來定位範圍的話,使用find方法返回的tag對象是最先找到的外層div,範圍不夠小;使用findAll,返回的tag對象列表還需要遍歷,綜合得出用& 作為定位元素,find方法定位返回的範圍夠小,又不需要對find結果進行遍歷。

使用urllib2 + Beautiful Soup 3 + re 再來提取一次 (Beautiful Soup最新版本為4.4,兼容python3和python2,BS4跟BS3在導包方式上有點差別):

#-*- coding:UTF-8 -*-

import re
import urllib2
from BeautifulSoup import BeautifulSoup

isbn = "7513300712"
url = "http://openisbn.com/isbn/{0}/".format(isbn)
category_pattern = re.compile(r"Category: *.*, ")
html = urllib2.urlopen(url).read()
soup = BeautifulSoup(html)
div_tag = soup.find("div",{"class":"Article"})
category_info = category_pattern.findall(str(div_tag))

if len(category_info) &> 0 :
print category_info[0]
else:
print "get category failed."

輸出:

Category: 現當代小說, 小說,


3. 抓取js渲染的內容:

用baidu搜索日曆,獲取結果頁中的節假日日期

像上次一樣直接使用urllib打開網頁,發現返回的html中並沒有期望得到的內容,原因是我通過瀏覽器所看到的頁面內容實際是等js渲染完成後最終展現的,中間還包含了多次的ajax請求,這樣使用urllib一次就不能勝任了,此時就可以讓selenium上場了(webdriver用的phantomjs,需要提前下載phantomjs放到當前的PATH路徑下),由於要查找的標識 & 包含了多個,所以這次使用的方法是findAll,然後再對返回的結果進行遍歷,解析每個tag對象的a屬性,如果包含了「休」字標識,那麼這一天就是節假日。

# -*- coding:UTF-8 -*-

import re
import urllib
from selenium import webdriver
from BeautifulSoup import BeautifulSoup

holiday_list = []
url = "http://www.baidu.com/s?" + urllib.urlencode({"wd": "日曆"})
date_pattern = re.compile(r"date="[d]+[-][d]+[-][d]+"")

driver = webdriver.PhantomJS()
driver.get(url)
html = driver.page_source
driver.quit()

soup = BeautifulSoup(html)
td_div_list = soup.findAll("div",{"class":"op-calendar-new-relative"})
for td_tag in td_div_list:
href_tag = str(td_tag.a)
if href_tag.find("休") != -1:
holiday_date_list = date_pattern.findall(href_tag)
if len(holiday_date_list) &> 0:
holiday_list.append(holiday_date_list[0].split(""")[1])

print holiday_list

輸出:

["2016-4-2", "2016-4-3", "2016-4-4", "2016-4-30", "2016-5-1』]


4. 設置代理,抓取google play排行榜(

selenium不僅可以很好的模擬瀏覽器行為,還可以將網頁內容截圖保存)

# -*- coding:UTF-8 -*-

from selenium import webdriver

url = "https://play.google.com/store/apps/top?hl=zh_CN"
proxy_setting = ["--proxy=127.0.0.1:10800", "--proxy-type=socks5"]
driver = webdriver.PhantomJS(service_args=proxy_setting)
driver.get(url)
driver.maximize_window()
# driver.implicitly_wait(10)

top_group_list = driver.find_elements_by_css_selector(".id-cluster-container.cluster-container.cards-transition-enabled")
driver.get_screenshot_as_file("top.jpg』)
for top_group in top_group_list:
group_name = top_group.find_element_by_xpath("div/div[@class="cluster-heading"]/h2/a").text
for item in top_group.find_elements_by_class_name("title"):
print u"bound: {0} app: {1}".format(group_name,item.text)

driver.quit()

5. 爬取全站

爬取一號店手機類目下的手機型號、價格。通過商品分類獲取起始url: "http://list.yhd.com/c23586-0/?tp=15.53938003.561.0.3.LFfY%607f-10-Enf8sti=1FM9" ,從起始頁中獲取每個商品的url,繼而抓取類目下的所有單品。爬取全站,網頁較多,涉及到並發問題,此時可以使用Scrapy框架了。

該類目下總共50頁,拼接上頁碼,url格式如下:

因為頁碼是用 # 拼接,會被Scrapy認為是同一個url,所以只會處理一次。

使用瀏覽器訪問url後觀察,頁面被重定向,真正的url格式卻是這樣的:

我們可以使用重定向後的url作為種子url進行爬取,Scrapy代碼如下:

# -*- coding:UTF-8 -*-

import re
import time

from scrapy import Spider, Request
# from selenium import webdriver
# from selenium.webdriver.common.action_chains import ActionChains

class YhdMobileSpider(Spider):
name = "yhd_mobile"
start_urls = ["http://list.yhd.com/c23586-0-81436/b/a-s1-v4-p1-price-d0-f0d-m1-rt0-pid-mid0-k/"]

def parse(self, response):
"""
@param response:
@return: item list
"""
page_number = self.get_page_count(response)
page_url_list = [ re.sub(r"-p[d]+-", "-p{0}-".format(page), response.url) for page in xrange(1, page_number+1) ]

return map(lambda url: Request(url, callback=self.parse_product_page), page_url_list)

def parse_product_page(self, response):
product_url_list = []
for product_address in response.xpath("//div[@id="itemSearchList"]/div/div[@class="itemBox"]/p[@class="proName clearfix"]/a[1]/@href"):
href = product_address.extract()
product_url_list.append(href)

item_list = map(lambda url: Request(url, callback=self.parse_item), product_url_list)

return item_list

def parse_item(self, response):
"""
根據單品鏈接抓取單品的屬性
@param response:
@return: item
"""
#商品詳情地址
url = response.url
#品牌
brand = response.xpath("//div[@class="crumb clearfix"]/a[@id="brand_relevance"]/text()").extract()[0]
#商品名稱
spu_name = response.xpath("//div[@class="crumb clearfix"]/span/text()").extract()[0]

print url,brand,spu_name

def get_page_count(self,response):
page_count = response.xpath("//input[@id="pageCountPage"]/@value").extract()
if page_count:
page_count = int(page_count[0])
else:
page_count = 1
return page_count

幾個例子都是之前工作的真實需求做的一些簡化,後續再做適當變換擴展就可以進行複雜的爬取了,至於爬取之後的數據如何存儲,做什麼用,那又是另外一個話題了。

(媽的,編輯好多遍知乎就是不能好好的顯示 url 原地址,真是日了狗了,算了 將就著看吧)


——————————————分割線——————————————

剛看到一篇專欄,資源挺全的,新手要學習 Python 的可以關注一下:如何學習Python爬蟲[入門篇]? - 學習編程 - 知乎專欄


深入學習的最好方式就是實際去做。可以試著去爬取一些更有難度的數據,比如百科,微博這類


給各位推薦騰訊雲技術社區-騰雲閣里,關於Python爬蟲的幾篇由淺至深教程。

【騰訊雲的1001種玩法】雲伺服器搭建Python環境 - 騰雲閣

【騰訊雲的1001種玩法】雲伺服器搭建Python爬蟲環境 - 騰雲閣

實操: 遍歷bilibili網站視頻信息 - 騰雲閣

今天我們要抓去的目標網站是,國內最大的年輕人潮流文化娛樂社區:嗶哩嗶哩 - ( ゜- ゜)つロ 乾杯~ - bilibili B站自建站以來已經收納了大約六百多萬的視頻,那麼今天我們就寫一個爬蟲去征服這六百多萬條視頻信息。

我們想抓取的就是上面的播放次數、評論數量、硬幣數量以及收藏數量,接著我們開始。

1、先分析
首先第一步這些數據在哪裡?我們第一個想到的就是在網頁源碼裡面,於是我們查看源碼,搜索相關信息。

遺憾的是我們會發現,信息並不在源碼中;緊接著我們打開chrome開發者工具查看請求信息。

http://api.bilibili.com/archive_stat/stat?callback=jQuery172011470242640208683_1488515896642amp;aid=8904657amp;type=jsonpamp;_=1488515897422

我們可以對以上的url進行修剪,刪除一些不是必須要的參數。我們先觀察這個url,aid是這個視頻的id唯一標識不能刪除,我們可以先把其餘的參數都刪掉試試看,如果不成功我們在一一加參數測試。

http://api.bilibili.com/archive_stat/stat?aid=8904657

顯然,刪除了非必要參數之後對內容毫無影響,所以我們只需要知道每個視頻的aid就可以抓取所有的視頻信息了。那麼B站的視頻aid是怎麼編號的呢?我們可以多觀察以下aid會發現這個aid是一個自動增長的主鍵,從1開始遞增。於是我們代碼思路有就了。

2、寫代碼
使用requests庫來請求獲取數據,並使用Python的內置庫Json來提取數據。

現在已經可以抓取單個視頻信息了,讓你的小爬蟲遍歷整個B站的視頻。

現在你只需要把你的爬蟲一直開在伺服器上就ok了。


最近都在做爬蟲相關,做到最後,做的最多的,就是攻克那些驗證碼,和數據經過怎麼處理得到。

還是那句,多動手,很多問題你不動手,你不知道其中的難度。


不會Python 使用過國內的開源框架webmagic java的 感覺還可以 比較簡單上手


去爬一下微信公眾號內容試試
要這樣的

http://weixin.sogou.com/gzh?openid=oIWsFt4ORWCSUS8szIwVLoRuAq9M


說一下自己最近在研究的搞得幾個進階的東西吧。
1、代理防止禁IP
可以參見: python中使用tor代理
2、js渲染,我這兩天寫了個百度貼吧自動簽到的玩了玩
GitHub - nladuo/tieba_autosign: 百度貼吧自動簽到
3、驗證碼破解,這個主要是C++寫的,用一些機器學習演算法破解了幾種驗證碼(由於樣本數量可能不是很多,所以準確率不算很高)。
GitHub - nladuo/captcha-break: captcha break based on opencv2, tesseract-ocr and some machine learning algorithm.

還有些東西如並發爬蟲、分散式爬蟲可以看看如何使用多線程,如何設計分散式的消息隊列。

目前大概就研究到這種程度,最近在還是在看機器學習的東西,感覺爬蟲研究到最後主要一是如何處理大量的數據,二是進行數據挖掘,分析出有用的信息。


推薦閱讀:

對於一些加密的動態網頁的數據採集,除了通過phantomjs還有沒有別的通用的方法?
如何避免「用隱形鏈接」的反爬蟲技術?
使用Hadoop能做哪些比較有趣的事情?
python關於xpath的一個問題:如何提取某標籤下所有內容?
如何處理python爬蟲ip被封?

TAG:Python | 爬蟲計算機網路 |