JS `Headless Browser` 反检测:模拟真实用户环境与指纹伪造

各位听众,早上好/下午好/晚上好! 很高兴今天能和大家聊聊一个挺有意思,也挺有挑战的话题:JS Headless Browser 的反检测。说白了,就是咱们用程序模拟人去浏览网页,但是有些网站它贼精,能一眼看穿你是不是机器人,然后把你拒之门外。咱们要做的,就是想办法把自己伪装得像个真人,让它们没法轻易识破。

一、 什么是 Headless Browser?为什么会被检测?

首先,简单解释下 Headless Browser。它就是一个没有界面的浏览器。你可以把它想象成一个在后台默默运行的浏览器内核,它可以执行 JavaScript,渲染网页,获取数据,等等,但你看不到它的窗口。 常用的 Headless Browser 包括:

  • Puppeteer (Chrome/Chromium): 由 Google 维护,功能强大,社区活跃。
  • Playwright (Chromium, Firefox, WebKit): 由 Microsoft 维护,支持多种浏览器内核。
  • Selenium (多种浏览器): 历史悠久,应用广泛。

这些工具之所以会被检测,是因为它们的一些默认行为和属性与真实浏览器存在差异。网站的反爬虫机制通常会检查以下几个方面:

  1. User-Agent: 这是浏览器告诉服务器自己身份的标识。 Headless Browser 的默认 User-Agent 通常包含 "HeadlessChrome" 或类似字样,很容易被识别。

  2. WebDriver 检测: 网站会检查是否存在 window.navigator.webdriver 属性。 如果这个属性为 true,就说明你很可能是在使用 Selenium 或类似工具。

  3. JavaScript 特性检测: Headless Browser 在某些 JavaScript 特性的实现上可能与真实浏览器存在差异。 比如, navigator.pluginsnavigator.languages 等属性的内容可能不一致。

  4. 行为模式分析: 网站会分析用户的行为模式,比如鼠标移动轨迹、键盘输入速度等。 如果你的行为过于规律或者过于快速,就可能被识别为机器人。

  5. IP 地址和请求频率: 如果你的 IP 地址在短时间内发送大量请求,或者 IP 地址来自已知的数据中心,也可能被封禁。

二、 User-Agent 伪造:第一层伪装

User-Agent 是最容易被检测的,也是最容易伪造的。 我们可以从网上找到各种真实浏览器的 User-Agent,然后设置到 Headless Browser 中。

Puppeteer 示例:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  // 随机选择一个 User-Agent
  const userAgents = [
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15',
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0'
  ];
  const randomUserAgent = userAgents[Math.floor(Math.random() * userAgents.length)];

  await page.setUserAgent(randomUserAgent);

  await page.goto('https://www.example.com'); // 替换成你要访问的网站

  await browser.close();
})();

Playwright 示例:

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const context = await browser.newContext({
    userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
  });
  const page = await context.newPage();
  await page.goto('https://www.example.com'); // 替换成你要访问的网站
  await browser.close();
})();

提示:

  • User-Agent 要定期更新,因为浏览器的版本在不断升级。
  • 尽量使用常见的 User-Agent,不要使用过于特殊的 User-Agent。
  • 可以根据目标网站的访问者画像,选择最合适的 User-Agent。

三、 移除 WebDriver 属性:更深一层的伪装

仅仅伪造 User-Agent 还不够,有些网站还会检查 window.navigator.webdriver 属性。 我们可以通过 JavaScript 来移除这个属性。

Puppeteer 示例:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  // 移除 navigator.webdriver 属性
  await page.evaluateOnNewDocument(() => {
    Object.defineProperty(navigator, 'webdriver', {
      get: () => false,
    });
  });

  await page.goto('https://www.example.com'); // 替换成你要访问的网站

  await browser.close();
})();

Playwright 示例:

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const context = await browser.newContext();
  const page = await context.newPage();

  // 移除 navigator.webdriver 属性
  await page.addInitScript(() => {
    Object.defineProperty(navigator, 'webdriver', {
      get: () => false,
    });
  });

  await page.goto('https://www.example.com'); // 替换成你要访问的网站
  await browser.close();
})();

