網頁擷取看似魔法,Python生態圈的各式方案更增添其神祕色彩,然而網頁擷取是個漸進需求,對於簡單的擷取,運用標準程式庫是個不錯的出發點,面對多樣化的第三方程式庫,也能根據需求來選擇。

從標準程式庫開始

就現今而言,網頁擷取看來像個魔法,畢竟使用者在瀏覽器中看到的畫面,是諸多技術組合後的成果,對於複雜的Web應用程式來說,更是如此。

基本上,網頁擷取是代表著Web應用程式未直接提供API,為何我們會產生魔幻的感覺?正是來自釐清複雜技術組成的原理並加以突破,以蒐集想要的資訊,而對開發者來說,是一種能力的檢驗,若能進一步運用蒐集而來的資料,也能使其具有實用的價值。

然而實際上,並非每個Web應用程式都會用上繁複機制。如果瀏覽器呈現畫面用的靜態HTML原始碼中,就包含了多種必要的資訊,標準程式庫中的urllib模組,就可以應付下載頁面的需求,同時,對於簡單的表單發送,也足以應付。例如,在《網站擷取 - 使用 Python》中,有大半的範例都是基於urllib,如果你想知道如何親手實作個爬行程式,以此爬行整個網站甚至網際網路,這本書也是個不錯的參考。

那麼分析網頁呢?這取決於你對想要擷取的資訊有多少認識,如果想擷取的是HTML中沒有上下文關係的資訊,例如特定文字、超鏈結、圖片標籤等,規則表示式也是個不錯的選擇。例如,擷取出<img>標籤的src屬性,以便進一步下載頁面中連結的圖片,就可以簡單地透過(?s)<img.+?src="(.+?)".*?>來擷取出圖片的鏈結。

有時,你必須處理HTML標籤,例如,在清理網頁資料時,將HTML標籤去除,只留下文字資訊是常見需求,此時,可透過規則表示式,尋找HTML標籤以空字串取代。

雖然這也是個方式,不過,HTML剖析並非規則表示式的主要任務,因為標準程式庫的html.parser模組已提供HTML剖析器(Parser)的功能,透過繼承HTMLParser,實作handle_data等方法,就可以針對HTML中特定元素進行處理。

省力的第三方程式庫

有時,我們想擷取的資訊,必須先登入為使用者,或者是點選過同意之類的按鈕,才可以下載內容。這基本上是種會話過程,開發過Web應用程式的人應該都知道:許多會話機制的實現,都是基於Cookie實現,標準程式庫urllib能控制當中許多細節,然而相對地,對於常見的需求,像是Cookie等,程式撰寫上就繁瑣許多,若需要更高階的API封裝,Python社群推薦的第三方程式庫是requests,使用上會容易許多。

requests可以透過dict物件來組織Cookie資訊,並在requests.get時,指定給cookies參數;如果不想自行設置Cookie,而想要requests接受Cookie後,自動在後續的請求附上Cookie,我們可以建立requests的Session實例,而且,該實例模擬了瀏覽器與Web應用程式的對話,若對話期間有相關的Cookie設置,Session實例會自動附在每次的請求中。

若需要處理HTML本身,從中擷取標籤屬性等必要的資訊,社群中耳熟能詳的是Beautiful Soup,它必須有個剖析器實作。若沒有指定,此時預設使用標準程式庫的html.parser剖析器,我們也可以選擇第三方的剖析器,像是html5lib,以及號稱解析速度最快的lxml。

透過其BeautifulSoup實例的find_all、find等方法,我們可以指定標籤、屬性等條件,在指定條件限制時,也可以進一步指定規則表示式,對於去除HTML標籤,單純只想分析文件內容的任務,只要透過BeautifulSoup實例的get_text()方法來進行,就可以輕鬆完成。

另一個社群常見的網頁剖析工具是pyquery,基於lxml剖析器,仿造jQuery來建立是其賣點,若偏好使用CSS選擇器語法來處理HTML標籤等資訊,是個可以考慮的方案。

需要有個瀏覽器

如同一開始談到的,網頁擷取之所以像個魔法,主要是現代Web應用程式的技術組成複雜。例如,現在不少網站透過requests之類的方案,取得的HTML頁面本身沒什麼用,因為它們會運用JavaScript執行非同步請求,即時地取得資料並呈現出來,這個過程可能是簡單到非常複雜,想自行克服這一切,可能就相當於要寫個瀏覽器了。

既然如此,那就直接驅動真正的瀏覽器,待瀏覽器處理完一切後,再從渲染出來的頁面取得想要的資料。對於這個方案,Python社群最常見的方案是Selenium,它本身並不包含瀏覽器,必須透過各瀏覽器對應的驅動器,促使電腦上已安裝的瀏覽器才能運作,由於可以驅動真正的瀏覽器,也常被用來作為自動化測試Web應用程式的工具。

在過去Python社群中常見使用PhantomJS,這是因為它提供了無頭(headless)模式,因為網頁擷取時,我們通常希望在背景執行,不需要有視窗畫面。然而,PhantomJS因為缺少貢獻者,2018年3月就暫停維護了,實際上,現代瀏覽器多半也提供無頭模式,像是Chrome、Firefox、Edge等,不必再使用PhantomJS。

相對於老牌的Selenium,微軟也提供了網頁自動化測試工具Playwright,最初只支援Node.js,支援的瀏覽器包含Chromium、Firefox、WebKit等,2020年9月底,進一步支援了Python,可透過pip安裝playwright,之後透過python -m playwright install安裝瀏覽器,而playwright預設就會以無頭模式啟動瀏覽器,可透過將launch方法的headless設為False,來取消無頭模式。

只不過,使用Selenium、Playwright這類方案,代表著要學習它們各自的API。有沒有方案可以基於urllib、requests,又能取得JavaScript執行後渲染出來的HTML網頁呢?我們可以透過Splash,它定位為JavaScript渲染服務,由於它基於Twisted、Qt──Twisted令其本身具備非同步處理能力,Qt實現了webkit瀏覽器,而且可以透過Docker來安裝,啟動後,則以一臺伺服器的方式接受客戶端請求。

也就是說,Splash就像個代理伺服器,會請求指定的目標網址,取得回應後進行頁面渲染,然後傳回給客戶端。這表示,開發者可以用自己的方案設計請求,像是urllib、requests,甚至是以async、await的方式設計非同步請求,正如《Python非同步設計 - 使用Asyncio》的第四章所言,就有結合Splash的案例可以供研究。

框架解決重複流程

偶而寫個網頁擷取,基本上,有上述方案應該就很足夠了,然而,若經常從事這類工作,我們會發現有許多重複的工作,此時,可透過重構抽取這些重複,這當然是件好事,不過,若需要有框架代勞這件事的話,可以考慮Scrapy,並且按照它規範的方式實現相關的元件,它會代為處理剩下的細節。

當然,別忘了Scrapy是個框架,本身也十分龐大,如果只是個簡單的擷取應用需求,用Scrapy來因應時,不見得能從中獲得效益;或者應該說,網頁擷取這部份,本來就是Python的強項,方案非常多,前提是釐清自身需求,才能從各式龐雜的程式庫中,找到合適的方案。

專欄作者

熱門新聞

Advertisement