我最想推薦給程序員們看的基于Python3.4實現的爬蟲書
互聯網包含了迄今為止最有用的數據集,并且大部分可以免費公開訪問。但是,這些數據難以復用。它們被嵌入在網站的結構和樣式當中,需要抽取出來才能使用。從網頁中抽取數據的過程又稱為網絡爬蟲,隨著越來越多的信息被發布到網絡上,網絡爬蟲也變得越來越有用。
今天介紹的這一本書《用Python寫網絡爬蟲(第2版)》是Python網絡爬蟲暢銷圖書全新升級版,上一版年度暢銷近4萬冊,而本書針對Python 3.x編寫,提供示例完整源碼和實例網站搭建源碼,確保你可以在本地成功復現爬取網站環境,并保障網站的穩定性與可靠性以及代碼運行結果的可再現性。?書中使用的所有代碼均已使用Python 3.4+測試通過,并且可以在異步社區下載到。
點擊封面查看更多詳情
“網絡爬蟲何時有用”
假設我有一個鞋店,并且想要及時了解競爭對手的價格。我可以每天訪問他們的網站,與我店鋪中鞋子的價格進行對比。但是,如果我店鋪中的鞋類品種繁多,或是希望能夠更加頻繁地查看價格變化的話,就需要花費大量的時間,甚至難以實現。再舉一個例子,我看中了一雙鞋,想等到它促銷時再購買。我可能需要每天訪問這家鞋店的網站來查看這雙鞋是否降價,也許需要等待幾個月的時間,我才能如愿盼到這雙鞋促銷。上述這兩個重復性的手工流程,都可以利用本書介紹的網絡爬蟲技術實現自動化處理。
在理想狀態下,網絡爬蟲并不是必需品,每個網站都應該提供API,以結構化的格式共享它們的數據。然而在現實情況中,雖然一些網站已經提供了這種API,但是它們通常會限制可以抓取的數據,以及訪問這些數據的頻率。另外,網站開發人員可能會變更、移除或限制其后端API。總之,我們不能僅僅依賴于API去訪問我們所需的在線數據,而是應該學習一些網絡爬蟲技術的相關知識。
“本書基于Python 3”
在本書中,我們將完全使用Python 3進行開發。Python軟件基金會已經宣布Python 2將會被逐步淘汰,并且只支持到2020年;出于該原因,我們和許多其他Python愛好者一樣,已經將開發轉移到對Python 3的支持當中,在本書中我們將使用3.6版本。本書代碼將兼容Python 3.4+的版本。
如果你熟悉Python Virtual Environments或Anaconda的使用,那么你可能已經知道如何在一個新環境中創建Python 3了。如果你希望以全局形式安裝Python 3,那么我們推薦你搜索自己使用的操作系統的特定文檔。就我而言,我會直接使用Virtual Environment?Wrapper,這樣就可以很容易地對不同項目和Python版本使用多個不同的環境了。使用Conda環境或虛擬環境是最為推薦的,這樣你就可以輕松變更基于項目需求的依賴,而不會影響到你正在做的其他工作了。對于初學者來說,我推薦使用Conda,因為其需要的安裝工作更少一些。
“編寫第一個網絡爬蟲”
為了抓取網站,我們首先需要下載包含有感興趣數據的網頁,該過程一般稱為爬取(crawling)。爬取一個網站有很多種方法,而選用哪種方法更加合適,則取決于目標網站的結構。本文中,我們首先會探討如何安全地下載網頁,然后會介紹如下3種爬取網站的常見方法:
爬取網站地圖;
使用數據庫ID遍歷每個網頁;
跟蹤網頁鏈接。
到目前為止,我們交替使用了抓取和爬取這兩個術語,接下來讓我們先來定義這兩種方法的相似點和不同點。
1.1抓取與爬取的對比
根據你所關注的信息以及站點內容和結構的不同,你可能需要進行網絡抓取或是網站爬取。那么它們有什么區別呢?
網絡抓取通常針對特定網站,并在這些站點上獲取指定信息。網絡抓取用于訪問這些特定的頁面,如果站點發生變化或者站點中的信息位置發生變化的話,則需要進行修改。例如,你可能想要通過網絡抓取查看你喜歡的當地餐廳的每日特色菜,為了實現該目的,你需要抓取其網站中日常更新該信息的部分。
與之不同的是,網絡爬取通常是以通用的方式構建的,其目標是一系列頂級域名的網站或是整個網絡。爬取可以用來收集更具體的信息,不過更常見的情況是爬取網絡,從許多不同的站點或頁面中獲取小而通用的信息,然后跟蹤鏈接到其他頁面中。
除了爬取和抓取外,我們還會在第8章中介紹網絡爬蟲。爬蟲可以用來爬取指定的一系列網站,或是在多個站點甚至整個互聯網中進行更廣泛的爬取。
一般來說,我們會使用特定的術語反映我們的用例。在你開發網絡爬蟲時,可能會注意到它們在你想要使用的技術、庫和包中的區別。在這些情況下,你對不同術語的理解,可以幫助你基于所使用的術語選擇適當的包或技術(例如,是否只用于抓取?是否也適用于爬蟲?)。
1.2下載網頁
要想抓取網頁,我們首先需要將其下載下來。下面的示例腳本使用Python的urllib模塊下載URL。
當傳入URL參數時,該函數將會下載網頁并返回其HTML。不過,這個代碼片段存在一個問題,即當下載網頁時,我們可能會遇到一些無法控制的錯誤,比如請求的頁面可能不存在。此時,
urllib
會拋出異常,然后退出腳本。安全起見,下面再給出一個更穩建的版本,可以捕獲這些異常。
None
下載時遇到的錯誤經常是臨時性的,比如服務器過載時返回的503 Service Unavailable錯誤。對于此類錯誤,我們可以在短暫等待后嘗試重新下載,因為這個服務器問題現在可能已經解決。不過,我們不需要對所有錯誤都嘗試重新下載。如果服務器返回的是404 Not Found這種錯誤,則說明該網頁目前并不存在,再次嘗試同樣的請求一般也不會出現不同的結果。
互聯網工程任務組(Internet Engineering Task Force)定義了HTTP錯誤的完整列表,從中可以了解到4xx錯誤發生在請求存在問題時,而5xx錯誤則發生在服務端存在問題時。所以,我們只需要確保download函數在發生5xx錯誤時重試下載即可。下面是支持重試下載功能的新版本代碼。
download
5xx
http://httpstat.us/500
500
download
500
默認情況下,urllib使用Python-urllib/``3.x作為用戶代理下載網頁內容,其中3.x是環境當前所用Python的版本號。如果能使用可辨識的用戶代理則更好,這樣可以避免我們的網絡爬蟲碰到一些問題。此外,也許是因為曾經歷過質量不佳的Python網絡爬蟲造成的服務器過載,一些網站還會封禁這個默認的用戶代理。
因此,為了使下載網站更加可靠,我們需要控制用戶代理的設定。下面的代碼對download函數進行了修改,設定了一個默認的用戶代理‘wswp’(即Web Scraping with Python的首字母縮寫)。
meetup.com
1.3網站地圖爬蟲
在第一個簡單的爬蟲中,我們將使用示例網站robots.txt文件中發現的網站地圖來下載所有網頁。為了解析網站地圖,我們將會使用一個簡單的正則表達式,從<loc>標簽中提取出URL。
我們需要更新代碼以處理編碼轉換,因為我們目前的download函數只是簡單地返回了字節。下面是該示例爬蟲的代碼。
正如上面代碼中的方法所示,我們必須更新字符編碼才能利用正則表達式處理網站響應。Python的方法返回字節,而正則表達式期望的則是字符串。我們的代碼依賴于網站維護者在響應頭中包含適當的字符編碼。如果沒有返回字符編碼頭部,我們將會把它設置為默認值UTF-8,并抱有最大的希望。當然,如果返回頭中的編碼不正確,或是編碼沒有設置并且也不是UTF-8的話,則會拋出錯誤。還有一些更復雜的方式用于猜測編碼(參見),該方法非常容易實現。
到目前為止,網站地圖爬蟲已經符合預期。不過正如前文所述,我們無法依靠Sitemap文件提供每個網頁的鏈接。下一節中,我們將會介紹另一個簡單的爬蟲,該爬蟲不再依賴于Sitemap文件。
1.4ID遍歷爬蟲
本節中,我們將利用網站結構的弱點,更加輕松地訪問所有內容。下面是一些示例國家(或地區)的URL。
http://example.python-scraping.com/view/Afghanistan-1
http://example.python-scraping.com/view/Australia-2
http://example.python-scraping.com/view/Brazil-3
可以看出,這些URL只在URL路徑的最后一部分有所區別,包括國家(或地區)名(作為頁面別名)和ID。在URL中包含頁面別名是非常普遍的做法,可以對搜索引擎優化起到幫助作用。一般情況下,Web服務器會忽略這個字符串,只使用ID來匹配數據庫中的相關記錄。下面我們將其移除,查看http://example.python-scraping.com/view/1,測試示例網站中的鏈接是否仍然可用。測試結果如圖1.1所示。
圖1.1
從圖1.1中可以看出,網頁依然可以加載成功,也就是說該方法是有用的。現在,我們就可以忽略頁面別名,只利用數據庫ID來下載所有國家(或地區)的頁面了。下面是使用了該技巧的代碼片段。
在這段代碼中,我們對ID進行遍歷,直到出現下載錯誤時停止,我們假設此時抓取已到達最后一個國家(或地區)的頁面。不過,這種實現方式存在一個缺陷,那就是某些記錄可能已被刪除,數據庫ID之間并不是連續的。此時,只要訪問到某個間隔點,爬蟲就會立即退出。下面是這段代碼的改進版本,在該版本中連續發生多次下載錯誤后才會退出程序。
在爬取網站時,遍歷ID是一個很便捷的方法,但是和網站地圖爬蟲一樣,這種方法也無法保證始終可用。比如,一些網站會檢查頁面別名是否在URL中,如果不是,則會返回404 Not Found錯誤。而另一些網站則會使用非連續大數作為ID,或是不使用數值作為ID,此時遍歷就難以發揮其作用了。例如,Amazon使用ISBN作為可用圖書的ID,這種編碼包含至少10位數字。使用ID對ISBN進行遍歷需要測試數十億次可能的組合,因此這種方法肯定不是抓取該站內容最高效的方法。
正如你一直關注的那樣,你可能已經注意到一些TOO MANY REQUESTS下載錯誤信息。現在無須擔心它,我們將會在1.5節的“高級功能”部分中介紹更多處理該類型錯誤的方法。
1.5鏈接爬蟲
到目前為止,我們已經利用示例網站的結構特點實現了兩個簡單爬蟲,用于下載所有已發布的國家(或地區)頁面。只要這兩種技術可用,就應當使用它們進行爬取,因為這兩種方法將需要下載的網頁數量降至最低。不過,對于另一些網站,我們需要讓爬蟲表現得更像普通用戶,跟蹤鏈接,訪問感興趣的內容。
通過跟蹤每個鏈接的方式,我們可以很容易地下載整個網站的頁面。但是,這種方法可能會下載很多并不需要的網頁。例如,我們想要從一個在線論壇中抓取用戶賬號詳情頁,那么此時我們只需要下載賬號頁,而不需要下載討論貼的頁面。本文使用的鏈接爬蟲將使用正則表達式來確定應當下載哪些頁面。下面是這段代碼的初始版本。
link_crawler
我們查看站點可以得知索引頁鏈接遵循如下格式:
http://example.python-scraping.com/index/1
http://example.python-scraping.com/index/2
國家(或地區)頁遵循如下格式:
http://example.python-scraping.com/view/Afghanistan-1
http://example.python-scraping.com/view/Aland-Islands-2
因此,我們可以用/(index|view)/這個簡單的正則表達式來匹配這兩類網頁。當爬蟲使用這些輸入參數運行時會發生什么呢?你會得到如下所示的下載錯誤。
可以看出,問題出在下載
/index/1
時,該鏈接只有網頁的路徑部分,而沒有協議和服務器部分,也就是說這是一個相對鏈接。由于瀏覽器知道你正在瀏覽哪個網頁,并且能夠采取必要的步驟處理這些鏈接,因此在瀏覽器瀏覽時,相對鏈接是能夠正常工作的。但是,
urllib
并沒有上下文。為了讓
urllib
能夠定位網頁,我們需要將鏈接轉換為絕對鏈接的形式,以便包含定位網頁的所有細節。如你所愿,Python的
urllib
中有一個模塊可以用來實現該功能,該模塊名為
parse
。下面是
link_crawler
的改進版本,使用了
urljoin
方法來創建絕對路徑。
當你運行這段代碼時,會看到雖然下載了匹配的網頁,但是同樣的地點總是會被不斷下載到。產生該行為的原因是這些地點相互之間存在鏈接。比如,澳大利亞鏈接到了南極洲,而南極洲又鏈接回了澳大利亞,此時爬蟲就會繼續將這些URL放入隊列,永遠不會到達隊列尾部。要想避免重復爬取相同的鏈接,我們需要記錄哪些鏈接已經被爬取過。下面是修改后的link_crawler函數,具備了存儲已發現URL的功能,可以避免重復下載。
當運行該腳本時,它會爬取所有地點,并且能夠如期停止。最終,我們得到了一個可用的鏈接爬蟲!
本文摘自《用Python寫網絡爬蟲(第2版)》
《用Python寫網絡爬蟲》
史上首本Python網絡爬蟲圖書全新升級版
針對Python 3.x編寫
提供示例完整源碼和實例網站搭建源碼
本書目錄
(滑動手機查看)
第1章 網絡爬蟲簡介 1
1.1 網絡爬蟲何時有用 1
1.2 網絡爬蟲是否合法 2
1.3 Python 3 3
1.4 背景調研 4
1.4.1 檢查robots.txt 4
1.4.2 檢查網站地圖 5
1.4.3 估算網站大小 6
1.4.4 識別網站所用技術 7
1.4.5 尋找網站所有者 9
1.5 編寫第一個網絡爬蟲 11
1.5.1 抓取與爬取的對比 11
1.5.2 下載網頁 12
1.5.3 網站地圖爬蟲 15
1.5.4 ID遍歷爬蟲 17
1.5.5 鏈接爬蟲 19
1.5.6 使用requests庫 28
1.6 本章小結 30
第2章 數據抓取 31
2.1 分析網頁 32
2.2 3種網頁抓取方法 34
2.2.1 正則表達式 35
2.2.2 Beautiful Soup 37
2.2.3 Lxml 39
2.3 CSS選擇器和瀏覽器控制臺 41
2.4 XPath選擇器 43
2.5 LXML和家族樹 46
2.6 性能對比 47
2.7 抓取結果 49
2.7.1 抓取總結 50
2.7.2 為鏈接爬蟲添加抓取回調 51
2.8 本章小結 55
第3章 下載緩存 56
3.1 何時使用緩存 57
3.2 為鏈接爬蟲添加緩存支持 57
3.3 磁盤緩存 60
3.3.1 實現磁盤緩存 62
3.3.2 緩存測試 64
3.3.3 節省磁盤空間 65
3.3.4 清理過期數據 66
3.3.5 磁盤緩存缺點 68
3.4 鍵值對存儲緩存 69
3.4.1 鍵值對存儲是什么 69
3.4.2 安裝Redis 70
3.4.3 Redis概述 71
3.4.4 Redis緩存實現 72
3.4.5 壓縮 74
3.4.6 測試緩存 75
3.4.7 探索requests-cache 76
3.5 本章小結 78
第4章 并發下載 79
4.1 100萬個網頁 79
4.2 串行爬蟲 82
4.3 多線程爬蟲 83
4.4 線程和進程如何工作 83
4.4.1 實現多線程爬蟲 84
4.4.2 多進程爬蟲 87
4.5 性能 91
4.6 本章小結 94
第5章 動態內容 95
5.1 動態 網頁示例 95
5.2 對動態 網頁進行逆向工程 98
5.3 渲染動態 網頁 104
5.3.1 PyQt還是PySide 105
5.3.2 執行JavaScript 106
5.3.3 使用WebKit與網站交互 108
5.4 渲染類 111
5.5 本章小結 117
第6章 表單交互 119
6.1 登錄表單 120
6.2 支持內容更新的登錄腳本擴展 128
6.3 使用Selenium實現自動化表單處理 132
6.4 本章小結 135
第7章 驗證碼處理 136
7.1 注冊賬號 137
7.2 光學字符識別 140
7.3 處理復雜驗證碼 144
7.4 使用驗證碼處理服務 144
7.4.1 9kw入門 145
7.4.2 報告錯誤 150
7.4.3 與注冊功能集成 151
7.5 驗證碼與機器學習 153
7.6 本章小結 153
第8章 Scrapy 154
8.1 安裝Scrapy 154
8.2 啟動項目 155
8.2.1 定義模型 156
8.2.2 創建爬蟲 157
8.3 不同的爬蟲類型 162
8.4 使用shell命令抓取 163
8.4.1 檢查結果 165
8.4.2 中斷與恢復爬蟲 167
8.5 使用Portia編寫可視化爬蟲 170
8.5.1 安裝 170
8.5.2 標注 172
8.5.3 運行爬蟲 176
8.5.4 檢查結果 176
8.6 使用Scrapely實現自動化抓取 177
8.7 本章小結 178
第9章 綜合應用 179
9.1 Google搜索引擎 179
9.2 Facebook 184
9.2.1 網站 184
9.2.2 Facebook API 186
9.3 Gap 188
9.4 寶 馬 192
9.5 本章小結 196
京東預售
當當預售
本文轉載自異步社區
網站 Python 開發者
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。