本文记录了 Python 使用 Playwright 库结合 Edge 浏览器通过 Cloudflare 人机验证的相关代码。
前言
本篇文章与《Debian 安装 Edge 浏览器并启用 CDP 协议》文章隶属于同一个项目。
本来打算这两篇文章前后脚发出,但中途沉迷于游戏无法自拔,故本文理所应当的延期了。
本文仅适用于 Cloudflare Turnstile 人机验证,对于其他人机验证不一定有效。
Windows 版本 : Windows 11 专业工作站版,23H2
Playwright 版本 : Version 1.49.1
Edge 版本 : 版本 124.0.2478.97 (正式版本) (64 位)
代码实现
此部分正式开始说明 Playwright 通过 Cloudflare 人机验证的基本原理,以及展示 Python 编写的相关代码。
基本原理
已开启 CDP 协议的 Edge 浏览器,若其正处于 Cloudflare 的人机验证页面,此时可用 Playwright 模拟鼠标点击通过验证。
只能选择 Chromium 内核的浏览器,且优先选择 Edge 浏览器,因 Chrome 浏览器尝试了几次均无法达到预期。
浏览器必须开启 CDP 协议,否则 Playwright 无法附加到浏览器实例。
在目标网站向 Cloudflare 发起人机验证请求之前,必须将 Playwright 与浏览器断开连接,否则本次人机验证将会失败。
不建议使用 Playwright 直接定位 iframe 标签中的元素,因其很难定位甚至无法定位。
最佳做法是先定位一个标志性的元素,然后使用坐标偏移的方式定位到 Cloudflare 的人机验证按钮,最后使用 Playwright 模拟鼠标点击。
核心代码
此部分展示 Playwright 通过 Cloudflare 人机验证的核心代码,该代码使用 Python 编写并尽可能添加详细的注释。
from time import sleep
from playwright.sync_api import sync_playwright
def pw_cloudflare_verify_human(css_selector: str, endpoint_url="http://localhost:9222", delay=5):
sleep(delay) # 延时启动,目的是等待 Cloudflare 元素加载完成
with sync_playwright() as p: # 创建 Playwright 上下文管理器
browser = p.chromium.connect_over_cdp(endpoint_url) # 使用 CDP 协议连接到 Chromium 浏览器
context = browser.contexts[0] # 获取浏览器中的第一个上下文
page = context.pages[0] # 获取上下文中的第一个页面
cf_div_bounding_box = page.locator(css_selector).bounding_box() # 使用 CSS 选择器定位元素
x = cf_div_bounding_box['x'] # 元素边界左上角的 X 坐标
y = cf_div_bounding_box['y'] # 元素边界左上角的 Y 坐标
width = cf_div_bounding_box['width'] # 元素的宽度(该变量此处用不上)
height = cf_div_bounding_box['height'] # 元素的高度
offset_x = x + 75 # 以 X 为起点向右偏移 75 像素
offset_y = y + (height / 2) # 以 Y 为起点向下偏移元素高度的一半(元素垂直中心)
page.mouse.click(offset_x, offset_y) # 模拟执行鼠标点击操作
函数 pw_cloudflare_verify_human()
主要作用是:
- 将 Playwright 通过 CDP 协议附加到 Chromium 内核的浏览器
- 使用 CSS 选择器定位关键元素并获取其屏幕坐标
- 根据关键元素坐标计算新的坐标并使用 Playwright 模拟鼠标点击
函数
pw_cloudflare_verify_human()
中的延时启动时间以及坐标偏移距离需要根据实际情况调整,此处设置的值仅适用于本文例子。
示例代码
此部分展示 Playwright 通过 Cloudflare 人机验证的示例代码,该代码使用 Python 编写并尽可能添加详细的注释。
from time import sleep
from subprocess import Popen, DEVNULL
from os.path import join, abspath, dirname
from playwright.sync_api import sync_playwright
def run_browser_with_cdp(browser_path: str):
"""
此函数用于启动 Chromium 浏览器同时开启 CDP 协议
:param browser_path: Chromium 浏览器的可执行文件路径
:return: 返回进程对象方便后续管理
"""
command = [
browser_path, # Chromium 浏览器的可执行文件路径
"--remote-debugging-port=9222", # 设置 CDP 协议使用的端口
f"--user-data-dir={join(dirname(abspath(__file__)), 'user_1')}", # 将浏览器用户数据保存在当前目录
"--no-first-run", # 阻止首次运行的引导界面
"--disable-sync", # 禁用浏览器同步功能
"--disable-extensions", # 禁用所有扩展程序
"--disable-infobars", # 禁用信息栏提示
"--disable-background-networking", # 禁用后台网络连接
"--no-default-browser-check", # 阻止默认浏览器检查
"--safebrowsing-disable-auto-update", # 禁用安全浏览自动更新
]
return Popen(command, shell=True, stdout=DEVNULL, stderr=DEVNULL) # 返回进程对象方便后续管理
def pw_cloudflare_trigger_turnstile_page(url: str, endpoint_url="http://localhost:9222"):
"""
此函数用于在 Chromium 浏览器中打开需要 Cloudflare 人机验证的页面
:param url: 需要 Cloudflare 人机验证的页面 URL 地址
:param endpoint_url: 开启 CDP 协议的 Chromium 浏览器地址和端口
:return: None
"""
with sync_playwright() as p: # 创建 Playwright 上下文管理器
browser = p.chromium.connect_over_cdp(endpoint_url) # 使用 CDP 协议连接到 Chromium 浏览器
context = browser.contexts[0] # 获取浏览器中的第一个上下文
page = context.pages[0] # 获取上下文中的第一个页面
context.clear_cookies() # 清除所有 Cookie 以便调试
page.goto(url) # 打开 URL 页面
def pw_cloudflare_verify_human(css_selector: str, endpoint_url="http://localhost:9222", delay=5):
sleep(delay) # 延时启动,目的是等待 Cloudflare 元素加载完成
with sync_playwright() as p: # 创建 Playwright 上下文管理器
browser = p.chromium.connect_over_cdp(endpoint_url) # 使用 CDP 协议连接到 Chromium 浏览器
context = browser.contexts[0] # 获取浏览器中的第一个上下文
page = context.pages[0] # 获取上下文中的第一个页面
cf_div_bounding_box = page.locator(css_selector).bounding_box() # 使用 CSS 选择器定位元素
x = cf_div_bounding_box['x'] # 元素边界左上角的 X 坐标
y = cf_div_bounding_box['y'] # 元素边界左上角的 Y 坐标
width = cf_div_bounding_box['width'] # 元素的宽度(该变量此处用不上)
height = cf_div_bounding_box['height'] # 元素的高度
offset_x = x + 75 # 以 X 为起点向右偏移 75 像素
offset_y = y + (height / 2) # 以 Y 为起点向下偏移元素高度的一半(元素垂直中心)
page.mouse.click(offset_x, offset_y) # 模拟执行鼠标点击操作
if __name__ == '__main__':
run_browser_with_cdp(r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe")
# 示例一 : Cloudflare 登录页
pw_cloudflare_trigger_turnstile_page("https://dash.cloudflare.com/login")
# pw_cloudflare_verify_human(".main-content>div:first-of-type")
pw_cloudflare_verify_human(".c_he>div") # 登录表单页的人机验证按钮
# # 示例二 : 某个小说网站的搜索页
# pw_cloudflare_trigger_turnstile_page("https://www.wcxsw.org/modules/article/search.php")
# pw_cloudflare_verify_human(".main-content>div:first-of-type")
# # 示例三 : 某个启用 Cloudflare 人机验证的博客
# pw_cloudflare_trigger_turnstile_page("https://blog.skk.moe/")
# pw_cloudflare_verify_human(".main-content>div:first-of-type")
# # 以下代码用于自动关闭 Edge 浏览器(仅适用于 Windows 系统)
# sleep(8)
# Popen(["taskkill", "/F", "/IM", "msedge.exe"], shell=True)
函数 run_browser_with_cdp()
用于启动 Chromium 浏览器,此处传入参数应为 Edge 浏览器的可执行文件路径。
函数 pw_cloudflare_trigger_turnstile_page()
用于触发 Cloudflare 人机验证,此处传入需要触发人机验证的页面 URL 地址。
此部分中代码是一份完整的示例代码,可直接复制粘贴到 PyCharm 中运行,前提是运行环境与本文一致。
此示例代码经过了一定程度的精简,不保证稳定性与通用性,不建议照搬到实际项目中。
代码中提供了三个通过 Cloudflare 人机验证的使用示例,根据需求自行选择注释/取消注释代码并运行以查看效果。
总结
事实上 Playwright 通过 Cloudflare Turnstile 的人机验证并不困难,只要能弄清楚原理,代码实现就是信手拈来。
再次重申,如果被 Cloudflare 检测到 Playwright 的使用痕迹,则人机验证将会失败,所以在加载 Cloudflare 元素前必须断开 Playwright 与浏览器的连接。
函数 pw_cloudflare_verify_human()
中元素定位方式使用 Playwright 的 get_by_role()
、 get_by_text()
等方法亦或是 XPath 都可以。