爬蟲是作什麼的?是幫助咱們來快速獲取有效信息的。然而作過爬蟲的人都知道,解析是個麻煩事。javascript
好比一篇新聞吧,連接是這個:https://news.ifeng.com/c/7kQcQG2peWU,頁面預覽圖以下:html
預覽圖java
咱們須要從頁面中提取出標題、發佈人、發佈時間、發佈內容、圖片等內容。通常狀況下咱們須要怎麼辦?寫規則。python
那麼規則都有什麼呢?懟正則,懟 CSS 選擇器,懟 XPath。咱們須要對標題、發佈時間、來源等內容作規則匹配,更有甚者再須要正則表達式來輔助一下。咱們可能就須要用 re、BeautifulSoup、pyquery 等庫來實現內容的提取和解析。git
但若是咱們有成千上萬個不一樣樣式的頁面怎麼辦呢?它們來自成千上萬個站點,難道咱們還須要對他們一一寫規則來匹配嗎?這得要多大的工做量啊。另外這些萬一弄很差還會解析有問題。好比正則表達式在某些狀況下匹配不了了,CSS、XPath 選擇器選錯位了也會出現問題。github
想必你們可能見過如今的瀏覽器有閱讀模式,好比咱們把這個頁面用 Safari 瀏覽器打開,而後開啓閱讀模式,看看什麼效果:web
Safari預覽正則表達式
頁面一會兒變得很是清爽,只保留了標題和須要讀的內容。原先頁面多餘的導航欄、側欄、評論等等的通通都被去除了。它怎麼作到的?難道是有人在裏面寫好規則了?那固然不可能的事。其實,這裏面就用到了智能化解析了。算法
那麼本篇文章,咱們就來了解一下頁面的智能化解析的相關知識。json
所謂爬蟲的智能化解析,顧名思義就是再也不須要咱們針對某一些頁面來專門寫提取規則了,咱們能夠利用一些算法來計算出來頁面特定元素的位置和提取路徑。好比一個頁面中的一篇文章,咱們能夠經過算法計算出來,它的標題應該是什麼,正文應該是哪部分區域,發佈時間是什麼等等。
其實智能化解析是很是難的一項任務,好比說你給人看一個網頁的一篇文章,人能夠迅速找到這篇文章的標題是什麼,發佈時間是什麼,正文是哪一塊,或者哪一塊是廣告位,哪一塊是導航欄。但給機器來識別的話,它面臨的是什麼?僅僅是一系列的 HTML 代碼而已。那究竟機器是怎麼作到智能化提取的呢?其實這裏面融合了多方面的信息。
好比標題。通常它的字號是比較大的,並且長度不長,位置通常都在頁面上方,並且大部分狀況下它應該和 title 標籤裏的內容是一致的。
好比正文。它的內容通常是最多的,並且會包含多個段落 p 或者圖片 img 標籤,另外它的寬度通常可能會佔用到頁面的三分之二區域,而且密度(字數除以標籤數量)會比較大。
好比時間。不一樣語言的頁面可能不一樣,但時間的格式是有限的,如 2019-02-20 或者 2019/02/20 等等,也有的多是美式的記法,順序不一樣,這些也有特定的模式能夠識別。
好比廣告。它的標籤通常可能會帶有 ads 這樣的字樣,另外大多數可能會處於文章底部、頁面側欄,並可能包含一些特定的外鏈內容。
另外還有一些特色就再也不一一贅述了,這其中包含了區塊位置、區塊大小、區塊標籤、區塊內容、區塊疏密度等等多種特徵,另外不少狀況下還須要藉助於視覺的特徵,因此說這裏面其實結合了算法計算、視覺處理、天然語言處理等各個方面的內容。若是能把這些特徵綜合運用起來,再通過大量的數據訓練,是能夠獲得一個很是不錯的效果的。
將來的話,頁面也會愈來愈多,頁面的渲染方式也會發生很大的變化,爬蟲也會愈來愈難作,智能化爬蟲也將會變得愈來愈重要。
目前工業界,其實已經有落地的算法應用了。通過個人一番調研,目前發現有這麼幾種算法或者服務對頁面的智能化解析作的比較好:
Diffbot,國外的一家專門來作智能化解析服務的公司,https://www.diffbot.com
Boilerpipe,Java 語言編寫的一個頁面解析算法,https://github.com/kohlschutter/boilerpipe
Embedly,提供頁面解析服務的公司,https://embed.ly/extract
Readability,是一個頁面解析算法,但如今官方的服務已經關閉了,https://www.readability.com/
Mercury,Readability 的替代品,https://mercury.postlight.com/
Goose,Java 語音編寫的頁面解析算法,https://github.com/GravityLabs/goose
那麼這幾種算法或者服務到底哪些好呢,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 的對接使用。
首先咱們須要註冊一個帳號,它有 15 天的免費試用,註冊以後會得到一個 Developer Token,這就是使用 Diffbot 接口服務的憑證。
接下來切換到它的測試頁面中,連接爲:https://www.diffbot.com/dev/home/,咱們來測試一下它的解析效果究竟是怎樣的。
這裏咱們選擇的測試頁面就是上文所述的頁面,連接爲:https://news.ifeng.com/c/7kQcQG2peWU,API 類型選擇 Article API,而後點擊 Test Drive 按鈕,接下來它就會出現當前頁面的解析結果:
結果
這時候咱們能夠看到,它幫咱們提取出來了標題、發佈時間、發佈機構、發佈機構連接、正文內容等等各類結果。並且目前來看都十分正確,時間也自動識別以後作了轉碼,是一個標準的時間格式。
接下來咱們繼續下滑,查看還有什麼其餘的字段,這裏咱們還能夠看到有 html 字段,它和 text 不一樣的是,它包含了文章內容的真實 HTML 代碼,所以圖片也會包含在裏面,如圖所示:
結果
另外最後面還有 images 字段,他以列表形式返回了文章套圖及每一張圖的連接,另外還有文章的站點名稱、頁面所用語言等等結果,如圖所示:
結果
固然咱們也能夠選擇 JSON 格式的返回結果,其內容會更加豐富,例如圖片還返回了其寬度、高度、圖片描述等等內容,另外還有各類其餘的結果如麪包屑導航等等結果,如圖所示:
結果
通過手工覈對,發現其返回的結果都是徹底正確的,準確率至關之高!
因此說,若是你對準確率要求沒有那麼很是很是嚴苛的狀況下,使用 Diffbot 的服務能夠幫助咱們快速地提取頁面中所需的結果,省去了咱們絕大多數的手工勞動,能夠說是很是讚了。
可是,咱們也不能總在網頁上這麼試吧。其實 Diffbot 也提供了官方的 API 文檔,讓咱們來一探究竟。
Driffbot 提供了多種 API,如 Analyze API、Article API、Disscussion API 等。
下面咱們以 Article API 爲例來講明一下它的用法,其官方文檔地址爲:https://www.diffbot.com/dev/docs/article/,API 調用地址爲:
https://api.diffbot.com/v3/article
咱們能夠用 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, json
url = 'https://api.diffbot.com/v3/article'
params = {
'token': '77b41f6fbb24495113d52836528fa',
'url': 'https://news.ifeng.com/c/7kQcQG2peWU',
'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": "https://news.ifeng.com/c/7kQcQG2peWU",
"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": "http://e0.ifengimg.com/02/2019/0219/1731DC8A29EB2219C7F2773CF9CF319B3503D0A1_size382_w690_h460.png",
"naturalWidth": 690,
"primary": true,
"height": 426
},
// ...
],
"author": "中國新聞網",
"estimatedDate": "Wed, 20 Feb 2019 06:47:52 GMT",
"diffbotUri": "article|3|1591137208",
"siteName": "ifeng.com",
"type": "article",
"title": "故宮,你低調點!故宮:不,實力已不容許我繼續低調",
"breadcrumb": [
{
"link": "https://news.ifeng.com/",
"name": "資訊"
},
{
"link": "https://news.ifeng.com/shanklist/3-35197-/",
"name": "大陸"
}
],
"humanLanguage": "zh",
"meta": {
"og": {
"og:time ": "2019-02-20 02:26:00",
"og:image": "https://e0.ifengimg.com/02/2019/0219/1731DC8A29EB2219C7F2773CF9CF319B3503D0A1_size382_w690_h460.png",
"og:category ": "鳳凰資訊",
"og: webtype": "news",
"og:title": "故宮,你低調點!故宮:不,實力已不容許我繼續低調",
"og:url": "https://news.ifeng.com/c/7kQcQG2peWU",
"og:description": " 「個人名字叫紫禁城,快要600歲了,這上元的夜啊,老是讓我沉醉,這麼久了卻從未中止。」 「重"
},
"referrer": "always",
"description": " 「個人名字叫紫禁城,快要600歲了,這上元的夜啊,老是讓我沉醉,這麼久了卻從未中止。」 「重",
"keywords": "故宮 紫禁城 故宮博物院 燈光 元宵節 博物館 一票難求 元之 中新社 午門 杜洋 藏品 文化 皇帝 清明上河圖 元宵 千里江山圖卷 中英北京條約 中法北京條約 天津條約",
"title": "故宮,你低調點!故宮:不,實力已不容許我繼續低調_鳳凰資訊"
},
"authorUrl": "https://feng.ifeng.com/author/308904",
"pageUrl": "https://news.ifeng.com/c/7kQcQG2peWU",
"html": "<p>“個人名字叫紫禁城,快要600歲了,這上元的夜啊,老是讓我沉醉,這麼久了卻從未中止。...</blockquote> </blockquote>",
"text": "「個人名字叫紫禁城,快要600歲了,這上元的夜啊,老是讓我沉醉,這麼久了卻從未中止。」\n「...",
"authors": [
{
"name": "中國新聞網",
"link": "https://feng.ifeng.com/author/308904"
}
]
}
]
}
可見其返回瞭如上的內容,是一個完整的 JSON 格式,其中包含了標題、正文、發佈時間等等各類內容。
可見,不須要咱們配置任何提取規則,咱們就能夠完成頁面的分析和抓取,得來全不費功夫。
另外 Diffbot 還提供了幾乎全部語言的 SDK 支持,咱們也可使用 SDK 來實現如上功能,連接爲:https://www.diffbot.com/dev/docs/libraries/,若是你們使用 Python 的話,能夠直接使用 Python 的 SDK 便可,Python 的 SDK 連接爲:https://github.com/diffbot/diffbot-python-client。
這個庫並無發佈到 PyPi,須要本身下載並導入使用,另外這個庫是使用 Python 2 寫的,其實本質上就是調用了 requests 庫,若是你們感興趣的話能夠看一下。
下面是一個調用示例:
from client import DiffbotClient,DiffbotCrawl
diffbot = DiffbotClient()
token = 'your_token'
url = 'http://shichuan.github.io/javascript-patterns/'
api = 'article'
response = diffbot.request(url, token, api)
經過這行代碼咱們就能夠經過調用 Article API 來分析咱們想要的 URL 連接了,返回結果是相似的。
具體的用法你們直接看下它的源碼註釋就一目瞭然了,仍是很清楚的。
好,以上即是對智能化提取頁面原理的基本介紹以及對 Diffbot 的用法的講解,後面我會繼續介紹其餘的智能化解析方法以及一些相關實戰,但願你們能夠多多關注。