解释:

  • evaluateOnNewDocument (Puppeteer) 和 addInitScript (Playwright) 的作用是在页面加载之前执行 JavaScript 代码。
  • Object.defineProperty 方法可以用来修改或删除对象的属性。
  • 我们将 navigator.webdriver 属性的 getter 函数设置为返回 false,这样网站就无法检测到 WebDriver 了。

四、 指纹伪造:终极伪装

User-Agent 和 WebDriver 属性只是冰山一角。 网站还可以通过其他方式来识别 Headless Browser,比如:

  • Canvas 指纹: 不同的浏览器在渲染 Canvas 元素时,可能会产生细微的差异。 网站可以利用这些差异来识别浏览器。
  • WebGL 指纹: 类似于 Canvas 指纹,WebGL 也会受到硬件和驱动程序的影响,产生差异。
  • 字体指纹: 不同的操作系统和浏览器安装的字体可能不同。

要彻底伪装,我们需要模拟这些指纹。 这比较复杂,需要用到一些专门的库。

1. headless-chrome-fingerprint:

这是一个 NodeJS 库,可以用来生成各种浏览器指纹。

npm install headless-chrome-fingerprint

示例:

const puppeteer = require('puppeteer');
const { HeadlessChrome } = require('headless-chrome-fingerprint');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  const fingerprint = HeadlessChrome.generateFingerprint();

  await page.evaluateOnNewDocument((fingerprint) => {
    // 修改 navigator 对象
    Object.defineProperties(navigator, {
      userAgent: { get: () => fingerprint.userAgent },
      platform: { get: () => fingerprint.platform },
    });

    // 修改 WebGL
    const getParameter = WebGLRenderingContext.prototype.getParameter;
    WebGLRenderingContext.prototype.getParameter = function(parameter) {
      if (parameter === 37445) {
        return 'Google Inc.';
      }
      if (parameter === 37446) {
        return 'ANGLE (NVIDIA GeForce GTX 970 Direct3D11 vs_5_0 ps_5_0)';
      }
      return getParameter.call(this, parameter);
    };
  }, fingerprint);

  await page.goto('https://www.example.com'); // 替换成你要访问的网站

  await browser.close();
})();

2. 使用 Stealth 插件:

Puppeteer 和 Playwright 都有一些 Stealth 插件,可以自动处理各种反检测措施。

Puppeteer Stealth:

npm install puppeteer-extra puppeteer-extra-plugin-stealth
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());

(async () => {
  const browser = await puppeteer.launch({ headless: true }); // 设置 headless: true
  const page = await browser.newPage();
  await page.goto('https://www.example.com'); // 替换成你要访问的网站
  await browser.close();
})();

Playwright Stealth: Playwright 本身的反爬能力更强,通常不需要额外的插件,但也可以结合一些技巧来增强。

五、 行为模拟:让机器人更像人

即使伪造了各种指纹,如果你的行为过于规律,仍然会被识别为机器人。 因此,我们需要模拟真实用户的行为。

  1. 随机延迟: 在执行操作之间添加随机延迟,模拟人的思考时间。
// 模拟随机延迟
function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// 示例
await page.type('#username', 'myusername');
await delay(Math.random() * 1000 + 500); // 延迟 500-1500 毫秒
await page.type('#password', 'mypassword');
  1. 鼠标移动轨迹模拟: 模拟真实的鼠标移动轨迹,不要直接跳转到目标位置。 可以使用一些库来生成平滑的鼠标移动轨迹。
    一种简单的实现方式是使用贝塞尔曲线:
