如何用 Python 實現 Web 抓取?

OneAPM官方技術部落格發表於2016-05-30

【編者按】本文作者為 Blog Bowl 聯合創始人 Shaumik Daityari,主要介紹 Web 抓取技術的基本實現原理和方法。文章系國內 ITOM 管理平臺 OneAPM 編譯呈現,以下為正文。

如何用 Python 實現 Web 抓取?

隨著電子商務的蓬勃發展,筆者近年越來越著迷於比價應用。我在網路上(甚至線下)的每次購買,都是在各大電商網站深入調研後的結果。

筆者常用的比價應用包括:RedLaser, ShopSavvy 以及 BuyHatke。這些應用有效提高了價格透明度,進而為消費者節省了可觀的時間。

但是,你是否想過,這些應用如何得到那些重要資料?通常,它們會藉助 Web 抓取技術來完成該任務。

Web 抓取的定義

Web 抓取是抽取網路資料的過程。只要藉助合適的工具,任何你能看到的資料都可以進行抽取。在本文中,我們將重點介紹自動化抽取過程的程式,幫助你在較短時間內收集大量資料。除了筆者前文提到的用例,抓取技術的用途還包括:SEO 追蹤、工作追蹤、新聞分析以及筆者的最愛——社交媒體的情感分析!

一點提醒

在開啟 Web 抓取的探險之前,請確保自己瞭解相關的法律問題。許多網站在其服務條款中明確禁止對其內容進行抓取。例如,Medium 網站就寫道:“遵照網站 robots.txt 檔案中的規定進行的爬取操作(Crawling)是可接受的,但是我們禁止抓取(Scraping)操作。”對不允許抓取的網站進行抓取可能會使你進入他們的黑名單!與任何工具一樣,Web 抓取也可能用於複製網站內容之類的不良目的。此外,由 Web 抓取引起的法律訴訟也不在少數。

設定程式碼

在充分了解小心行事的必要之後,讓我們開始學習 Web 抓取。其實,Web 抓取可以通過任何程式語言實現,在不久之前,我們使用 Node 實現過。在本文中,考慮到其簡潔性與豐富的包支援,我們將使用 Python 實現抓取程式。

Web 抓取的基本過程

當你開啟網路中的某個站點時,就會下載其 HTML 程式碼,由你的 web 瀏覽器對其進行分析與展示。該 HTML 程式碼包含了你所看到的所有資訊。因此,通過分析 HTML 程式碼就能得到所需資訊(比如價格)。你可以使用正規表示式在資料海洋中搜尋你需要的資訊,也可以使用函式庫來解釋 HTML,同樣也能得到需要資料。

在 Python 中,我們將使用一個名為靚湯(Beautiful Soup)的模組對 HTML 資料進行分析。你可以藉助 pip 之類的安裝程式安裝之,執行如下程式碼即可:

pip install beautifulsoup4

或者,你也可以根據原始碼進行構建。在該模組的文件說明頁,可以看到詳細的安裝步驟。

安裝完成之後,我們大致會遵循以下步驟實現 web 抓取:

  • 向 URL 傳送請求
  • 接收響應
  • 分析響應以尋找所需資料

作為演示,我們將使用筆者的部落格 http://dada.theblogbowl.in/. 作為目標 URL。

前兩個步驟相對簡單,可以這樣完成:

from urllib import urlopen#Sending the http requestwebpage = urlopen('http://my_website.com/').read()

接下來,將響應傳給之前安裝的模組:

from bs4 import BeautifulSoup#making the soup! yummy ;)soup = BeautifulSoup(webpage, "html5lib")

請注意,此處我們選擇了 html5lib 作為解析器。根據 BeautifulSoup 的文件,你也可以為其選擇不同的解析器。

解析 HTML

在將 HTML 傳給 BeautifulSoup 之後,我們可以嘗試一些指令。譬如,檢查 HTML 標記程式碼是否正確,可以驗證該頁面的標題(在 Python 直譯器中):

>>> soup.title<title>Transcendental  Tech Talk</title>>>> soup.title.text
u'Transcendental  Tech Talk'
>>>

接下來,開始抽取頁面中的特定元素。譬如,我想抽取部落格中文章標題的列表。為此,我需要分析 HTML 的結構,這一點可以藉助 Chrome 檢查器完成。其他瀏覽器也提供了類似的工具。

如何用 Python 實現 Web 抓取? 使用 Chrome 檢查器檢查某個頁面的 HTML 結構

如你所見,所有文章標題都帶有 h3 標籤與兩個類屬性:post-titleentry-title 類。因此,用 post-title 類搜尋所有 h3 元素就能得到該頁的文章標題列表。在此例中,我們使用 BeautifulSoup 提供的 find_all 函式,並通過 class_ 引數確定所需的類:

>>> titles = soup.find_all('h3', class_ = 'post-title') #Getting all titles>>> titles[0].textu'\nKolkata #BergerXP IndiBlogger meet, Marketing Insights, and some Blogging Tips\n'>>>

只通過 post-title 類進行條目搜尋應該可以得到相同的結果:

