還在一個個寫規則?來了解下爬蟲的智慧化解析吧!

qwer1030274531發表於2020-09-12

爬蟲是做什麼的?是幫助我們來快速獲取有效資訊的。然而做過爬蟲的人都知道,解析是個麻煩事。

比如一篇新聞吧,連結是這個:,頁面預覽圖如下:

預覽圖

我們需要從頁面中提取出標題、釋出人、釋出時間、釋出內容、圖片等內容。一般情況下我們需要怎麼辦?寫規則。

那麼規則都有什麼呢?懟正則,懟 CSS 選擇器,懟 XPath。我們需要對標題、釋出時間、來源等內容做規則匹配,更有甚者再需要正規表示式來輔助一下。我們可能就需要用 re、BeautifulSoup、pyquery 等庫來實現內容的提取和解析。

但如果我們有成千上萬個不同樣式的頁面怎麼辦呢?它們來自成千上萬個站點,難道我們還需要對他們一一寫規則來匹配嗎?這得要多大的工作量啊。另外這些萬一弄不好還會解析有問題。比如正規表示式在某些情況下匹配不了了,CSS、XPath 選擇器選錯位了也會出現問題。

想必大家可能見過現在的瀏覽器有閱讀模式,比如我們把這個頁面用 Safari 瀏覽器開啟,然後開啟閱讀模式,看看什麼效果:

Safari預覽

頁面一下子變得非常清爽,只保留了標題和需要讀的內容。原先頁面多餘的導航欄、側欄、評論等等的統統都被去除了。它怎麼做到的?難道是有人在裡面寫好規則了?那當然不可能的事。其實,這裡面就用到了智慧化解析了。

那麼本篇文章,我們就來了解一下頁面的智慧化解析的相關知識。

 

智慧化解析

所謂爬蟲的智慧化解析,顧名思義就是不再需要我們針對某一些頁面來專門寫提取規則了,我們可以利用一些演算法來計算出來頁面特定元素的位置和提取路徑。比如一個頁面中的一篇文章,我們可以透過演算法計算出來,它的標題應該是什麼,正文應該是哪部分割槽域,釋出時間是什麼等等。

其實智慧化解析是非常難的一項任務,比如說你給人看一個網頁的一篇文章,人可以迅速找到這篇文章的標題是什麼,釋出時間是什麼,正文是哪一塊,或者哪一塊是廣告位,哪一塊是導航欄。但給機器來識別的話,它面臨的是什麼?僅僅是一系列的 HTML 程式碼而已。那究竟機器是怎麼做到智慧化提取的呢?其實這裡面融合了多方面的資訊。

  • 比如標題。一般它的字號是比較大的,而且長度不長,位置一般都在頁面上方,而且大部分情況下它應該和 title 標籤裡的內容是一致的。

  • 比如正文。它的內容一般是最多的,而且會包含多個段落 p 或者圖片 img 標籤,另外它的寬度一般可能會佔用到頁面的三分之二區域,並且密度(字數除以標籤數量)會比較大。

  • 比如時間。不同語言的頁面可能不同,但時間的格式是有限的,如 2019-02-20 或者 2019/02/20 等等,也有的可能是美式的記法,順序不同,這些也有特定的模式可以識別。

  • 比如廣告。它的標籤一般可能會帶有 ads 這樣的字樣,另外大多數可能會處於文章底部、頁面側欄,並可能包含一些特定的外鏈內容。

另外還有一些特點就不再一一贅述了,這其中包含了區塊位置、區塊大小、區塊標籤、區塊內容、區塊疏密度等等多種特徵,另外很多情況下還需要藉助於視覺的特徵,所以說這裡面其實結合了演算法計算、視覺處理、自然語言處理等各個方面的內容。如果能把這些特徵綜合運用起來,再經過大量的資料訓練,是可以得到一個非常不錯的效果的。

 

業界進展

未來的話,頁面也會越來越多,頁面的渲染方式也會發生很大的變化,爬蟲也會越來越難做,智慧化爬蟲也將會變得越來越重要。

