2017年网页抓取:先进的Headless Chrome技巧

2023-10-13 04:40

本文主要是介绍2017年网页抓取:先进的Headless Chrome技巧,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原文:Web Scraping in 2017: Advanced Headless Chrome Tips & Tricks
作者: Martin Tapia
翻译:不二

Headless Chrome是Chrome 浏览器的无界面形态,可以在不打开浏览器的前提下,使用所有 Chrome 支持的特性运行程序。相比于现代浏览器,Headless Chrome 更加方便测试web应用,获得网站的截图,做爬虫抓取信息等,也更加贴近浏览器环境。下面看看作者分享的使用Headless Chrome进行网页抓取的经验。

PhantomJS的研发已经停止,而Headless Chrome成了热门关注的焦点,大家都很喜欢它,包括我们。在Phantombuster公司,网页抓取是我们工作的很大一部分,现在我们广泛使用Headless Chrome。

这篇文章,将告诉你如何快速入门Headless Chrome生态系统,并展示从已经抓取数百万网页中学到的经验。

文章摘要:
1. 有很多库可以控制Chrome,可以根据自己的喜欢选择。
2. 使用Headless Chrome进行网页抓取非常简单,掌握下面的技巧之后更是如此。
3. Headless浏览器访客可以被检测到,但没人这么做。

Headless Chrome简述

Headless Chrome基于PhantomJS(QtWebKit内核)由谷歌Chrome团队开发。团队表示将专注研发这个项目,未来也会不断维护它。

这意味着对于网页抓取和自动化的需求,现在可以体会Chrome的速度和功能,因为它具备世界上使用最多的浏览器的特性:支持所有网站,支持JS引擎,还有伟大的开发者工具API。太可怕啦!

选用哪个工具控制Headless Chrome?


image

市面上确实有很多NodeJS库来支持Chrome新版headless模式,每一个都各有特色,我们自己的一款是NickJS。倘若没有自己的抓取库,怎么敢轻易的说自己是网页抓取专家。

还有一套C++ API和社区用其他语言发布的库,比如说基于GO语言。我们推荐使用NodeJS工具,因为它和网页解析语言一样(下面你会看到它有多便利)。

网页抓取?它不是非法的吗?

我们无意挑起无休止的争论,但不到两周前,美国一名地方法官下令允许第三方抓取领英(LinkedIn)公众档案。目前为止这只是初步的法令,诉讼仍会继续进行,领英肯定会反对,但尽管放心,我们会密切关注情况,因为这篇文章里谈论了很多关于领英的内容。

无论如何作为一篇技术性的文章,我们不会深入探究特定的抓取操作的合法性问题,我们应该始终努力去尊重目标网站的ToS。而对你在这篇文章中所学到的造成任何损害概不负责。

目前为止学到的很酷的东西

下面列出的一些技巧,我们每天几乎都在使用。代码示例采用NickJS抓取库,但它们很容易被其他Headless Chrome工具改写,重要的是分享概念。

把cookies放回cookie jar

使用功能齐全的浏览器抓取会让人安心,无需担心CORS、会话、cookie、CSRF和其他web问题。

但有时登录表单变得非常强硬,唯一的解决方案是恢复以前保存的会话cookie。当察觉故障时,有些网站会发送电子邮件或短信。我们就没有时间这么做,只是使用已设置好的会话cookie打开页面。

领英有一个很好的例子,设置li_atcookie能保证抓取机器访问他们的社交网络(请记住:注意尊重目标网站Tos)。

await nick.setCookie({name: "li_at",value: "a session cookie value copied from your DevTools",domain: "www.linkedin.com"
})

相信像领英这样的网站不会用一个有效的会话cookie来阻止一个真实的浏览器访问。这么做相当有风险,因为错误的信息会引发愤怒用户的大量支持请求。

jQuery不会让你失望

我们学到了一件重要的事,那就是通过jQuery从网页提取数据真是太容易了。现在回想起来,这是显而易见的。网站提供了一个高度结构化的、可查询的、包含数据元素的树(它被称为DOM),而jQuery是非常高效的DOM查询库。所以为什么不用它来抓取呢?这个技巧会屡试不爽。

很多网站都已经使用了jQuery,所以只需在页面中添加几行就可以得到数据。

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 toolconst data = []$(".athing").each((index, element) => {data.push({title: $(element).find(".storylink").text(),url: $(element).find(".storylink").attr("href")})})callback(null, data)
})
印度、俄罗斯和巴基斯坦屏蔽机器人的做法有什么共同之处?


image

