用 Python 抓網頁?你想問的都幫答好了

近年來,隨著大資料、人工智慧、機器學習等技術的興起,Python 語言也越來越為人們所喜愛。但早在這些技術普及之前,Python 就一直擔負著一個重要的工作:自動化抓取網頁內容。

舉個栗子,飛機票的價格每時每刻都在變化,甚至有些 app,你搜索的越多,價格就越貴。那不搜又不行啊,怎麼樣才能知道確切的價格呢?

這就是 Python 大顯身手的時候啦~ 我們可以用Python寫一段程式,讓它自動幫你從網路上獲取需要的資料——這就是所謂的“爬蟲程式”——它能從你指定的一個或多個網站上讀取並記錄資料(比如從某個航班資料網站上讀取指定日期和航線的機票資訊),並根據資料進行一些自動操作,比如記錄下最低價,並通知使用者。

總結一下:

網頁抓取是一種透過自動化程式從網頁上獲取頁面內容的計算機軟體技術。

我們這裡說的“爬蟲”,正式名稱叫做“網頁抓取”。按照維基百科的說法,網頁抓取和大多數搜尋引擎採用的網頁索引的爬蟲技術不同,網頁抓取更側重於將網路上的非結構化資料(常見的是HTML格式)轉換成為能在一箇中央資料庫中儲存和分析的結構化資料。“網頁抓取也涉及到網路自動化,它利用計算機軟體模擬了人的瀏覽。網頁抓取的用途包括線上比價,聯絡人資料抓取,氣象資料監測,網頁變化檢測,以及各類科研和Web資料整合等。”

用 Python 抓網頁?你想問的都幫答好了

對於一般使用者,我們主要關注的就是網頁抓取。因此,以下提到的“爬蟲”一律指網頁抓取所用的自動化程式。

在今天的文章裡,我們將帶你從最基礎的工具和庫入手,詳細瞭解一下一個爬蟲程式的常用結構,爬取網路資料時應該遵循哪些規則,存在哪些陷阱;最後,我們還將解答一些常見的問題,比如反追蹤,該做什麼不該做什麼,以及如何採用並行處理技術加速你的爬蟲等等。

文中介紹的每項內容都會附上 Python 的例項程式碼,方便你可以直接上手試玩。同時,我們還會介紹幾個非常有用的 Python 庫。

本教程主要分為5個部分:

1。 常用的程式碼庫和工具

2。 從最簡單的例子開始

3。 小心陷阱

4。 一些規則

5。 利用並行加速爬蟲程式

在開始之前,請記住:務必善待伺服器,我們並不希望把人家網站弄掛了,是吧。

1。 常用的程式碼庫和工具

總的來說,網頁抓取並沒有一個一成不變的解決方案,畢竟通常每個網站的資料都因為網站自身結構的不同而具有各不相同的特性。事實上,如果你希望從某個網站上抓取資料,你需要對這個網站的結構有足夠的理解,針對這個網站自己寫出對應的指令碼,或將某個指令碼設定到符合網站的結構,才可能成功。不過,你也無須重新發明輪子:已經有很多不同的程式碼庫,能幫你完成絕大多數底層的工作,它們多多少少都能幫上你一點忙。

1。1“檢查”選項

大部分時候,在實際爬取之前,你都需要熟悉網站的 HTML 程式碼。你可以簡單地在你想檢視的網頁元素上點選右鍵,選擇“檢查”(Chrome)或者“檢視元素”(火狐)

用 Python 抓網頁?你想問的都幫答好了

之後,系統就會彈出一個除錯工具區,高亮你剛選中的網頁元素。以 Medium 網站的作者資訊頁為例:

用 Python 抓網頁?你想問的都幫答好了

在頁面上,這個被選中的元素包含了作者的姓名、標籤及個人介紹。這個元素的 class 是 hero hero——profile u-flexTOP。然後在這個元素裡還有幾個子元素,其中顯示作者姓名的是

標籤,它的 class 是 ui-h2 hero-title,顯示作者個人資訊的

,它的 class 是 ui-body hero-description。

你可以在 Mozilla 的開發者學院裡找到更多關於 HTML 標記,以及 class 和 id 的區別等的詳細介紹。

1。2 Scrapy 庫

有個可獨立執行,開箱即用的資料抓取框架,名叫 Scrapy。除了抓取並輸出 HTML 外,這個庫還提供了許多額外的功能,比如按特定的格式輸出資料,記錄日誌等。同時,它的可定製性也很高,你可以在多個不同的程序上執行不同的爬蟲,禁用 cookie ¹,設定下載延時²等。

¹ 有些站點會用 cookie 來識別爬蟲。

² 數量過多的爬取請求會給網站帶來額外的負擔,甚至可能會導致網站宕機。