目前工業界,其實已經有落地的演算法應用了。經過我的一番調研,目前發現有這麼幾種演算法或者服務對頁面的智慧化解析做的比較好:

  • Diffbot,國外的一家專門來做智慧化解析服務的公司,

  • Boilerpipe,Java 語言編寫的一個頁面解析演算法,

  • Embedly,提供頁面解析服務的公司,

  • Readability,是一個頁面解析演算法,但現在官方的服務已經關閉了,

  • Mercury,Readability 的替代品,https://mercury.postlight.com/

  • Goose,Java 語音編寫的頁面解析演算法,

那麼這幾種演算法或者服務到底哪些好呢,Driffbot 官方曾做過一個對比評測,使用 Google 新聞的一些文章,使用不同的演算法依次摘出其中的標題和文字,然後與真實標註的內容進行比較,比較的指標就是文字的準確率和召回率,以及根據二者計算出的 F1 分數。

其結果對比如下:

Service/Software Precision Recall F1-Score

Diffbot

0.968

0.978

0.971

Boilerpipe

0.893

0.924

0.893

Readability

0.819

0.911

0.854

AlchemyAPI

0.876

0.892

0.850

Embedly

0.786

0.880

0.822

Goose

0.498

0.815

0.608

經過對比我們可以發現,Diffbot 的準確率和召回率都獨佔鰲頭,其中的 F1 值達到了 0.97,可以說準確率非常高了。另外接下來比較厲害的就是 Boilerpipe 和 Readability,Goose 的表現則非常差,F1 跟其他的演算法差了一大截。下面是幾個演算法的 F1 分數對比情況:

F1分數對比

有人可能好奇為什麼 Diffbot 這麼厲害?我也查詢了一番。Diffbot 自 2010 年以來就致力於提取 Web 頁面資料,並提供許多 API 來自動解析各種頁面。其中他們的演算法依賴於自然語言技術、機器學習、計算機視覺、標記檢查等多種演算法,並且所有的頁面都會考慮到當前頁面的樣式以及視覺化佈局,另外還會分析其中包含的影像內容、CSS 甚至 Ajax 請求。另外在計算一個區塊的置信度時還考慮到了和其他區塊的關聯關係,基於周圍的標記來計算每個區塊的置信度。

總之,Diffbot 也是一直致力於這一方面的服務,整個 Diffbot 就是頁面解析起家的,現在也一直專注於頁面解析服務,準確率高也就不足為怪了。

但它們的演算法開源了嗎?很遺憾,並沒有,而且我也沒有找到相關的論文介紹它們自己的具體演算法。

所以,如果想實現這麼好的效果,那就使用它們家的服務就好了。

接下來的內容,我們就來說說如何使用 Diffbot 來進行頁面的智慧解析。另外還有 Readability 演算法也非常值得研究,我會寫專門的文章來介紹 Readability 及其與 Python 的對接使用。

 

Diffbot 頁面解析

首先我們需要註冊一個賬號,它有 15 天的免費試用,註冊之後會獲得一個 Developer Token,這就是使用 Diffbot 介面服務的憑證。

接下來切換到它的測試頁面中,連結為:/dev/home/,我們來測試一下它的解析效果到底是怎樣的。

這裡我們選擇的測試頁面就是上文所述的頁面,連結為:,API 型別選擇 Article API,然後點選 Test Drive 按鈕,接下來它就會出現當前頁面的解析結果:

結果

這時候我們可以看到,它幫我們提取出來了標題、釋出時間、釋出機構、釋出機構連結、正文內容等等各種結果。而且目前來看都十分正確,時間也自動識別之後做了轉碼,是一個標準的時間格式。

接下來我們繼續下滑,檢視還有什麼其他的欄位,這裡我們還可以看到有 html 欄位,它和 text 不同的是,它包含了文章內容的真實 HTML 程式碼,因此圖片也會包含在裡面,如圖所示:

結果

另外最後面還有 images 欄位,他以列表形式返回了文章套圖及每一張圖的連結,另外還有文章的站點名稱、頁面所用語言等等結果,如圖所示:

結果

當然我們也可以選擇 JSON 格式的返回結果,其內容會更加豐富,例如圖片還返回了其寬度、高度、圖片描述等等內容,另外還有各種其他的結果如麵包屑導航等等結果,如圖所示:

