一日一技:在 Puppeteer 中如何攔截並修改網站的 JavaScript 代碼
攝影:產品經理
看起來很好喫啊
在我的爬蟲書中,講到了使用 Charles 或者 MitmProxy 實現中間人攻擊,從而繞過反爬蟲機制的方法。但這兩種方法都需要安裝根證書。
今天,我們來試一試在 Puppeteer 中,使用中間人攻擊,攻擊目標是我們自己,來繞過反爬蟲機制。
首先,我們用以下代碼訪問網站http://exercise.kingname.info/exercise_ajax_1.html
:
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
。然後修改代碼:
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');
})();
運行效果如下圖所示:
其中關鍵的代碼如下:
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
文件。然後修改裏面的代碼,繞過反爬蟲邏輯:
var a = 1;
var b = 2;
function antispider(){
console.log('開始檢測爬蟲');
console.log('開始收集瀏覽器指紋');
console.log('開始檢查是否是模擬瀏覽器');
if (a + b === 3) { // 是爬蟲!
return true;
}
}
is_spider = false; //這裏強制寫成 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 文件,然後攔截真正的請求並使用修改後的代碼替換:
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
不僅可以修改返回的內容,還可以修改網站的請求。更多強大功能,大家可以閱讀它的官方文檔 [1]。
參考文獻
[1] 官方文檔: https://www.npmjs.com/package/puppeteer-interceptor
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/kXZEcxYazHFM46EnHVX-DQ