在我的爬虫书中,讲到了使用 Charles 或者 MitmProxy 实现中间人攻击,从而绕过反爬虫机制的方法。但这两种方法都需要安装根证书。
今天,我们来试一试在 Puppeteer 中,使用中间人攻击,攻击目标是我们自己,来绕过反爬虫机制。
首先,我们用以下代码访问网站http://exercise.kingname.info/exercise_ajax_1.html
:
1 2 3 4 5 6 7 8 9 10 11
| const puppeteer = require('puppeteer-core');
(async () => { const browser = await puppeteer.launch({ executablePath: '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge', headless:false, });
const [page] = await browser.pages() await page.goto('http://exercise.kingname.info/exercise_ajax_1.html'); })();
|
运行效果如下图所示:
现在,我想拦截网站返回的数据,并篡改它。首先我们打开 Chrome 的开发者工具,看看这个页面有哪些 Ajax 请求:
红框框住的这个 Ajax 请求,返回了网页上面的文字。这个请求对应的地址是:http://exercise.kingname.info/ajax_1_backend
,如下图所示:
现在,我们就来尝试篡改这个请求的返回数据。首先使用npm
安装一个包:npm install puppeteer-interceptor
。然后修改代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const puppeteer = require('puppeteer-core'); const { intercept, patterns } = require('puppeteer-interceptor');
(async () => { const browser = await puppeteer.launch({ executablePath: '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge', headless:false, });
const [page] = await browser.pages();
intercept(page, patterns.XHR('*ajax_1_backend'), { onResponseReceived: event => { console.log(`${event.request.url} intercepted, going to modify`) var content = `You are hacked by me`; event.response.body = content; return event.response; } }); await page.goto('http://exercise.kingname.info/exercise_ajax_1.html'); })();
|
运行效果如下图所示:
其中关键的代码如下:
1 2 3 4 5 6 7 8
| intercept(page, patterns.XHR('*ajax_1_backend'), { onResponseReceived: event => { console.log(`${event.request.url} intercepted, going to modify`) var content = `You are hacked by me`; event.response.body = content; return event.response; } });
|
这一段代码指定,要修改一个 XHR 请求的返回。这个 XHR 请求的 URL 是以ajax_1_backend
结尾的。所谓的 XHR 请求,全称是XMLHttpRequest
,大家可以把它近似看做 Ajax 请求。
当检测到满足这个通配符的请求时,无论它的内容是什么,都改写成You are hacked by me
,然后返回给浏览器。
有人可能会问,你这样修改,简单是简单,但它有什么用呢?它的用处非常大,比如你在做爬虫的时候,把网站的 JavaScript 的一部分代码替换了,这样就能绕过反爬虫检测。
我做了一个示例的页面来说明。这个页面直接访问,如下图所示:
使用开发者工具,我们可以看到核心的反爬虫逻辑在http://127.0.0.1:8000/backend.js
这个 js 文件中,如下图所示:
这个演示的例子中,这个反爬虫函数非常简单。但在真实的场景中,它的逻辑非常复杂。然而,逻辑再复杂,也有个调用入口。简单分析这个JavaScript 代码,我们可以知道,只需要把代码第14行注释掉,强制设置is_spider = false
,就可以绕过这个反爬虫逻辑了。
为了绕过反爬虫逻辑,首先,我们把网站的这个JavaScript 代码复制下来,保存成safe.js
文件。然后修改里面的代码,绕过反爬虫逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| var a = 1; var b = 2;
function antispider(){ console.log('开始检测爬虫'); console.log('开始收集浏览器指纹'); console.log('开始检查是否是模拟浏览器'); if (a + b === 3) { return true; } }
is_spider = false; if(is_spider) { alert('你是爬虫!'); } else { document.getElementById("content").innerHTML = "In America, leave airplane, inner People, related to the benefit, know everything, know nothing, said nothing, above."; }
|
接下来,修改 Puppeteer 的代码,从本地读取这个修改后的 js 文件,然后拦截真正的请求并使用修改后的代码替换:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const puppeteer = require('puppeteer-core'); const fs = require('fs'); const { intercept, patterns } = require('puppeteer-interceptor');
(async () => { const safe_code = fs.readFileSync('./safe.js', 'utf8'); const browser = await puppeteer.launch({ executablePath: '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge', headless:false, });
const [page] = await browser.pages();
intercept(page, patterns.Script('*backend.js'), { onResponseReceived: event => { console.log(`${event.request.url} intercepted, going to modify`) event.response.body = safe_code; return event.response; } }); await page.goto('http://127.0.0.1:8000'); })();
|
运行效果如下图所示:
从图中可以看到,我们成功绕过了反爬虫的逻辑,获得了真正的页面数据。
这里有两个地方需要注意:
- 要拦截哪个请求,对应到的是
intercept
函数的第二个参数。这个参数的值是patterns.XXX(地址通配符)
。其中的 XXX 可以是如下几个关键词:Document, Stylesheet, Image, Media, Font, Script, TextTrack, XHR, Fetch, EventSource, WebSocket, Manifest, SignedExchange, Ping, CSPViolationReport, Preflight, Other
。地址通配符注意是通配符
不是正则表达式。通配符里面,*
表示多个字符,?
表示一个字符。
puppeteer-interceptor
对中文的支持不太好。拦截到请求返回的数据以后,如果要修改文本,尽量修改成英文的。否则可能会报错。
puppeteer-interceptor
不仅可以修改返回的内容,还可以修改网站的请求。更多强大功能,大家可以阅读它的官方文档。