結果

經過手工核對,發現其返回的結果都是完全正確的,準確率相當之高!

所以說,如果你對準確率要求沒有那麼非常非常嚴苛的情況下,使用 Diffbot 的服務可以幫助我們快速地提取頁面中所需的結果,省去了我們絕大多數的手工勞動,可以說是非常讚了。

但是,我們也不能總在網頁上這麼試吧。其實 Diffbot 也提供了官方的 API 文件,讓我們來一探究竟。

 

Diffbot API

Driffbot 提供了多種 API,如 Analyze API、Article API、Disscussion API 等。

下面我們以 Article API 為例來說明一下它的用法,其官方文件地址為:/dev/docs/article/,API 呼叫地址為:

我們可以用 GET 方式來進行請求,其中的 Token 和 URL 都可以以引數形式傳遞給這個 API,其必備的引數有:

  • token:即 Developer Token

  • url:即要解析的 URL 連結

另外它還有幾個可選引數:

  • fields:用來指定返回哪些欄位,預設已經有了一些固定欄位,這個引數可以指定還可以額外返回哪些可選欄位

  • paging:如果是多頁文章的話,如果將這個引數設定為 false 則可以禁止多頁內容拼接

  • maxTags:可以設定返回的 Tag 最大數量,預設是 10 個

  • tagConfidence:設定置信度的閾值,超過這個值的 Tag 才會被返回,預設是 0.5

  • discussion:如果將這個引數設定為 false,那麼就不會解析評論內容

  • timeout:在解析的時候等待的最長時間,預設是 30 秒

  • callback:為 JSONP 型別的請求而設計的回撥

這裡大家可能關注的就是 fields 欄位了,在這裡我專門做了一下梳理,首先是一些固定欄位:

  • type:文字的型別,這裡就是 article 了

  • title:文章的標題

  • text:文章的純文字內容,如果是分段內容,那麼其中會以換行符來分隔

  • html:提取結果的 HTML 內容

  • date:文章的釋出時間,其格式為 RFC 1123

  • estimatedDate:如果日期時間不太明確,會返回一個預估的時間,如果文章超過兩天或者沒有釋出日期,那麼這個欄位就不會返回

  • author:作者

  • authorUrl:作者的連結

  • discussion:評論內容,和 Disscussion API 返回結果一樣

  • humanLanguage:語言型別,如英文還是中文等

  • numPages:如果文章是多頁的,這個引數會控制最大的翻頁拼接數目

  • nextPages:如果文章是多頁的,這個引數可以指定文章後續連結

  • siteName:站點名稱

  • publisherRegion:文章釋出地區

  • publisherCountry:文章釋出國家

  • pageUrl:文章連結

  • resolvedPageUrl:如果文章是從 pageUrl 重定向過來的,則返回此內容

  • tags:文章的標籤或者文章包含的實體,根據自然語言處理技術和 DBpedia 計算生成,是一個列表,裡面又包含了子欄位:

  • label:標籤名

  • count:標籤出現的次數

  • score:標籤置信度

  • rdfTypes:如果實體可以由多個資源表示,那麼則返回相關的 URL

  • type:型別

  • uri:Diffbot Knowledge Graph 中的實體連結

  • images:文章中包含的圖片

  • videos:文章中包含的影片

  • breadcrumb:麵包屑導航資訊

  • diffbotUri:Diffbot 內部的 URL 連結

以上的預定欄位就是如果可以返回那就會返回的欄位,是不能定製化配置的,另外我們還可以透過 fields 引數來指定擴充套件如下可選欄位:

  • quotes:引用資訊

  • sentiment:文章的情感值,-1 到 1 之間

  • links:所有超連結的頂級連結

  • querystring:請求的引數列表

好,以上便是這個 API 的用法,大家可以申請之後使用這個 API 來做智慧化解析了。

下面我們用一個例項來看一下這個 API 的用法,程式碼如下:

import requests, jsonurl = ''params = {    'token': '77b41f6fbb24495113d52836528fa',    'url': '',    'fields': 'meta' }response = requests.get(url, params=params)print(json.dumps(response.json(), indent=2, ensure_ascii=False))

