Web 爬虫中绕过 Cloudflare
爬虫浏览器和 Cloudflare
本文档介绍如何使用 Scrapeless 爬虫浏览器和 Scrapeless Web Unlocker 工具来处理 Cloudflare 设置的各种安全挑战。主要功能包括绕过 Cloudflare JS 挑战、Cloudflare Turnstile 以及执行 JavaScript 渲染以访问受 Cloudflare 保护的内容。本文档将提供相关的背景知识、功能介绍、操作步骤和代码示例说明。
了解 Cloudflare 挑战和安全层
Cloudflare 是一款流行的 Web 安全和性能服务,主要保护网站免受恶意或意外流量(例如机器人和扫描程序)的攻击。为此,Cloudflare 实施了各种检测和防御机制,包括但不限于:
**1. JS 挑战(JavaScript 挑战):**要求访问者的浏览器执行特定的 JavaScript 代码,以验证它是一个具有标准功能的合法浏览器环境。
**2. Turnstile CAPTCHA 替代方案:**一种侵入性较小的验证机制,用于区分人类用户和机器人。
**3. 浏览器指纹识别:**收集和分析浏览器和设备的技术特征(例如 User-Agent、屏幕分辨率、已安装字体、插件)以识别和跟踪访问者。
**4. 速率限制:**监控和限制特定时间内来自单个来源(例如 IP 地址)的请求数量,以防止暴力破解或资源滥用。
这些安全层阻止基本的 HTTP 请求库、简单的脚本或配置不当的无头浏览器访问受 Cloudflare 保护的网站,导致验证失败和访问被拒绝。
Cloudflare JS 挑战与其他挑战的区别
Cloudflare JS 挑战的独特之处在于其验证方法。它不仅要求用户完成简单的交互式任务(例如图像识别);它要求客户端环境(浏览器)成功解析并执行 Cloudflare 生成的动态的、通常是经过混淆的 JavaScript 代码。此代码执行环境检查、计算密集型任务或其他逻辑,以验证客户端的复杂行为能力,模拟真实的浏览器。
成功通过 JS 挑战涉及生成有效的清除令牌(通常以 cf_clearance
Cookie 的形式)。此令牌证明客户端通过了 JavaScript 执行能力验证。许多自动化工具缺乏完整的 JavaScript 执行引擎和真实的浏览器环境模拟,因此无法通过此类挑战。
使用 Scrapeless 爬虫浏览器绕过 Cloudflare JS 挑战
Scrapeless 爬虫浏览器旨在处理复杂的网站保护措施,包括 Cloudflare JS 挑战。
步骤和代码示例
环境设置
创建项目文件夹
为项目创建一个新文件夹,例如:scrapeless-bypass。
在终端中导航到该文件夹:
cd path/to/scrapeless-bypass
初始化 Node.js 项目
运行以下命令创建 package.json 文件:
npm init -y
安装所需的依赖项
安装 Puppeteer-core,它允许远程连接到浏览器实例:
npm install puppeteer-core
如果系统上尚未安装 Puppeteer,请安装完整版本:
npm install puppeteer puppeteer-core
获取和配置您的 Scrapeless API 密钥。
连接并确保 CAPTCHA 已成功解决
Scrapeless 在连接到浏览器以访问目标网站时会自动检测并解决 CAPTCHA。但是,我们需要确保 CAPTCHA 已成功解决。Scrapeless 爬虫浏览器使用一组强大的自定义功能扩展了标准 CDP(Chrome DevTools Protocol)。可以通过检查从 CDP API 返回的结果来直接观察 CAPTCHA 求解器的状态:
Captcha.detected
: 检测到 CAPTCHACaptcha.solveFinished
: CAPTCHA 成功解决Captcha.solveFailed
: CAPTCHA 解决失败
import puppeteer from "puppeteer-core";
import EventEmitter from 'events';
import { Scrapeless } from '@scrapeless-ai/sdk';
const emitter = new EventEmitter();
const client = new Scrapeless({ apiKey: 'API Key' });
const { browserWSEndpoint } = client.browser.create({
session_name: 'sdk_test',
session_ttl: 180,
proxy_country: 'ANY',
session_recording: true,
fingerprint,
});
export async function example(url) {
const browser = await puppeteer.connect({
browserWSEndpoint,
defaultViewport: null
});
console.log("Verbonden met Scrapeless browser");
try {
const page = await browser.newPage();
// Listen for captcha events
console.debug("addCaptchaListener: Start listening for captcha events");
await addCaptchaListener(page);
console.log("Navigated to URL:", url);
await page.goto(url, { waitUntil: "domcontentloaded", timeout: 30000 });
console.log("onCaptchaFinished: Waiting for captcha solving to finish...");
await onCaptchaFinished()
// Screenshot for debugging
console.debug("Taking screenshot of the final page...");
await page.screenshot({ path: 'screenshot.png', fullPage: true });
} catch (error) {
console.error(error);
} finally {
await browser.close();
console.log("Browser closed");
}
}
async function addCaptchaListener(page) {
const client = await page.createCDPSession();
client.on("Captcha.detected", (msg) => {
console.debug("Captcha.detected: ", msg);
});
client.on("Captcha.solveFinished", async (msg) => {
console.debug("Captcha.solveFinished: ", msg);
emitter.emit("Captcha.solveFinished", msg);
client.removeAllListeners()
});
}
async function onCaptchaFinished(timeout = 60_000) {
return Promise.race([
new Promise((resolve) => {
emitter.on("Captcha.solveFinished", (msg) => {
resolve(msg);
});
}),
new Promise((_, reject) => setTimeout(() => reject('Timeout'), timeout))
])
}
此示例工作流程访问目标网站并通过侦听 Captcha.solveFinished CDP 事件来确认 CAPTCHA 已成功解决。最后,它捕获页面的屏幕截图以进行验证。
此示例定义了两种主要方法:
addCaptchaListener
:用于侦听浏览器会话中的 CAPTCHA 事件onCaptchaFinished
:用于等待直到 CAPTCHA 已解决
上述示例代码可用于侦听本文中讨论的三种常见 CAPTCHA 类型的 CDP 事件:reCAPTCHA v2、Cloudflare Turnstile 和 Cloudflare 5s 挑战。
请注意,Cloudflare 5s 挑战有些特殊。有时它不会触发实际的挑战,并且仅依赖于 CDP 事件检测来判断成功可能会导致超时。因此,在挑战之后等待页面上出现特定元素是更稳定的解决方案。
连接到 Scrapeless 无服务器 WebSocket
Scrapeless 提供 WebSocket 连接,允许 Puppeteer 直接与无头浏览器交互,从而绕过 Cloudflare 挑战。
完整的 WebSocket 连接地址:
wss://browser.scrapeless.com/browser?token=APIKey&session_ttl=180&proxy_country=ANY
代码示例:绕过 Cloudflare 挑战
我们只需要以下代码即可连接到 Scrapeless 的无服务器服务。
import puppeteer from 'puppeteer-core';
const API_KEY = 'your_api_key'
const host = 'wss://browser.scrapeless.com';
const query = new URLSearchParams({
token: API_KEY,
session_ttl: '180', // 浏览器会话的生命周期(秒)
proxy_country: 'GB', // 代理国家/地区
proxy_session_id: 'test_session_id', // 代理会话 ID 用于保持代理 IP 不变。会话时间默认为 3 分钟,基于 proxy_session_duration 设置。
proxy_session_duration: '5' // 代理会话时间,单位分钟
}).toString();
const connectionURL = `${host}/browser?${query}`;
const browser = await puppeteer.connect({
browserWSEndpoint: connectionURL,
defaultViewport: null,
});
console.log('Connected!')
访问受 Cloudflare 保护的网站和屏幕截图验证
接下来,我们使用 scrapeless browserless 直接访问 cloudflare-challenge 测试站点 并添加屏幕截图,以便进行可视化验证。在拍摄屏幕截图之前,请注意您需要使用 waitForSelector
来等待页面上的元素,以确保已成功绕过 Cloudflare 挑战。
const page = await browser.newPage();
await page.goto('https://www.scrapingcourse.com/cloudflare-challenge', {waitUntil: 'domcontentloaded'});
// 通过等待站点页面中的元素,确保已成功绕过 Cloudflare 挑战。
await page.waitForSelector('main.page-content .challenge-info', {timeout: 30 * 1000})
await page.screenshot({path: 'challenge-bypass.png'});
此时,您已使用 Scrapeless Browserless 绕过了 Cloudflare 挑战。
获取 cf_clearance Cookie 和 Header
通过 Cloudflare 挑战后,您可以从成功的页面中检索请求标头和 cf_clearance Cookie。
const cookies = await browser.cookies()
const cfClearance = cookies.find(cookie => cookie.name === 'cf_clearance')?.value
启用请求拦截以捕获请求标头并匹配 Cloudflare 挑战后的页面请求。
await page.setRequestInterception(true);
page.on('request', request => {
// 匹配 Cloudflare 挑战后的页面请求
if (request.url().includes('https://www.scrapingcourse.com/cloudflare-challenge') && request.headers()?.['origin']) {
const accessRequestHeaders = request.headers();
console.log('[access_request_headers] =>', accessRequestHeaders);
}
request.continue();
});
使用 Scrapeless Web Unlocker 进行 JavaScript 渲染
对于核心内容依赖于客户端 JavaScript 执行才能完全加载和显示的受 Cloudflare 保护的网站,Scrapeless Web Unlocker 提供了专门的解决方案。
Scrapeless 通用 API 启用 JavaScript 渲染和动态交互,使其成为绕过 Cloudflare 的有效工具。
JavaScript 渲染
JavaScript 渲染支持处理动态加载的内容和 SPA(单页应用程序)。它支持完整的浏览器环境,处理更复杂的页面交互和渲染要求。
使用 js_render=true
,我们将使用浏览器进行请求
{
"actor": "unlocker.webunlocker",
"input": {
"url": "https://www.google.com/",
"js_render": true
},
"proxy": {
"country": "US"
}
}
JavaScript 指令
提供广泛的 JavaScript 指令,允许与网页进行动态交互。
这些指令能够点击元素、填充表单、提交表单或等待特定元素出现,从而为诸如点击“阅读更多”按钮或提交表单之类的任务提供灵活性。
{
"actor": "unlocker.webunlocker",
"input": {
"url": "https://example.com",
"js_render": true,
"js_instructions": [
{
"wait_for": [
".dynamic-content",
30000
]
// 等待元素
},
{
"click": [
"#load-more",
1000
]
// 点击元素
},
{
"fill": [
"#search-input",
"search term"
]
// 填充表单
},
{
"keyboard": [
"press",
"Enter"
]
// 模拟按键
},
{
"evaluate": "window.scrollTo(0, document.body.scrollHeight)"
// 执行自定义 JS
}
]
}
}
挑战绕过示例
以下示例使用 axios 向 Scrapeless 的 Web Unlocker 服务发送请求。它启用 js_render
并使用 js_instructions
参数中的 wait_for
指令来等待页面上绕过 Cloudflare 挑战后的元素:
import axios from 'axios'
async function sendRequest() {
const host = "api.scrapeless.com";
const url = `https://${host}/api/v1/unlocker/request`;
const API_KEY = 'your_api_key'
const payload = {
actor: "unlocker.webunlocker",
proxy: {
country: "US"
},
input: {
url: "https://www.scrapingcourse.com/cloudflare-challenge",
js_render: true,
js_instructions: [
{
wait_for: [
"main.page-content .challenge-info",
30000
]
}
]
},
}
try {
const response = await axios.post(url, payload, {
headers: {
'Content-Type': 'application/json',
'x-api-token': API_KEY
}
});
console.log("[page_html_body] =>", response.data);
} catch (error) {
console.error('Error:', error);
}
}
sendRequest();
运行上述脚本后,您将能够在控制台中看到成功绕过 Cloudflare 挑战的页面的 HTML。