一日一技:在 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');
})();

運行效果如下圖所示:

從圖中可以看到,我們成功繞過了反爬蟲的邏輯,獲得了真正的頁面數據。

這裏有兩個地方需要注意:

  1. 要攔截哪個請求,對應到的是intercept函數的第二個參數。這個參數的值是patterns.XXX(地址通配符)。其中的 XXX 可以是如下幾個關鍵詞:Document, Stylesheet, Image, Media, Font, Script, TextTrack, XHR, Fetch, EventSource, WebSocket, Manifest, SignedExchange, Ping, CSPViolationReport, Preflight, Other。地址通配符注意是通配符不是正則表達式。通配符裏面,*表示多個字符,?表示一個字符。

  2. puppeteer-interceptor對中文的支持不太好。攔截到請求返回的數據以後,如果要修改文本,儘量修改成英文的。否則可能會報錯。

puppeteer-interceptor不僅可以修改返回的內容,還可以修改網站的請求。更多強大功能,大家可以閱讀它的官方文檔 [1]。

參考文獻

[1] 官方文檔: https://www.npmjs.com/package/puppeteer-interceptor

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/kXZEcxYazHFM46EnHVX-DQ