无头浏览器puppeteer的检测攻防
1. 引言
许多爬虫初学者在接触到无头浏览器的时候都会有一种如获至宝的感觉,仿佛看到了爬虫的终极解决方案。无论是所有爬虫教程中都会出现的PhantomJS、Selenium,亦或是相对冷门的Nightmare,到后来居上的Puppeteer,都能够作为爬虫工程师的利刃,撕开反爬的一道道屏障。无头浏览器难道就是爬虫的终点了吗?那必然不是,否则各位爬虫工程师就只值3000块一个月了。
首先,无论多强大多轻便的无头浏览器,在同等配置的机器上,并发永远不可能高过python的一行request请求。在大规模数据采集中,服务器成本是必须考虑的问题,采集同样规模的数据,人家服务器成本花了1万块,你给霍霍了十几万,你猜老板会不会问候你老豆。其次,用无头浏览器写过爬虫的人应该都会觉得,很难靠headless browser搞出来一个复杂的、长期稳定的、可靠的大型爬虫,它们更适合应用在一些小规模的数据采集场合。最后,也是最重要的,无头浏览器并不是无敌的,反爬的一方不会乖乖束手就擒,你有张良计,他自然就有过强梯,反爬一方会通过某些方法检测出无头浏览器,然后把这些请求全部处理掉,某些网站你使用无头浏览器甚至无法打开首页。
上段说的最后一点,也就是针对无头浏览器的反爬攻防,就是本文所要讨论的内容。PhantomJS和Selenium已经日薄西山,本文只研究后来居上的Puppeteer。
2. 从蛛丝马迹中认出Puppeteer
2.1 webdriver
介绍
webdriver可以说是Puppeteer最明显的一个特征,检测也非常简单,获取navigator.webdriver
这一属性,在默认启动的Puppeteer中,它的值为true,而在正常浏览器中,navigator
里是没有这一属性的,是undefined。
矛
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, 'webdriver', {
get: () => false,
});
});
简单解释一下这段代码,在新建页面之前,将webdriver的get方法强制返回false。那么类似于if (navigator.webdriver)
这样的检测就不会生效了。
盾
var attr = window.navigator, result = [];
do {
Object.getOwnPropertyNames(attr).forEach(function(a) {
result.push(a)
})
} while (attr=Object.getPrototypeOf(attr));
这段代码中,获取了navigator中所有属性名,而非属性值,也就是说,即便你把webdriver的值改为false了,这个属性仍然是在的。但是,在正常使用的chrome中,navigator是没有这一属性的,一旦检测到webdriver这个属性名,大概率可以判定为puppeteer。
破盾
破盾就不能针对puppeteer下手了,反正我是没有办法在检测前delete掉navigator.webdriver这个属性。 在发现这段盾的代码后,给它后面注入一点:
result = result.filter(function(item) {
return item != "webdriver"
});
2.2 UserAgent
介绍
UA在反爬界的地位,相当于hello world在编程界的地位,入门第一课,就会教UA的检测。所以再垃圾的爬虫,也知道给自己伪造一个UA,puppeteer的UA也是如此。只要对puppeteer反爬稍有研究,就会知道,默认情况下,puppeteer的UA有HeadlessChrome这一关键词,非常容易检测。
矛
这个矛简单的我都不想写,一行代码搞定。
await page.setUserAgent("随便写个UA");
盾
从上面可以看到,修改UA一行代码就搞定了,而且没什么门槛,可以说人人都知道这个事情,所以这里要检测,肯定不能检测HeadlessChrome关键词这么简单。
我在windows和linux下的puppeteer分别获取了一些属性:
- windows中的
navigator.userAgent
: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3477.0 Safari/537.36 - windows中的
navigator.platform
: “Win32” - linux中的
navigator.userAgent
: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3477.0 Safari/537.36 - linux中的
navigator.platform
: “Linux x86_64” 由于手动设置了UA,所以在windows和linux下的UA是一致的,都是我设的值,但在platform这一属性漏出了马脚。UA中明明写着windows,platform却说你是linux,这岂不是自相矛盾?一经发现,肯定是要把这个请求打入冷宫的。
我相信大多数程序员都会选择把爬虫部署在linux服务器上,windows服务器真是谁用谁知道。。。这里就不吐槽它了。而且根据我实践经验,puppeteer在linux上运行的远比在windows上稳定。根据上述两点,得出结论,对比检查UA和platform,能取得一些效果。
破盾
举一隅不以三隅反,则不复也。