答案就是利用验证码解决服务器验证。你可以几美元买到上千个验证码,通常产生验证码不到30秒。但晚上的时候,因为没有人,所以一般比较贵。

一个简单的谷歌搜索将提供多个api来解决任何类型的验证码问题,包括获取谷歌最新的recaptcha验证码(2美元1000个)。

将抓取机器连接到这些服务就如发出HTTP请求一样简单,现在机器人是人类了。

在我们的平台上,用户很容易解决他们需要的验证码问题。我们的巴斯特图书馆可以调用多个解决服务器验证:

if (await tab.isVisible(".captchaImage")) {// Get the URL of the generated CAPTCHA image// Note that we could also get its base64-encoded value and solve it tooconst captchaImageLink = await tab.evaluate((arg, callback) => {callback(null, $(".captchaImage").attr("src"))})// Make a call to a CAPTCHA solving serviceconst captchaAnswer = await buster.solveCaptchaImage(captchaImageLink)// Fill the form with our solutionawait tab.fill(".captchaForm", { "captcha-answer": captchaAnswer }, { submit: true })
}
等待的是DOM元素,而不是固定的时间

经常看到抓取初学者让他们的机器人在打开一个页面或点击一个按钮后等待5到10秒——他们想要确定他们所做的动作有时间产生效果。

但这不是应该做的。我们的3步理论适用于任何抓取场景:应该等待的是想要操作的特定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

我们发现MongoDB很适合大部分的抓取工作,它有一套优秀的JS API和Mongoose ORM。考虑到当使用Headless Chrome时已经处于NodeJS环境中,为什么不采用它呢?

JSON-LD 和微数据开发

有时网页抓取并不需要理解DOM,而是要找到正确的“导出”按钮。记住这一点可以节省了不少时间。

严谨的说有些网站会比其他网站容易一些,以Macys.com为例,他们所有的产品页面都以JSON-LD形式的产品数据显示在DOM中。可以说到它们的任何一个产品页面然后运行

JSON.parse(document . queryselector(" # productSEOData "). innertext)

将得到一个可以插入MongoDB很好的数据对象,没有真正抓取的必要!

网络请求拦截


image

因为使用的是DevTools API,所以编写的代码具有使用Chrome的DevTools的等效功能。这意味着产生的机器人可以拦截、检查甚至修改或中止任何网络请求。

通过从LinkedIn下载PDF格式的简历来测试网络请求拦截。从配置文件中单击“Save to PDF”按钮触发XHR,其中响应内容为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("linkedin-cv.pdf", Buffer.from(cv.body, (cv.base64Encoded ? 'base64' : 'utf8')))})}
})

值得一提的是DevTools协议正在迅速发展,现在有一种方法可以使用Page.setDownloadBehavior()设置下载传入文件的方式和路径。我们还没有测试它,但看起来很有前途!

广告拦截
const nick = new Nick({loadImages: false,whitelist: [/.*\.aspx/,/.*axd.*/,/.*\.html.*/,/.*\.js.*/],blacklist: [/.*fsispin360\.js/,/.*fsitouchzoom\.js/,/.*\.ashx.*/,/.*google.*/]
})

同样可以通过屏蔽不必要的请求来加速抓取,分析、广告和图片是典型的屏蔽目标。然而,谨记它会让机器人变得不那么像人(例如,如果屏蔽了所有的图片,领英就不会正确响应页面请求——不确定这是否是故意的)。

在NickJS中用户可以指定一个白名单和一个包含正则表达式或字符串的黑名单。白名单特别强大,但如果不小心的话,很容易让目标网站崩溃。

DevTools协议也有Network.setBlockedURLs(),它使用带有通配符的字符串数组作为输入。

更重要的是,新版本的Chrome将带有谷歌自带的“广告拦截器”——它更像是一个广告“过滤器”。协议已经有一个端点叫做Page.setAdBlockingEnabled()。

这就是我们说的技巧!

Headless Chrome检测

最近发表的一篇文章列举了多种方法来检测Headless Chrome访问者,也有可能检测PhantomJS。那些方法描述了从基本的User-Agent字符串比较到更复杂的诸如触发错误和检查堆栈跟踪的技术。

在愤怒的管理员和巧妙的机器人制造商之间,这基本上是一个加大版的猫捉老鼠游戏。但从未见过这些方法在官方实施。在技术上是可以检测到自动访问者,但谁会愿意面对潜在的错误消息呢?对于大型的网站来说尤其有风险。

如果你知道那些网站有这些检测功能,请告诉我们!

结束语

抓取从来没有这么容易过,有了我们最新的工具和技术,它甚至可以成为我们开发人员愉快而有趣的活动。

