声明
本文所讨论的内容及技术均纯属学术交流与技术研究目的,旨在探讨和总结互联网数据流动、前后端技术架构及安全防御中的技术演进。文中提及的各类技术手段和策略均仅供技术人员在合法与合规的前提下进行研究、学习与防御测试之用。
作者不支持亦不鼓励任何未经授权的工程应用或违法行为,所有内容均不构成任何非法操作的技术指导或建议。请各位读者根据所在平台的相关规定及法律法规谨慎使用和解读本文内容。
引言
在过去的六篇文章中,我们以“庖丁解牛”的方式拆解了反爬虫攻防的核心逻辑,从协议分析、行为特征检测到动态渲染对抗,逐步构建了一套完整的反爬虫技术设计框架。
从这一篇开始,一切将截然不同,我们将以代码说话。
设计是技术的骨架,而开发才是赋予其血肉与灵魂的过程。本文将带领读者正式跨越理论与实践的边界,进入实战开发阶段。我们将亲手实现一个轻量指纹浏览器SDK,并深度集成浏览器自动化工具(如Puppeteer、Playwright)。
接下来的内容将默认读者已掌握Python/JavaScript基础及HTTP协议核心知识。
接口设计
在设计SDK接口时,我反复澄清要给到最终使用者的是什么?更多功能?还是更简单易用?在这里,我选择了更加倾向于易用性。毕竟我们的程序,是要解决问题,降低复杂度,而非制造问题。因此我们在设计浏览器模块接口时,只关注以下几个动作:
- 创建一个浏览器实例
- 关闭浏览器实例,并回收相关资源
- 查看当前正在运行中的浏览器实例
这些行为,已经完全可以满足一个爬虫在执行过程中所需进行的全部动作了。因为在浏览器实例创建完毕之并与爬虫框架完成CDP连接之后,后续的就都是爬虫系统的行为了。
接口文档
由于我们是指纹浏览器,因此我们先把指纹模型的设计放在最开始
指纹数据
{
"screen": {
"width": 800, // 宽度
"height": 600 // 高度
},// 屏幕分辨率 非必填
"timezone": "Asia/Hong_Kong",// 时区,非必填
"userAgent": "Mozilla/5.0 (Windows NT 10.0; WOW64) Chrome/86.0.4240.198", // UA信息 非必填
"platform": "Win32",// 平台信息 非必填
"userAgentData": {
"productName": "Google Chrome", //浏览器品牌名 Google Chrome / Microsoft Edge / Chromium / Brave
"productVersion": "110.0.5481.78", // 浏览器版本号
"isMobile": false, // 是否为手机版
"platform": "Windows", // 运行平台 Windows/Mac OS X/Linux/iOS/Android
"platformVersion": "15.0.0", // 运行平台版本号 Windows 举例 Win11:15.0.0 /Win10: 10.0.0
"architecture": "x86", // 编译架构 arm/x86
"bitness": "64", //运行平台 位数 32/64
"model": "", //品牌型号,仅限手机版情况下有值
"wow64": false // 99 以下 undefined,99 以上 false(仅win有判断,其他系统统一false)
},// UAData信息 非必填 但UA发生变化时必填,同时会校验此信息合法性
"deviceMemory?": 8, // 设备内存 非必填
"hardwareConcurrency?": 4, // 硬件并发数 非必填
"languages": [
"en-US",
], // 页面语言 非必填
"locale": "en-US", // UI语言 非必填
},
"font": {
"unicode": true, //是否禁用Emoji表情,true|false
"fonts": [
"Arial",
"Arial Black",
"Arial Narrow",
"Calibri",
"Cambria",
"Cambria Math",
"Comic Sans MS"
],// 字体列表
}, // 字体信息 非必填
"audio": {
"seed": "xxxxx"
},// 音频指纹噪声 非必填
"canvas": {
"seed": "xxxxx"
},// Canvas指纹噪声 非必填
"webgl": {
"seed": "xxxxx", // 噪声 非必填
"meta":{
"vendor": "Google Inc.", // 提供商
"renderer": "ANGLE (NVIDIA GeForce GT 730)" // 渲染器
} //元数据 非必填
},// WebGL配置 非必填
"geolocation": {
"latitude": 44.0,
"longitude": 55.0
},// 定位信息 非必填
"bluetooth": {
"enabled": false // 是否启用蓝牙,true or false
},// 蓝牙状态 非必填
"speech": [
// 至少有一项的is_default为true !
// 否则朗读者列表强制转化为空,显示为blocked
{
"name": "Microsoft Swara - Hindi (India)", // 朗诵者名称,字符串
"lang": "hi-IN", // 朗诵者语言,字符串
"isDefault": true // 是否为默认语音,true or false
},
{
"name": "Microsoft Sylvie - French (Canada)",
"lang": "fr-CA",
"isDefault": false
}
]// 朗读者列表 非必填
}
以上是我们指纹部分支持的全部配置,通过这些指纹的排列组合,我们已经可以很好的确定浏览器实例的指纹唯一性了,下面我们来继续完善其他操作的文档。
新建浏览器实例
API
路径 | 请求方式 |
/browsers | POST |
Request
{
"userDataDir":"", // 数据缓存路径 必填
"proxy":{
"protocol":"http",// 协议 支持http,https,socks 必填
"host":"39.123.33.154",// 代理主机 支持 IPv4 IPv6 以及域名 必填
"port":10234,// 代理端口 必填
"username":"",// 用户名 非必填
"password":"",// 密码 非必填
},//代理 非必填
"args":[],// 浏览器附加的命令行参数,非必填 会过滤掉 --proxy-server --remote-debugging-port
"metadata":{},//用户存储的元数据,非必填
"fingerprint":{},// 指纹数据,非必填 无指纹数据时则不会对指纹进行模拟。
}
Response
{
"id":"xxxx-xxxx-xxxx-xxxx-xxxx", // 浏览器实例ID
"proxyUrl":"http://127.0.0.1:11111",// 浏览器使用的本地转发后的代理链接
"args":[],// 浏览器附加的参数
"metadata":{},// 用户缓存的元数据
"automation":{
"driverPath":"",// 浏览器匹配的WebDriver路径
"remoteDebuggingPort":32023,// 远程调试端口
"cdpUrl":"http://127.0.0.1:32023",// CDP连接路径
"cdpWsUrl":"ws://localhost:32023/devtools/page/DAB7FB6187B554E10B0BD18821265734",// CDP Websocket连接路径
},// 自动化相关信息
"createdTime":""// 创建时间
}
关闭浏览器实例
API
路径 | 请求方式 |
/browsers/{browserId} | DELETE |
Response
{
"id":"xxxx-xxxx-xxxx-xxxx-xxxx", // 浏览器实例ID
"proxyUrl":"http://127.0.0.1:11111",// 浏览器使用的本地转发后的代理链接
"args":[],// 浏览器附加的参数
"metadata":{},// 用户缓存的元数据
"automation":{
"driverPath":"",// 浏览器匹配的WebDriver路径
"remoteDebuggingPort":32023,// 远程调试端口
"cdpUrl":"http://127.0.0.1:32023",// CDP连接路径
"cdpWsUrl":"ws://localhost:32023/devtools/page/DAB7FB6187B554E10B0BD18821265734",// CDP Websocket连接路径
},// 自动化相关信息
"createdTime":""// 创建时间
}
关闭全部浏览器实例
API
路径 | 请求方式 |
/browsers | DELETE |
Response
{
"id":"xxxx-xxxx-xxxx-xxxx-xxxx", // 浏览器实例ID
"proxyUrl":"http://127.0.0.1:11111",// 浏览器使用的本地转发后的代理链接
"args":[],// 浏览器附加的参数
"metadata":{},// 用户缓存的元数据
"automation":{
"driverPath":"",// 浏览器匹配的WebDriver路径
"remoteDebuggingPort":32023,// 远程调试端口
"cdpUrl":"http://127.0.0.1:32023",// CDP连接路径
"cdpWsUrl":"ws://localhost:32023/devtools/page/DAB7FB6187B554E10B0BD18821265734",// CDP Websocket连接路径
},// 自动化相关信息
"createdTime":""// 创建时间
}
查看当前所有正在运行的浏览器实例
API
路径 | 请求方式 |
/browsers | GET |
Response
[{
"id":"xxxx-xxxx-xxxx-xxxx-xxxx", // 浏览器实例ID
"proxyUrl":"http://127.0.0.1:11111",// 浏览器使用的本地转发后的代理链接
"args":[],// 浏览器附加的参数
"metadata":{},// 用户缓存的元数据
"automation":{
"driverPath":"",// 浏览器匹配的WebDriver路径
"remoteDebuggingPort":32023,// 远程调试端口
"cdpUrl":"http://127.0.0.1:32023",// CDP连接路径
"cdpWsUrl":"ws://localhost:32023/devtools/page/DAB7FB6187B554E10B0BD18821265734",// CDP Websocket连接路径
},// 自动化相关信息
"createdTime":""// 创建时间
}]
以上就是全部的接口设计文档了。
使用案例
下面我们以用户的角度,来使用我们的SDK,以下是一个基于Python与Playwright框架的简单例子。
import requests
from playwright.sync_api import sync_playwright
# API信息
api_url = "http://localhost:3000/browsers" # 假设这是我们SDK服务的地址
# 请求数据
request_data = {
"userDataDir": "/path/to/user/data", # 请替换为实际的数据缓存路径
"proxy": {
"protocol": "http",
"host": "39.123.33.154",
"port": 10234,
"username": "",
"password": ""
},
"args": [],
"metadata": {
"description":"一个演示用的实例"
},
"fingerprint": {
"screen":{
"width":800,
"height":600
},
"userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
"userAgentData": {
"productName": "Google Chrome",
"productVersion": "133.0.6943.142",
"isMobile": false,
"platform": "Windows",
"platformVersion": "15.0.0",
"architecture": "x86",
"bitness": "64",
"model": "",
"wow64": false
}
}
}
# 发送POST请求创建浏览器实例
response = requests.post(api_url, json=request_data)
# 检查响应状态码
if response.status_code == 200:
response_data = response.json()
print("浏览器实例创建成功:")
print(response_data)
# 获取CDP连接信息
cdp_ws_url = response_data["automation"]["cdpWsUrl"]
# 使用Playwright连接到浏览器实例
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(cdp_ws_url)
# ... Todo
else:
print(f"请求失败,状态码: {response.status_code},错误信息: {response.text}")
以上就是一个使用SDK的简单示例,我们在启动时,提供了一个基于Http协议的代理,以及提供了屏幕大小、UA的指纹模拟。并在启动完成之后,通过cdpWsUrl,将浏览器实例与Playwright进行连接,最终完成我们的爬虫操作。
总结
本文从理论到实践,详细介绍了一个轻量级指纹浏览器SDK的设计与实现。文章强调了易用性,将SDK功能聚焦于创建、关闭和查看浏览器实例三个核心操作。文中详细说明了指纹数据模型结构,包括屏幕分辨率、用户代理、平台信息等多个维度,确保浏览器实例的唯一性。
接口设计部分提供了完整的API文档,并通过一个Python和Playwright的案例展示了实际应用。这一实现不仅将之前讨论的反爬虫理论付诸实践,还为开发者提供了一个灵活、易用的工具,帮助他们在复杂的反爬虫环境中更有效地进行数据采集工作。并成功地将复杂的反爬虫技术转化为可操作的开发实践,为读者搭建了理论与实践之间的桥梁。