async function simulateMouseMove(page, targetX, targetY, duration = 200) {
    const startX = await page.evaluate(() => window.screenX + (window.outerWidth - window.innerWidth) / 2 + window.innerWidth / 2);
    const startY = await page.evaluate(() => window.screenY + (window.outerHeight - window.innerHeight) + window.innerHeight / 2);
    // 控制点距离起始点或终点距离
    const controlPointDistance = Math.min(Math.abs(targetX - startX), Math.abs(targetY - startY)) / 2;
    // 随机计算控制点
    const controlX1 = startX + (targetX > startX ? controlPointDistance : -controlPointDistance) + (Math.random() - 0.5) * 50;
    const controlY1 = startY + (targetY > startY ? controlPointDistance : -controlPointDistance) + (Math.random() - 0.5) * 50;
    const controlX2 = targetX + (startX > targetX ? controlPointDistance : -controlPointDistance) + (Math.random() - 0.5) * 50;
    const controlY2 = targetY + (startY > targetY ? controlPointDistance : -controlPointDistance) + (Math.random() - 0.5) * 50;

    const steps = 20;
    for (let i = 1; i <= steps; i++) {
        const t = i / steps;
        const x = (1 - t) * (1 - t) * (1 - t) * startX + 3 * (1 - t) * (1 - t) * t * controlX1 + 3 * (1 - t) * t * t * controlX2 + t * t * t * targetX;
        const y = (1 - t) * (1 - t) * (1 - t) * startY + 3 * (1 - t) * (1 - t) * t * controlY1 + 3 * (1 - t) * t * t * controlY2 + t * t * t * targetY;
        await page.mouse.move(x, y);
        await new Promise(resolve => setTimeout(resolve, duration / steps));
    }
}

// 示例
const element = await page.$('#my-button');
const box = await element.boundingBox();
const targetX = box.x + box.width / 2;
const targetY = box.y + box.height / 2;

await simulateMouseMove(page, targetX, targetY);
await page.click('#my-button');
  1. 键盘输入模拟: 模拟真实的键盘输入速度和停顿。

  2. 滚动页面: 模拟用户滚动页面的行为。

  3. 随机访问页面: 不要只访问目标页面,可以先访问一些其他页面,模拟用户的浏览习惯。

六、 IP 代理:最后的防线

即使你伪造了各种指纹,模拟了各种行为,如果你的 IP 地址被封禁,一切都白费了。 因此,使用 IP 代理是必不可少的。

  1. 免费代理: 可以从网上找到一些免费代理,但是质量通常不高,速度慢,不稳定,而且容易被封禁。

  2. 付费代理: 付费代理通常质量更高,速度更快,更稳定,而且不容易被封禁。 常用的付费代理包括:

    • 住宅代理: IP 地址来自真实用户的家庭网络,不容易被识别为代理。
    • 数据中心代理: IP 地址来自数据中心,容易被识别为代理,但是速度更快。

Puppeteer 和 Playwright 都可以很方便地设置代理:

Puppeteer 示例:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({
    args: ['--proxy-server=http://your_proxy_address:your_proxy_port'],
  });
  const page = await browser.newPage();
  await page.goto('https://www.example.com'); // 替换成你要访问的网站
  await browser.close();
})();

Playwright 示例:

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch({
    proxy: {
      server: 'http://your_proxy_address:your_proxy_port',
    },
  });
  const context = await browser.newContext();
  const page = await context.newPage();
  await page.goto('https://www.example.com'); // 替换成你要访问的网站
  await browser.close();
})();

提示:

  • 使用代理池,定期更换 IP 地址。
  • 选择与目标网站地理位置相近的代理服务器,可以提高访问速度。
  • 验证代理服务器是否可用,可以使用 curlwget 命令。

七、 总结与展望

反检测是一个持续对抗的过程。 网站的反爬虫技术在不断升级,我们的反反爬虫技术也要不断进步。 没有一劳永逸的解决方案。

技巧 优点 缺点
User-Agent 伪造 简单易用 容易被识别,只能作为第一层伪装
WebDriver 属性移除 较为有效,可以避免被 Selenium 等工具直接识别 有些网站会使用其他方式来检测 WebDriver
指纹伪造 可以模拟真实的浏览器指纹,提高伪装度 比较复杂,需要使用专门的库
行为模拟 可以模拟真实用户的行为,避免被识别为机器人 需要花费更多的时间和精力
IP 代理 可以避免 IP 地址被封禁 需要购买代理服务器,成本较高

未来,反检测技术可能会更加智能化,比如使用机器学习来分析用户的行为模式。 我们也需要不断学习新的技术,才能更好地应对反检测的挑战。 例如,可以研究如何利用 GAN (Generative Adversarial Network) 来生成更真实的浏览器指纹。

最后,强调一点: 请遵守法律法规和网站的使用协议,不要进行恶意爬虫行为。 咱们的目标是学习技术,而不是破坏网络秩序。

希望今天的分享对大家有所帮助! 如果大家有什么问题,欢迎提问。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注