自动化突破Cloudflare的5秒盾,Python WebDriver爬虫应对策略详解
技术背景
首先我们要理解什么是5秒盾:
这里的5秒盾指 Cloudflare 的“5 秒盾”(也叫 Cloudflare 5s Challenge)。这是 Cloudflare 用来防止机器人访问网站的一种机制,通常会显示一个“请稍等 5 秒钟”的页面,然后自动重定向到目标页面。
当我们使用webdirver爬取页面的时候,遇到这种问题经常束手无策,网上有的方案是人工点击,获取token注入cookie等,但是避免不了手动点击。
是否有个方案自动化解决? 答案是有的,本文采用的方案 能100%的跳过5秒盾,方案成熟,对 Bing Chat(https://copilot.microsoft.com/),Perplexity(https://www.perplexity.ai/) 做过多次测试。
实现方案
技术方案: selenium + DissPage(https://drissionpage.cn/)
本文以perplexity 网站5s盾为用例
检测是否有弹窗
解决问题,首先要检测并且定位是否页面出现了5秒盾
def check_robots():
try:
main_content = driver.find_element(By.CLASS_NAME, "main-content")
h1_element = main_content.find_element(By.CLASS_NAME, "zone-name-title")
h1_element.find_element(By.XPATH, "following-sibling::div")
return True
except:
return False
用新的tab也打开目标链接
一般情况下,遇到这个验证,几秒之后自动会跳走,但是当服务端发现客户端请求异常(比如检测到大量多次请求,检测到可能是自动化程序等),这个验证会一直过不去,并且即使非程序点击,人工点击后也一样。一直重复提示验证。
那么我们可以变向的,在当前tab下重新打开一个标签页面,在那个页面去验证人工校验。
def close_other_tab():
try:
window_handles = driver.window_handles
current_window = driver.current_window_handle
if len(current_window) > 1:
for handle in window_handles:
if handle != current_window:
driver.switch_to.window(handle)
driver.close()
driver.switch_to.window(current_window)
except:
print(traceback.format_exc())
调用DissPage点击弹窗
直接使用selenium 是无法通过的,即使点击了人工校验也会重复失败。我们使用 drissionpage 来处理点击人工校验部分。
def auto_robots():
idx = 0
while True:
idx = idx + 1
if idx > 3:
print("check Failed")
return False
if check_robots():
try:
drissObj = Driss(browser_port)
drissObj.to_click()
time.sleep(10)
close_other_tab()
switch_to_chat_page()
except:
continue
else:
return True
return False
Driss 相关处理(diss.py)
import time
import traceback
from DrissionPage import ChromiumPage, ChromiumOptions, WebPage
from DrissionPage.common import Keys, By
class Driss:
def __init__(self, port):
self.port = port
self.driver = self.getDriver()
def getDriver(self):
co = ChromiumOptions()
co.set_pref('credentials_enable_service', False)
co.set_argument('--hide-crash-restore-bubble')
co_page = ChromiumOptions().set_local_port(self.port)
return ChromiumPage(co_page)
def initDriver(self):
self.driver = self.getDriver()
def auto_robots(self):
try:
dom = self.driver. \
ele((By.CLASS_NAME, "main-content")). \
ele((By.CLASS_NAME, "zone-name-title h1")). \
ele((By.XPATH, "following-sibling::div")). \
ele((By.TAG_NAME, "div")).ele((By.TAG_NAME, "div")).shadow_root. \
ele((By.TAG_NAME, "iframe")).ele((By.TAG_NAME, "body")).shadow_root. \
ele((By.TAG_NAME, "input"))
dom.click()
return True
except:
print(traceback.format_exc())
return False
def to_click(self):
print("DISS create new tab")
self.driver = self.driver.new_tab()
time.sleep(2)
print("DISS to chat page")
self.driver.get('https://www.perplexity.ai/')
time.sleep(15)
self.auto_robots()
print("DISS handle ok")
技术难点解析
难点:不管那个站点,遇到人工校验首先要检测和定位。
部分网站可能做了影子元素不好定位,可以直接使用影子元素定位的方式来定位,比如
host_element = driver.find_element(By.CSS_SELECTOR, '.xxxx').shadow_root()
部分的影子元素可能是关闭的,需要执行js来打开。
from selenium import webdriver
# 创建WebDriver实例
driver = webdriver.Chrome()
# 打开目标网页
driver.get('http://example.com')
# 执行JavaScript脚本
script = """
let host = document.querySelector('#host');
if (host.shadowRoot === null) {
host.attachShadow({ mode: 'open' });
}
let shadowRoot = host.shadowRoot;
let child = document.createElement('div');
child.textContent = 'Hello, World!';
shadowRoot.appendChild(child);
"""
driver.execute_script(script)
# 关闭浏览器
driver.quit()
甚至部分网站限定了webdirver的各种检测访问,可以在启动浏览器的时候添加一些参数。
难点:原标签不能验证,那么使用新标签
部分网站,使用新标签 5s后会自动验证通过,但是有些网站做了一些安全防护后,这个自动就过不去了。需要手动点击或者模拟手动点击后才会通过。
因为selenium 比较流行,很多验证有对是否selenium 的检测,我们可以使用DissPage 从某些方面来规避该问题
最后,调试过程可能遇到各种问题,可以私我。
微信:Uminicmf
作者:youmypig