>>> titles = soup.find_all(class_ = 'post-title') #Getting all items with class post-title>>> titles[0].textu'\nKolkata #BergerXP
   IndiBlogger meet, Marketing Insights, and some Blogging Tips\n'>>>

如果你想進一步瞭解條目所指的連結,可以執行下面的程式碼:

>>> for title in titles:...     # Each title is in the form of <h3 ...><a href=...>Post Title<a/></h3>...     print title.find("a").get("href")...http://dada.theblogbowl.in/2015/09/kolkata-bergerxp-indiblogger-meet.html
http://dada.theblogbowl.in/2015/09/i-got-published.html
http://dada.theblogbowl.in/2014/12/how-to-use-requestput-or-requestdelete.html
http://dada.theblogbowl.in/2014/12/zico-isl-and-atk.html...>>>

BeautifulSoup 內建了許多方法,可以幫助你玩轉 HTML。其中一些方法列舉如下:

>>> titles[0].contents
[u'\n', <a href="http://dada.theblogbowl.in/2015/09/kolkata-bergerxp-indiblogger-meet.html">Kolkata #BergerXP IndiBlogger meet, Marketing Insights, and some Blogging Tips</a>, u'\n']>>>

請注意,你也可以使用 children 屬性,不過它有點像生成器

>>> titles[0].parent<div class="post hentry uncustomized-post-template">\n<a name="6501973351448547458"></a>\n<h3 class="post-title entry-title">\n<a href="http://dada.theblogbowl.in/2015/09/kolkata-bergerxp-indiblogger-meet.html">Kolkata #BergerXP IndiBlogger ...
>>>

你也可以使用正規表示式搜尋 CSS 類,對此,本文件有詳細的介紹

使用 Mechanize 模擬登入

目前為止,我們做的只是下載一個頁面進而分析其內容。然而,web 開發者可能遮蔽了非瀏覽器發出的請求,或者有些網站內容只能在登入之後讀取。那麼,我們該如何處理這些情況呢?

對於第一種情況,我們需要在向頁面傳送請求時模擬一個瀏覽器。每個 HTTP 請求都包含一些相關的資料頭(header),其中包含了訪客瀏覽器、作業系統以及螢幕大小之類的資訊。我們可以改變這些資料頭,偽裝為瀏覽器傳送請求。

至於第二種情況,為了訪問帶有訪客限制的內容,我們需要登入該網站,使用 cookie 保持會話。下面,讓我們來看看在偽裝成瀏覽器的同時,如何完成這一點。

我們將藉助 cookielib 模組使用 cookie 管理會話。此外,我們還將用到 mechanize,後者可以使用 pip 之類的安裝程式進行安裝。

我們會通過 Blog Bowl 這個頁面進行登入,並訪問通知頁面。下面的程式碼通過行內註釋進行了解釋:

import mechanize
import cookielib

from urllib import urlopen
from bs4 import BeautifulSoup# Cookie Jarcj = cookielib.LWPCookieJar()

browser = mechanize.Browser()
browser.set_cookiejar(cj)
browser.set_handle_robots(False)
browser.set_handle_redirect(True)# Solving issue #1 by emulating a browser by adding HTTP headersbrowser.addheaders = [('User-agent', 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.1) Gecko/2008071615 Fedora/3.0.1-1.fc9 Firefox/3.0.1')]# Open Login Pagebrowser.open("http://theblogbowl.in/login/")# Select Login form (1st form of the page)browser.select_form(nr = 0)# Alternate syntax - browser.select_form(name = "form_name")# The first <input> tag of the form is a CSRF token# Setting the 2nd and 3rd tags to email and passwordbrowser.form.set_value("email@example.com", nr=1)
browser.form.set_value("password", nr=2)# Logging inresponse = browser.submit()# Opening new page after loginsoup = BeautifulSoup(browser.open('http://theblogbowl.in/notifications/').read(), "html5lib")

如何用 Python 實現 Web 抓取? 通知頁面的結構

# Print notificationsprint soup.find(class_ = "search_results").text

如何用 Python 實現 Web 抓取? 登入進通知頁面後的結果

結語

許多開發者會告訴你:你在網路上看到的任何資訊都可以被抓取。通過這篇文章,你學會了如何輕鬆抽取登入後才能看到的內容。此外,如果你的 IP 遭到了遮蔽,你可以掩蓋自己的 IP 地址(或選用其他地址)。同時,為了看起來像是人類在訪問,你應該在請求之間保留一定的時間間隔。

隨著人們對資料的需求不斷增長,web 抓取(不論原因好壞)技術在未來的應用只會更加廣泛。也因此,理解其原理是相當重要的,不管你是為了有效利用該技術,還是為了免受其坑害。

OneAPM 能幫您檢視 Python 應用程式的方方面面,不僅能夠監控終端的使用者體驗,還能監控伺服器效能,同時還支援追蹤資料庫、第三方 API 和 Web 伺服器的各種問題。想閱讀更多技術文章,請訪問 OneAPM 官方技術部落格

本文轉自 OneAPM 官方部落格

原文地址:https://www.sitepoint.com/web-scraping-for-beginners/

相關文章