但對我個人而言,這個庫有點太大太全面了:我只不過是想讀取站點每個頁面上的連結,按順序訪問每個連結並匯出頁面上的資料而已。

1。3 BeautifulSoup 和 Requests 庫

BeautifulSoup 庫能讓你優雅地處理 HTML 原始碼。同時你還需要一個 Request 庫,用於從指定URL獲取內容。不過,你需要自己處理其他的細節問題,包括錯誤捕獲與處理,匯出資料,並行處理等。

我個人特別喜歡 BeautifulSoup 因為它迫使我自己探索許多 Scrapy 可能已經幫我處理好了的技術細節,讓我從自己動手開始,從錯誤中學習。

2。 從最簡單的例子開始

從網站上抓取資料其實還是蠻直截了當的。大部分時候我們要關注的就是 HTML 原始碼,找到你需要的內容所對應的 class 和 id。

下面是一個示例的網頁 HTML 程式碼,假設我們要抓取到原價和折後價,那我們需要關注的就是 main_price 和 discounted_price 兩個元素。請注意,discounted_price 元素並不總是出現。

用 Python 抓網頁?你想問的都幫答好了

於是,我們從最基本的程式碼開始:先匯入需要用的 BeautifulSoup 和 Requests 庫,然後發起查詢請求( requests。get() ),接著處理 html 原始碼,最後找到所有 class 為 main_price 的元素。

用 Python 抓網頁?你想問的都幫答好了

文字版原始碼詳見:gist。github。com/jkokatjuhha/02af3a28cf512ee8a3096273850fe029

有的時候,網頁的其他地方可能也有 main_price 的元素。為了避免匯出無關的資訊,我們可以先找到我們需要的 id=‘listings_prices’,然後只在這個元素的子元素中查詢 main_price 元素。

3。 Pitfalls 小心陷阱

3。1 檢查 robots。txt

許多網站會將爬取規則和限制寫在 robots。txt 裡,這個檔案通常是在根域名下,你可以直接在域名後面加上 /robots。txt 來獲取這個檔案。例如: www。example。com/robots。txt

robots。txt 裡一般會規定哪些網頁不允許被自動抓取,或者限定某個頁面被機器人訪問的頻率。雖然大部分人確實都不理會這些,不過就算你真的不打算遵守這個規定,起碼也先看一看它的內容,給點表面的尊重吧,哈哈。

Google官方的幫助文件中,對此的解釋是:“robots。txt 檔案中的命令並不能強制抓取工具對您的網站採取具體的操作;對於訪問您網站的抓取工具來說,這些命令僅作為指令。Googlebot 和其他正規的網頁抓取工具都會遵循 robots。txt 檔案中的命令,但其他抓取工具未必也會如此。”

3。2 小心 HTML 裡的坑

HTML 標籤中可能包含 id 或 class,或二者兼有。 HTML id 是一個獨一無二的標記,而 HTML class 可能在多個元素中被重用。class 名或元素內容可能會改變,而這種改變可能會讓你的程式碼崩潰,或是返回錯誤的結果。

一般來說,有兩種辦法避免這種情況出現:

● 採用 id 來獲取元素內容,而不是 class,因為 id 一般來說不那麼容易改變。

● 記得檢查返回值,如果返回了 None,那很可能有什麼地方出了問題。

用 Python 抓網頁?你想問的都幫答好了

不過,因為有一些 class 可能並不總是出現(例如前面例子中的 discounted_price ),相關的元素並不一定在每個列表中都有。所以你需要統計某個元素在所有列表中出現的比例,比如計算返回 None 的次數。如果每次都返回 None,那也許你需要檢查程式碼或者是 HTML 原始碼,看看是不是這個元素在網站的 HTML 中就已經改變了。

3。3 對 User agent 進行偽裝

每當你訪問一個網站時,網站都會透過瀏覽器的 user agent 獲取到你的瀏覽器資訊。有些網站如果沒收到 user agent 資訊,就不會返回任何內容,還有些網站會根據不同的 user agent,給不同的瀏覽器提供不同的內容。

網站並不會阻止正常使用者的訪問,但如果你用同一個 user agent 發起每秒 200 次的請求,那看起來也太可疑了一點。怎麼解決呢?你可以透過 user_agent 庫,產生(幾乎是)隨機的 user agent,也可以自定義一個特殊的 user agent。

用 Python 抓網頁?你想問的都幫答好了

文字版見:gist。github。com/jkokatjuhha/083c1b5e14e64b3b1ff734bb45b859be

3。4 給 request 請求設定一個超時時間

在預設狀態,request 庫會無止境地等待某個請求返回對應的響應內容。所以,給它設定一個引數,等待超時就斷開連線,還是很有必要的。

用 Python 抓網頁?你想問的都幫答好了

文字版見:gist。github。com/jkokatjuhha/64cecefa0bf31c2b21111373c11fcc66

