網(wǎng)頁抓取之Headless Chrome技巧
文章摘要:
1. 有很多庫可以控制Chrome,可以根據(jù)自己的喜歡選擇。
2. 使用Headless Chrome進行網(wǎng)頁抓取非常簡單,掌握下面的技巧之后更是如此。
3. Headless瀏覽器訪客可以被檢測到,但沒人這么做。
Headless Chrome簡述
Headless Chrome基于PhantomJS(QtWebKit內(nèi)核)由谷歌Chrome團隊開發(fā)。團隊表示將專注研發(fā)這個項目,未來也會不斷維護它。
這意味著對于網(wǎng)頁抓取和自動化的需求,現(xiàn)在可以體會Chrome的速度和功能,因為它具備世界上使用最多的瀏覽器的特性:支持所有網(wǎng)站,支持JS引擎,還有偉大的開發(fā)者工具API。太可怕啦!
選用哪個工具控制Headless Chrome?
市面上確實有很多NodeJS庫來支持Chrome新版headless模式,每一個都各有特色,我們自己的一款是NickJS。倘若沒有自己的抓取庫,怎么敢輕易的說自己是網(wǎng)頁抓取專家。
還有一套C++ API和社區(qū)用其他語言發(fā)布的庫,比如說基于GO語言。我們推薦使用NodeJS工具,因為它和網(wǎng)頁解析語言一樣(下面你會看到它有多便利)。
網(wǎng)頁抓取?它不是非法的嗎?
我們無意挑起無休止的爭論,但不到兩周前,美國一名地方法官下令允許第三方抓取領(lǐng)英(LinkedIn)公眾檔案。目前為止這只是初步的法令,訴訟仍會繼續(xù)進行,領(lǐng)英肯定會反對,但盡管放心,我們會密切關(guān)注情況,因為這篇文章里談?wù)摿撕芏嚓P(guān)于領(lǐng)英的內(nèi)容。
無論如何作為一篇技術(shù)性的文章,我們不會深入探究特定的抓取操作的合法性問題,我們應(yīng)該始終努力去尊重目標網(wǎng)站的ToS。而對你在這篇文章中所學(xué)到的造成任何損害概不負責(zé)。
目前為止學(xué)到的很酷的東西
下面列出的一些技巧,我們每天幾乎都在使用。代碼示例采用NickJS抓取庫,但它們很容易被其他Headless Chrome工具改寫,重要的是分享概念。
把cookies放回cookie jar
使用功能齊全的瀏覽器抓取會讓人安心,無需擔(dān)心CORS、會話、cookie、CSRF和其他web問題。
但有時登錄表單變得非常強硬,唯一的解決方案是恢復(fù)以前保存的會話cookie。當察覺故障時,有些網(wǎng)站會發(fā)送電子郵件或短信。我們就沒有時間這么做,只是使用已設(shè)置好的會話cookie打開頁面。
領(lǐng)英有一個很好的例子,設(shè)置li_atcookie能保證抓取機器訪問他們的社交網(wǎng)絡(luò)(請記?。鹤⒁庾鹬啬繕司W(wǎng)站Tos)。
await nick.setCookie({
name: “l(fā)i_at”,
value: “a session cookie value copied from your DevTools”,
domain: “www.linkedin.com”
})
相信像領(lǐng)英這樣的網(wǎng)站不會用一個有效的會話cookie來阻止一個真實的瀏覽器訪問。這么做相當有風(fēng)險,因為錯誤的信息會引發(fā)憤怒用戶的大量支持請求。
jQuery不會讓你失望
我們學(xué)到了一件重要的事,那就是通過jQuery從網(wǎng)頁提取數(shù)據(jù)真是太容易了。現(xiàn)在回想起來,這是顯而易見的。網(wǎng)站提供了一個高度結(jié)構(gòu)化的、可查詢的、包含數(shù)據(jù)元素的樹(它被稱為DOM),而jQuery是非常高效的DOM查詢庫。所以為什么不用它來抓取呢?這個技巧會屢試不爽。
很多網(wǎng)站都已經(jīng)使用了jQuery,所以只需在頁面中添加幾行就可以得到數(shù)據(jù)。
await tab.open(“news.ycombinator.com”)
await tab.untilVisible(“#hnmain”) // Make sure we have loaded the page
await tab.inject(“https://code.jquery.com/jquery-3.2.1.min.js”) // We‘re going to use jQuery to scrape
const hackerNewsLinks = await tab.evaluate((arg, callback) =》 {
// Here we’re in the page context. It‘s like being in your browser’s inspector tool
const data = []
$(“.athing”).each((index, element) =》 {
data.push({
title: $(element).find(“.storylink”).text(),
url: $(element).find(“.storylink”).attr(“href”)
})
})
callback(null, data)
})
印度、俄羅斯和巴基斯坦屏蔽機器人的做法有什么共同之處?
答案就是利用驗證碼解決服務(wù)器驗證。你可以幾美元買到上千個驗證碼,通常產(chǎn)生驗證碼不到30秒。但晚上的時候,因為沒有人,所以一般比較貴。
一個簡單的谷歌搜索將提供多個api來解決任何類型的驗證碼問題,包括獲取谷歌最新的recaptcha驗證碼(2美元1000個)。
將抓取機器連接到這些服務(wù)就如發(fā)出HTTP請求一樣簡單,現(xiàn)在機器人是人類了。
在我們的平臺上,用戶很容易解決他們需要的驗證碼問題。我們的巴斯特圖書館可以調(diào)用多個解決服務(wù)器驗證:
if (await tab.isVisible(“.captchaImage”)) {
// Get the URL of the generated CAPTCHA image
// Note that we could also get its -encoded value and solve it too
const captchaImageLink = await tab.evaluate((arg, callback) =》 {
callback(null, $(“.captchaImage”).attr(“src”))
})
// Make a call to a CAPTCHA solving service
const captchaAnswer = await buster.solveCaptchaImage(captchaImageLink)
// Fill the form with our solution
await tab.fill(“.captchaForm”, { “captcha-answer”: captchaAnswer }, { submit: true })
}
等待的是DOM元素,而不是固定的時間
經(jīng)??吹阶ト〕鯇W(xué)者讓他們的機器人在打開一個頁面或點擊一個按鈕后等待5到10秒——他們想要確定他們所做的動作有時間產(chǎn)生效果。
但這不是應(yīng)該做的。我們的3步理論適用于任何抓取場景:應(yīng)該等待的是想要操作的特定DOM元素。它更快、更清晰,如果出了問題,會得到更準確的錯誤提示。
await tab.open(“https://www.facebook.com/phbuster/posts/676836339178597”)
// await Promise.delay(5000) // DON‘T DO THIS!
await tab.waitUntilVisible(“.permalinkPost .UFILikeLink”)
// You can now safely click the “Like” button.。。
await tab.click(“.permalinkPost .UFILikeLink”)
在某些情況下,可能的確有必要偽造人為的延遲。可以使用
await Promise.delay(2000 + Math.random() * 3000)
糊弄過去。
MongoDB
我們發(fā)現(xiàn)MongoDB很適合大部分的抓取工作,它有一套優(yōu)秀的JS API和Mongoose ORM??紤]到當使用Headless Chrome時已經(jīng)處于NodeJS環(huán)境中,為什么不采用它呢?
JSON-LD 和微數(shù)據(jù)開發(fā)
有時網(wǎng)頁抓取并不需要理解DOM,而是要找到正確的“導(dǎo)出”按鈕。記住這一點可以節(jié)省了不少時間。
嚴謹?shù)恼f有些網(wǎng)站會比其他網(wǎng)站容易一些,以Macys.com為例,他們所有的產(chǎn)品頁面都以JSON-LD形式的產(chǎn)品數(shù)據(jù)顯示在DOM中??梢哉f到它們的任何一個產(chǎn)品頁面然后運行。
JSON.parse(document 。 queryselector(“ # productSEOData ”)。 innertext)
將得到一個可以插入MongoDB很好的數(shù)據(jù)對象,沒有真正抓取的必要!
網(wǎng)絡(luò)請求攔截
因為使用的是DevTools API,所以編寫的代碼具有使用Chrome的DevTools的等效功能。這意味著產(chǎn)生的機器人可以攔截、檢查甚至修改或中止任何網(wǎng)絡(luò)請求。
通過從LinkedIn下載PDF格式的簡歷來測試網(wǎng)絡(luò)請求攔截。從配置文件中單擊“Save to PDF”按鈕觸發(fā)XHR,其中響應(yīng)內(nèi)容為PDF文件,這是一種攔截文件并將其寫入磁盤的方法。
let cvRequestId = null
tab.driver.client.Network.responseReceived((e) =》 {
if (e.type === “XHR” && e.response.url.indexOf(“profile-profilePdf/”) 》 0) {
cvRequestId = e.requestId
}
})
tab.driver.client.Network.loadingFinished((e) =》 {
if (e.requestId === cvRequestId) {
tab.driver.client.Network.getResponseBody({ requestId: cvRequestId }, (err, cv) =》 {
require(“fs”).writeFileSync(“l(fā)inkedin-cv.pdf”, Buffer.from(cv.body, (cv.Encoded ? ’‘ : ’utf8‘)))
})
}
})
值得一提的是DevTools協(xié)議正在迅速發(fā)展,現(xiàn)在有一種方法可以使用Page.setDownloadBehavior()設(shè)置下載傳入文件的方式和路徑。我們還沒有測試它,但看起來很有前途!
廣告攔截
const nick = new Nick({
loadImages: false,
whitelist: [
/.*.aspx/,
/.*axd.*/,
/.*.html.*/,
/.*.js.*/
?。?,
blacklist: [
/.*fsispin360.js/,
/.*fsitouchzoom.js/,
/.*.ashx.*/,
/.*google.*/
?。?/p>
})
同樣可以通過屏蔽不必要的請求來加速抓取,分析、廣告和圖片是典型的屏蔽目標。然而,謹記它會讓機器人變得不那么像人(例如,如果屏蔽了所有的圖片,領(lǐng)英就不會正確響應(yīng)頁面請求——不確定這是否是故意的)。
在NickJS中用戶可以指定一個白名單和一個包含正則表達式或字符串的黑名單。白名單特別強大,但如果不小心的話,很容易讓目標網(wǎng)站崩潰。
DevTools協(xié)議也有Network.setBlockedURLs(),它使用帶有通配符的字符串數(shù)組作為輸入。
更重要的是,新版本的Chrome將帶有谷歌自帶的“廣告攔截器”——它更像是一個廣告“過濾器”。協(xié)議已經(jīng)有一個端點叫做Page.setAdBlockingEnabled()。
這就是我們說的技巧!
Headless Chrome檢測
最近發(fā)表的一篇文章列舉了多種方法來檢測Headless Chrome訪問者,也有可能檢測PhantomJS。那些方法描述了從基本的User-Agent字符串比較到更復(fù)雜的諸如觸發(fā)錯誤和檢查堆棧跟蹤的技術(shù)。
在憤怒的管理員和巧妙的機器人制造商之間,這基本上是一個加大版的貓捉老鼠游戲。但從未見過這些方法在官方實施。在技術(shù)上是可以檢測到自動訪問者,但誰會愿意面對潛在的錯誤消息呢?對于大型的網(wǎng)站來說尤其有風(fēng)險。
如果你知道那些網(wǎng)站有這些檢測功能,請告訴我們!
結(jié)束語
抓取從來沒有這么容易過,有了我們最新的工具和技術(shù),它甚至可以成為我們開發(fā)人員愉快而有趣的活動。
順便說一下,我們從Franciskim.co“我不需要臭烘烘的API”文章中受到了啟發(fā),非常感謝!另外,關(guān)于了解怎樣開始使用木偶的詳細說明,請點擊這里。
在下一篇文章中,將寫到關(guān)于“bot mitigation”的工具,比如Distill Networks,講述HTTP代理和IP地址分配的美妙世界。
在Phantombuster.com上有我們的抓取和自動化平臺 NickJS.org庫。有興趣的話還可以了解我們的3個抓取步驟的理論信息
非常好我支持^.^
(0) 0%
不好我反對
(0) 0%