顺便说一下,我们从Franciskim.co“我不需要臭烘烘的API”文章中受到了启发,非常感谢!另外,关于了解怎样开始使用木偶的详细说明,请点击这里。

在下一篇文章中,将写到关于“bot mitigation”的工具,比如Distill Networks,讲述HTTP代理和IP地址分配的美妙世界。

在Phantombuster.com上有我们的抓取和自动化平台 NickJS.org库。有兴趣的话还可以了解我们的3个抓取步骤的理论信息。

这篇关于2017年网页抓取:先进的Headless Chrome技巧的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/200835

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

购买磨轮平衡机时应该注意什么问题和技巧

在购买磨轮平衡机时,您应该注意以下几个关键点: 平衡精度 平衡精度是衡量平衡机性能的核心指标,直接影响到不平衡量的检测与校准的准确性,从而决定磨轮的振动和噪声水平。高精度的平衡机能显著减少振动和噪声,提高磨削加工的精度。 转速范围 宽广的转速范围意味着平衡机能够处理更多种类的磨轮,适应不同的工作条件和规格要求。 振动监测能力 振动监测能力是评估平衡机性能的重要因素。通过传感器实时监

滚雪球学Java(87):Java事务处理:JDBC的ACID属性与实战技巧!真有两下子!

咦咦咦,各位小可爱,我是你们的好伙伴——bug菌,今天又来给大家普及Java SE啦,别躲起来啊,听我讲干货还不快点赞,赞多了我就有动力讲得更嗨啦!所以呀,养成先点赞后阅读的好习惯,别被干货淹没了哦~ 🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎大家关注&&收藏!持续更新中,up!up!up!! 环境说明:Windows 10

EasyPlayer.js网页H5 Web js播放器能力合集

最近遇到一个需求,要求做一款播放器,发现能力上跟EasyPlayer.js基本一致,满足要求: 需求 功性能 分类 需求描述 功能 预览 分屏模式 单分屏(单屏/全屏) 多分屏(2*2) 多分屏(3*3) 多分屏(4*4) 播放控制 播放(单个或全部) 暂停(暂停时展示最后一帧画面) 停止(单个或全部) 声音控制(开关/音量调节) 主辅码流切换 辅助功能 屏

禁止复制的网页怎么复制

禁止复制的网页怎么复制 文章目录 禁止复制的网页怎么复制前言准备工作操作步骤一、在浏览器菜单中找到“开发者工具”二、点击“检查元素(inspect element)”按钮三、在网页中选取需要的片段,锁定对应的元素四、复制被选中的元素五、粘贴到记事本,以`.html`为后缀命名六、打开`xxx.html`,优雅地复制 前言 在浏览网页的时候,有的网页内容无法复制。比如「360

zeroclipboard 粘贴板的应用示例, 兼容 Chrome、IE等多浏览器

zeroclipboard单个复制按钮和多个复制按钮的实现方法 最近网站改版想让复制代码功能在多个浏览器上都可以实现,最近看网上不少说我们的代码复制功能不好用的,我们最近将会增加代码高亮等功能,希望大家多多支持我们 zeroclipboard是一个跨浏览器的库类 它利用 Flash 进行复制,所以只要浏览器装有 Flash 就可以运行,而且比 IE 的

火语言RPA流程组件介绍--浏览网页

🚩【组件功能】:浏览器打开指定网址或本地html文件 配置预览 配置说明 网址URL 支持T或# 默认FLOW输入项 输入需要打开的网址URL 超时时间 支持T或# 打开网页超时时间 执行后后等待时间(ms) 支持T或# 当前组件执行完成后继续等待的时间 UserAgent 支持T或# User Agent中文名为用户代理,简称 UA,它是一个特殊字符串头,使得服务器

小技巧绕过Sina Visitor System(新浪访客系统)

0x00 前言 一直以来,爬虫与反爬虫技术都时刻进行着博弈,而新浪微博作为一个数据大户更是在反爬虫上不遗余力。常规手段如验证码、封IP等等相信很多人都见识过…… 当然确实有需要的话可以通过新浪开放平台提供的API进行数据采集,但是普通开发者的权限比较低,限制也比较多。所以如果只是做一些简单的功能还是爬虫比较方便~ 应该是今年的早些时候,新浪引入了一个Sina Visitor Syst

PHP抓取网站图片脚本

方法一: <?phpheader("Content-type:image/jpeg"); class download_image{function read_url($str) { $file=fopen($str,"r");$result = ''; while(!feof($file)) { $result.=fgets($file,9999); } fclose($file); re