3。5 我是不是剛被遮蔽了?

如果你拿到的返回值經常是 404(找不到頁面)、403(被禁止)、408(訪問超時),就應該考慮你是不是被這個站點遮蔽了。

如果你對 HTTP 返回值不熟悉,看看我們之前解釋 HTTP 返回值的漫畫吧~

同樣,你也應該在返回的響應中對這類錯誤進行處理。

用 Python 抓網頁?你想問的都幫答好了

文字版見:gist。github。com/jkokatjuhha/a33467fae4c9f7fac64f067501b484ac

3。6 切換 IP 地址

就算你採用了隨機生成的 user agent,程式發起的所有連線都還用的是同一個 IP 地址:你的地址。雖然這通常並不會引起太多重視,畢竟很多圖書館、大學以及企業分別都只有少數幾個 IP 地址,由這些機構內的所有計算機共同使用。然而,如果在短時間內從某一個 IP 地址發出了巨量的請求,還是會被伺服器發現的。

這時候,你多年珍藏的科學上網工具就能大顯身手啦。

用 Python 抓網頁?你想問的都幫答好了

當你採用了代理、VPN或者其他技術之後,對應的網站會將你發起的請求識別為來自相應的伺服器,而不是你的。

3。7 蜜罐攻擊

蜜罐是引誘網頁爬蟲對其進行抓取或索引,從而進行偵測的一種技術手段。

比如,網頁上可能會存在一些“隱藏”連結,正常使用者在訪問的時候看不到這個連結,但爬蟲在處理 HTML 原始碼的時候會把它當作正常連結進行處理。此類連結有可能用 CSS 樣式設定了 display:none,或者設定成和背景相同的顏色,甚至採用比如藏在頁面中的不可見位置等手段。一旦你的爬蟲訪問了這類連結,你的 IP 地址可能就被記錄日誌,甚至伺服器可能直接將你遮蔽。

另外一種蜜罐,是用超連結建立一串近乎無限深度的目錄樹,如果有人訪問了足夠深位置的內容,那基本上可以確定這人不是個普通使用者。因此,在編寫爬蟲時,需要限制爬蟲取回的頁面數量,或控制遍歷深度。

4。 一些規則

在抓取之前,先看看目標網站是不是已經提供了公開的 API。畢竟透過 API 能更好更快(也合法)地獲取所需的資訊。比如社交網站 Twitter 就提供了許多不同的 API。

如果你需要抓取非常大量的資料,你應該考慮用一個數據庫把這些資料整理起來,方便之後進行分析和使用。這裡有一篇用 Python 操作本地資料庫的教程。

務必保持禮貌。有時候,甚至建議你直接和對方網站的運維人員取得聯絡,說不定他們能更方便快速地幫你解決你的機器人遇到的問題。

同時,再強調一遍,切記不要貪得無厭地發起太多請求,這會給目標網站帶來不必要的負載。

5。 利用並行加速爬蟲程式

如果你希望讓你的程式並行執行,一定要小心檢查自己的程式碼,否則可能你會突然發現自己正在榨乾目標伺服器的資源。同時,請一定一定認真看完上一節的幾個規則。最後,你需要確保自己已經理解了並行處理和併發處理,多執行緒和多程序之間的區別。

如果你在抓取過程中還要對巨量的頁面資訊進行預處理,你會發現平均每秒鐘能發起的請求數其實是相當少的。

在我個人的另一個抓取出租房價格的專案裡,因為抓取時的預處理資訊量實在太大,每秒能發起的請求數大約只有1個。處理 4000 個左右的連結,需要程式執行上大約一個小時。

為了並行傳送請求,你可能需要採用一個叫做 multiprocessing 的 Python 庫。

假設我們有100個頁面要發起請求,我們希望給將任務量平均分給每個處理器。假設你有 N 個 CPU,你可以把所有的頁面分成 N 個部分,每個 CPU 處理一個部分。每個程序都將有自己的名字,目標函式以及需要處理的引數。每個程序的名字可以在之後被呼叫,以便將獲取到的資訊寫入具體的檔案中。

後來,我將 4000 個頁面分成 4 份,我的 4 個 CPU 各分到 1000 個,於是總的請求數增加到 4 個/秒,總的抓取時間就減少到了 17 分鐘左右。

用 Python 抓網頁?你想問的都幫答好了

文字版見:gist。github。com/jkokatjuhha/7927b27cf7a831c48e223b7c06fbd401

最後,祝大家爬得開心順利!記得多關注我們哦!!

來自矽谷的終身學習平臺——優達學城(cn。udacity。com),專注於技能提升和求職法則,讓你在家能追隨 Google、Facebook、IBM 等行業大佬,從零開始掌握資料分析、機器學習、深度學習、人工智慧、無人駕駛等前沿技術,激發未來無限可能!