這裡首先定義了 API 的連結,然後指定了 params 引數,即 GET 請求引數。

引數中包含了必選的 token、url 欄位,也設定了可選的 fields 欄位,其中  fields 為可選的擴充套件欄位 meta 標籤。

我們來看下執行結果,結果如下:

{  "request": {    "pageUrl": "",    "api": "article",    "fields": "sentiment, meta",    "version": 3  },  "objects": [    {      "date": "Wed, 20 Feb 2019 02:26:00 GMT",      "images": [        {          "naturalHeight": 460,          "width": 640,          "diffbotUri": "image|3|-1139316034",          "url": "},        // ...      ],      "author": "中國新聞網",      "estimatedDate": "Wed, 20 Feb 2019 06:47:52 GMT",      "diffbotUri": "article|3|1591137208",      "siteName": "ifeng.com",      "type": "article",      "title": "故宮,你低調點!故宮:不,實力已不允許我繼續低調",      "breadcrumb": [        {          "link": "資訊"        },        {          "link": "大陸"        }      ],      "humanLanguage": "zh",      "meta": {        "og": {          "og:time ": "2019-02-20 02:26:00",          "og:image": "鳳凰資訊",          "og: webtype": "news",          "og:title": "故宮,你低調點!故宮:不,實力已不允許我繼續低調",          "og:url": "",          "og:description": "  “我的名字叫紫禁城,快要600歲了,這上元的夜啊,總是讓我沉醉,這麼久了卻從未停止。”   “重"        },        "referrer": "always",        "description": "  “我的名字叫紫禁城,快要600歲了,這上元的夜啊,總是讓我沉醉,這麼久了卻從未停止。”   “重",        "keywords": "故宮 紫禁城 故宮博物院 燈光 元宵節 博物館 一票難求 元之 中新社 午門 杜洋 藏品 文化 皇帝 清明上河圖 元宵 千里江山圖卷 中英北京條約 中法北京條約 天津條約",        "title": "故宮,你低調點!故宮:不,實力已不允許我繼續低調_鳳凰資訊"      },      "authorUrl": "https://feng.ifeng.com/author/308904",      "pageUrl": "",      "html": "<p>&ldquo;我的名字叫紫禁城,快要600歲了,這上元的夜啊,總是讓我沉醉,這麼久了卻從未停止。...</blockquote> </blockquote>",      "text": "“我的名字叫紫禁城,快要600歲了,這上元的夜啊,總是讓我沉醉,這麼久了卻從未停止。”\n“...",      "authors": [        {          "name": "中國新聞網",          "link": "}      ]    }  ]}

可見其返回瞭如上的內容,是一個完整的 JSON 格式,其中包含了標題、正文、釋出時間等等各種內容。

可見,不需要我們配置任何提取規則,我們就可以完成頁面的分析和抓取,得來全不費功夫。

 

Diffbot SDK

另外 Diffbot 還提供了幾乎所有語言的 SDK 支援,我們也可以使用 SDK 來實現如上功能,連結為:/dev/docs/libraries/,如果大家使用  Python 的話,可以直接使用 Python 的 SDK 即可,Python 的 SDK 連結為:。

這個庫並沒有釋出到 PyPi,需要自己下載並匯入使用,另外這個庫是使用 Python 2 寫的,其實本質上就是呼叫了 requests 庫,如果大家感興趣的話可以看一下。

下面是一個呼叫示例:

from client import DiffbotClient,DiffbotCrawldiffbot = DiffbotClient()token = 'your_token'url = '(url, token, api)

透過這行程式碼我們就可以透過呼叫 Article API 來分析我們想要的 URL 連結了,返回結果是類似的。

具體的用法大家直接看下它的原始碼註釋就一目瞭然了,還是很清楚的。

好,以上便是對智慧化提取頁面原理的基本介紹以及對 Diffbot 的用法的講解,後面我會繼續介紹其他的智慧化解析方法以及一些相關實戰,希望大家可以多多關注。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30239065/viewspace-2719022/,如需轉載,請註明出處,否則將追究法律責任。

相關文章