網(wǎng)頁(yè)抓取之Headless Chrome技巧
推薦 + 挑錯(cuò) + 收藏(0) + 用戶評(píng)論(0)
文章摘要:
1. 有很多庫(kù)可以控制Chrome,可以根據(jù)自己的喜歡選擇。
2. 使用Headless Chrome進(jìn)行網(wǎng)頁(yè)抓取非常簡(jiǎn)單,掌握下面的技巧之后更是如此。
3. Headless瀏覽器訪客可以被檢測(cè)到,但沒(méi)人這么做。
Headless Chrome簡(jiǎn)述
Headless Chrome基于PhantomJS(QtWebKit內(nèi)核)由谷歌Chrome團(tuán)隊(duì)開(kāi)發(fā)。團(tuán)隊(duì)表示將專注研發(fā)這個(gè)項(xiàng)目,未來(lái)也會(huì)不斷維護(hù)它。
這意味著對(duì)于網(wǎng)頁(yè)抓取和自動(dòng)化的需求,現(xiàn)在可以體會(huì)Chrome的速度和功能,因?yàn)樗邆涫澜缟鲜褂米疃嗟臑g覽器的特性:支持所有網(wǎng)站,支持JS引擎,還有偉大的開(kāi)發(fā)者工具API。太可怕啦!
選用哪個(gè)工具控制Headless Chrome?
市面上確實(shí)有很多NodeJS庫(kù)來(lái)支持Chrome新版headless模式,每一個(gè)都各有特色,我們自己的一款是NickJS。倘若沒(méi)有自己的抓取庫(kù),怎么敢輕易的說(shuō)自己是網(wǎng)頁(yè)抓取專家。
還有一套C++ API和社區(qū)用其他語(yǔ)言發(fā)布的庫(kù),比如說(shuō)基于GO語(yǔ)言。我們推薦使用NodeJS工具,因?yàn)樗途W(wǎng)頁(yè)解析語(yǔ)言一樣(下面你會(huì)看到它有多便利)。
網(wǎng)頁(yè)抓取?它不是非法的嗎?
我們無(wú)意挑起無(wú)休止的爭(zhēng)論,但不到兩周前,美國(guó)一名地方法官下令允許第三方抓取領(lǐng)英(LinkedIn)公眾檔案。目前為止這只是初步的法令,訴訟仍會(huì)繼續(xù)進(jìn)行,領(lǐng)英肯定會(huì)反對(duì),但盡管放心,我們會(huì)密切關(guān)注情況,因?yàn)檫@篇文章里談?wù)摿撕芏嚓P(guān)于領(lǐng)英的內(nèi)容。
無(wú)論如何作為一篇技術(shù)性的文章,我們不會(huì)深入探究特定的抓取操作的合法性問(wèn)題,我們應(yīng)該始終努力去尊重目標(biāo)網(wǎng)站的ToS。而對(duì)你在這篇文章中所學(xué)到的造成任何損害概不負(fù)責(zé)。
目前為止學(xué)到的很酷的東西
下面列出的一些技巧,我們每天幾乎都在使用。代碼示例采用NickJS抓取庫(kù),但它們很容易被其他Headless Chrome工具改寫,重要的是分享概念。
把cookies放回cookie jar
使用功能齊全的瀏覽器抓取會(huì)讓人安心,無(wú)需擔(dān)心CORS、會(huì)話、cookie、CSRF和其他web問(wèn)題。
但有時(shí)登錄表單變得非常強(qiáng)硬,唯一的解決方案是恢復(fù)以前保存的會(huì)話cookie。當(dāng)察覺(jué)故障時(shí),有些網(wǎng)站會(huì)發(fā)送電子郵件或短信。我們就沒(méi)有時(shí)間這么做,只是使用已設(shè)置好的會(huì)話cookie打開(kāi)頁(yè)面。
領(lǐng)英有一個(gè)很好的例子,設(shè)置li_atcookie能保證抓取機(jī)器訪問(wèn)他們的社交網(wǎng)絡(luò)(請(qǐng)記住:注意尊重目標(biāo)網(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)站不會(huì)用一個(gè)有效的會(huì)話cookie來(lái)阻止一個(gè)真實(shí)的瀏覽器訪問(wèn)。這么做相當(dāng)有風(fēng)險(xiǎn),因?yàn)殄e(cuò)誤的信息會(huì)引發(fā)憤怒用戶的大量支持請(qǐng)求。
jQuery不會(huì)讓你失望
我們學(xué)到了一件重要的事,那就是通過(guò)jQuery從網(wǎng)頁(yè)提取數(shù)據(jù)真是太容易了。現(xiàn)在回想起來(lái),這是顯而易見(jiàn)的。網(wǎng)站提供了一個(gè)高度結(jié)構(gòu)化的、可查詢的、包含數(shù)據(jù)元素的樹(shù)(它被稱為DOM),而jQuery是非常高效的DOM查詢庫(kù)。所以為什么不用它來(lái)抓取呢?這個(gè)技巧會(huì)屢試不爽。
很多網(wǎng)站都已經(jīng)使用了jQuery,所以只需在頁(yè)面中添加幾行就可以得到數(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)
})
印度、俄羅斯和巴基斯坦屏蔽機(jī)器人的做法有什么共同之處?
答案就是利用驗(yàn)證碼解決服務(wù)器驗(yàn)證。你可以幾美元買到上千個(gè)驗(yàn)證碼,通常產(chǎn)生驗(yàn)證碼不到30秒。但晚上的時(shí)候,因?yàn)闆](méi)有人,所以一般比較貴。
一個(gè)簡(jiǎn)單的谷歌搜索將提供多個(gè)api來(lái)解決任何類型的驗(yàn)證碼問(wèn)題,包括獲取谷歌最新的recaptcha驗(yàn)證碼(2美元1000個(gè))。
將抓取機(jī)器連接到這些服務(wù)就如發(fā)出HTTP請(qǐng)求一樣簡(jiǎn)單,現(xiàn)在機(jī)器人是人類了。
在我們的平臺(tái)上,用戶很容易解決他們需要的驗(yàn)證碼問(wèn)題。我們的巴斯特圖書館可以調(diào)用多個(gè)解決服務(wù)器驗(yàn)證:
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元素,而不是固定的時(shí)間
經(jīng)常看到抓取初學(xué)者讓他們的機(jī)器人在打開(kāi)一個(gè)頁(yè)面或點(diǎn)擊一個(gè)按鈕后等待5到10秒——他們想要確定他們所做的動(dòng)作有時(shí)間產(chǎn)生效果。
但這不是應(yīng)該做的。我們的3步理論適用于任何抓取場(chǎng)景:應(yīng)該等待的是想要操作的特定DOM元素。它更快、更清晰,如果出了問(wèn)題,會(huì)得到更準(zhǔn)確的錯(cuò)誤提示。
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)
糊弄過(guò)去。
MongoDB
我們發(fā)現(xiàn)MongoDB很適合大部分的抓取工作,它有一套優(yōu)秀的JS API和Mongoose ORM。考慮到當(dāng)使用Headless Chrome時(shí)已經(jīng)處于NodeJS環(huán)境中,為什么不采用它呢?
JSON-LD 和微數(shù)據(jù)開(kāi)發(fā)
有時(shí)網(wǎng)頁(yè)抓取并不需要理解DOM,而是要找到正確的“導(dǎo)出”按鈕。記住這一點(diǎn)可以節(jié)省了不少時(shí)間。
嚴(yán)謹(jǐn)?shù)恼f(shuō)有些網(wǎng)站會(huì)比其他網(wǎng)站容易一些,以Macys.com為例,他們所有的產(chǎn)品頁(yè)面都以JSON-LD形式的產(chǎn)品數(shù)據(jù)顯示在DOM中。可以說(shuō)到它們的任何一個(gè)產(chǎn)品頁(yè)面然后運(yùn)行。
JSON.parse(document 。 queryselector(“ # productSEOData ”)。 innertext)
將得到一個(gè)可以插入MongoDB很好的數(shù)據(jù)對(duì)象,沒(méi)有真正抓取的必要!
網(wǎng)絡(luò)請(qǐng)求攔截
因?yàn)槭褂玫氖荄evTools API,所以編寫的代碼具有使用Chrome的DevTools的等效功能。這意味著產(chǎn)生的機(jī)器人可以攔截、檢查甚至修改或中止任何網(wǎng)絡(luò)請(qǐng)求。
通過(guò)從LinkedIn下載PDF格式的簡(jiǎn)歷來(lái)測(cè)試網(wǎng)絡(luò)請(qǐng)求攔截。從配置文件中單擊“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è)置下載傳入文件的方式和路徑。我們還沒(méi)有測(cè)試它,但看起來(lái)很有前途!
廣告攔截
const nick = new Nick({
loadImages: false,
whitelist: [
/.*.aspx/,
/.*axd.*/,
/.*.html.*/,
/.*.js.*/
],
blacklist: [
/.*fsispin360.js/,
/.*fsitouchzoom.js/,
/.*.ashx.*/,
/.*google.*/
]
})
同樣可以通過(guò)屏蔽不必要的請(qǐng)求來(lái)加速抓取,分析、廣告和圖片是典型的屏蔽目標(biāo)。然而,謹(jǐn)記它會(huì)讓機(jī)器人變得不那么像人(例如,如果屏蔽了所有的圖片,領(lǐng)英就不會(huì)正確響應(yīng)頁(yè)面請(qǐng)求——不確定這是否是故意的)。
在NickJS中用戶可以指定一個(gè)白名單和一個(gè)包含正則表達(dá)式或字符串的黑名單。白名單特別強(qiáng)大,但如果不小心的話,很容易讓目標(biāo)網(wǎng)站崩潰。
DevTools協(xié)議也有Network.setBlockedURLs(),它使用帶有通配符的字符串?dāng)?shù)組作為輸入。
更重要的是,新版本的Chrome將帶有谷歌自帶的“廣告攔截器”——它更像是一個(gè)廣告“過(guò)濾器”。協(xié)議已經(jīng)有一個(gè)端點(diǎn)叫做Page.setAdBlockingEnabled()。
這就是我們說(shuō)的技巧!
Headless Chrome檢測(cè)
最近發(fā)表的一篇文章列舉了多種方法來(lái)檢測(cè)Headless Chrome訪問(wèn)者,也有可能檢測(cè)PhantomJS。那些方法描述了從基本的User-Agent字符串比較到更復(fù)雜的諸如觸發(fā)錯(cuò)誤和檢查堆棧跟蹤的技術(shù)。
在憤怒的管理員和巧妙的機(jī)器人制造商之間,這基本上是一個(gè)加大版的貓捉老鼠游戲。但從未見(jiàn)過(guò)這些方法在官方實(shí)施。在技術(shù)上是可以檢測(cè)到自動(dòng)訪問(wèn)者,但誰(shuí)會(huì)愿意面對(duì)潛在的錯(cuò)誤消息呢?對(duì)于大型的網(wǎng)站來(lái)說(shuō)尤其有風(fēng)險(xiǎn)。
如果你知道那些網(wǎng)站有這些檢測(cè)功能,請(qǐng)告訴我們!
結(jié)束語(yǔ)
抓取從來(lái)沒(méi)有這么容易過(guò),有了我們最新的工具和技術(shù),它甚至可以成為我們開(kāi)發(fā)人員愉快而有趣的活動(dòng)。
順便說(shuō)一下,我們從Franciskim.co“我不需要臭烘烘的API”文章中受到了啟發(fā),非常感謝!另外,關(guān)于了解怎樣開(kāi)始使用木偶的詳細(xì)說(shuō)明,請(qǐng)點(diǎn)擊這里。
在下一篇文章中,將寫到關(guān)于“bot mitigation”的工具,比如Distill Networks,講述HTTP代理和IP地址分配的美妙世界。
在Phantombuster.com上有我們的抓取和自動(dòng)化平臺(tái) NickJS.org庫(kù)。有興趣的話還可以了解我們的3個(gè)抓取步驟的理論信息
非常好我支持^.^
(0) 0%
不好我反對(duì)
(0) 0%