各位听众,早上好/下午好/晚上好! 很高兴今天能和大家聊聊一个挺有意思,也挺有挑战的话题:JS Headless Browser 的反检测。说白了,就是咱们用程序模拟人去浏览网页,但是有些网站它贼精,能一眼看穿你是不是机器人,然后把你拒之门外。咱们要做的,就是想办法把自己伪装得像个真人,让它们没法轻易识破。
一、 什么是 Headless Browser?为什么会被检测?
首先,简单解释下 Headless Browser。它就是一个没有界面的浏览器。你可以把它想象成一个在后台默默运行的浏览器内核,它可以执行 JavaScript,渲染网页,获取数据,等等,但你看不到它的窗口。 常用的 Headless Browser 包括:
- Puppeteer (Chrome/Chromium): 由 Google 维护,功能强大,社区活跃。
- Playwright (Chromium, Firefox, WebKit): 由 Microsoft 维护,支持多种浏览器内核。
- Selenium (多种浏览器): 历史悠久,应用广泛。
这些工具之所以会被检测,是因为它们的一些默认行为和属性与真实浏览器存在差异。网站的反爬虫机制通常会检查以下几个方面:
-
User-Agent: 这是浏览器告诉服务器自己身份的标识。 Headless Browser 的默认 User-Agent 通常包含 "HeadlessChrome" 或类似字样,很容易被识别。
-
WebDriver 检测: 网站会检查是否存在
window.navigator.webdriver
属性。 如果这个属性为true
,就说明你很可能是在使用 Selenium 或类似工具。 -
JavaScript 特性检测: Headless Browser 在某些 JavaScript 特性的实现上可能与真实浏览器存在差异。 比如,
navigator.plugins
、navigator.languages
等属性的内容可能不一致。 -
行为模式分析: 网站会分析用户的行为模式,比如鼠标移动轨迹、键盘输入速度等。 如果你的行为过于规律或者过于快速,就可能被识别为机器人。
-
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 本身的反爬能力更强,通常不需要额外的插件,但也可以结合一些技巧来增强。
五、 行为模拟:让机器人更像人
即使伪造了各种指纹,如果你的行为过于规律,仍然会被识别为机器人。 因此,我们需要模拟真实用户的行为。
- 随机延迟: 在执行操作之间添加随机延迟,模拟人的思考时间。
// 模拟随机延迟
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');
- 鼠标移动轨迹模拟: 模拟真实的鼠标移动轨迹,不要直接跳转到目标位置。 可以使用一些库来生成平滑的鼠标移动轨迹。
一种简单的实现方式是使用贝塞尔曲线:
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');
-
键盘输入模拟: 模拟真实的键盘输入速度和停顿。
-
滚动页面: 模拟用户滚动页面的行为。
-
随机访问页面: 不要只访问目标页面,可以先访问一些其他页面,模拟用户的浏览习惯。
六、 IP 代理:最后的防线
即使你伪造了各种指纹,模拟了各种行为,如果你的 IP 地址被封禁,一切都白费了。 因此,使用 IP 代理是必不可少的。
-
免费代理: 可以从网上找到一些免费代理,但是质量通常不高,速度慢,不稳定,而且容易被封禁。
-
付费代理: 付费代理通常质量更高,速度更快,更稳定,而且不容易被封禁。 常用的付费代理包括:
- 住宅代理: 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 地址。
- 选择与目标网站地理位置相近的代理服务器,可以提高访问速度。
- 验证代理服务器是否可用,可以使用
curl
或wget
命令。
七、 总结与展望
反检测是一个持续对抗的过程。 网站的反爬虫技术在不断升级,我们的反反爬虫技术也要不断进步。 没有一劳永逸的解决方案。
技巧 | 优点 | 缺点 |
---|---|---|
User-Agent 伪造 | 简单易用 | 容易被识别,只能作为第一层伪装 |
WebDriver 属性移除 | 较为有效,可以避免被 Selenium 等工具直接识别 | 有些网站会使用其他方式来检测 WebDriver |
指纹伪造 | 可以模拟真实的浏览器指纹,提高伪装度 | 比较复杂,需要使用专门的库 |
行为模拟 | 可以模拟真实用户的行为,避免被识别为机器人 | 需要花费更多的时间和精力 |
IP 代理 | 可以避免 IP 地址被封禁 | 需要购买代理服务器,成本较高 |
未来,反检测技术可能会更加智能化,比如使用机器学习来分析用户的行为模式。 我们也需要不断学习新的技术,才能更好地应对反检测的挑战。 例如,可以研究如何利用 GAN (Generative Adversarial Network) 来生成更真实的浏览器指纹。
最后,强调一点: 请遵守法律法规和网站的使用协议,不要进行恶意爬虫行为。 咱们的目标是学习技术,而不是破坏网络秩序。
希望今天的分享对大家有所帮助! 如果大家有什么问题,欢迎提问。