在开始构建滑块验证码识别系统之前,我们需要准备好开发环境和必要的工具链。这里我推荐使用Python 3.8+版本,因为这个版本在兼容性和性能方面都有不错的表现。核心工具包括Selenium用于浏览器自动化,ddddocr用于图像识别,以及一些辅助库如requests用于网络请求。
安装依赖库非常简单,只需要执行以下命令:
bash复制pip install selenium ddddocr requests pillow
关于浏览器驱动的选择,我建议使用ChromeDriver,因为它与Selenium的兼容性最好。不过在实际项目中我发现,不同版本的Chrome浏览器需要对应特定版本的ChromeDriver,这点需要特别注意。我曾经因为版本不匹配的问题调试了整整一个下午,后来发现只需要去ChromeDriver官网下载对应版本就能解决。
对于开发环境配置,我强烈建议使用虚拟环境。这样可以避免不同项目之间的依赖冲突。创建虚拟环境的命令如下:
bash复制python -m venv captcha_env
source captcha_env/bin/activate # Linux/Mac
captcha_env\Scripts\activate # Windows
在实际操作中,我发现极验验证码的触发机制有些特殊。它通常不会在页面加载时就出现,而是需要先点击某个按钮才会显示验证码界面。这给自动化测试带来了一些挑战,因为我们需要模拟真实的用户操作流程。
下面这段代码展示了如何通过Selenium触发验证码:
python复制from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
driver.get('https://www.geetest.com/adaptive-captcha-demo')
# 等待并点击滑块验证选项
slider_option = WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.XPATH, "//div[contains(text(),'滑动拼图验证')]"))
)
slider_option.click()
# 点击开始验证按钮
start_button = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_btn_click'))
)
start_button.click()
这里有几个关键点需要注意:
获取验证码图片是整个流程中最关键的一步。极验验证码通常由两张图片组成:背景图和滑块图。这两张图片都是以CSS背景图的方式动态加载的,我们需要通过解析元素的style属性来获取真实的图片URL。
我整理了一个可靠的图片获取方法:
python复制import re
def get_image_url(driver, class_name):
element = driver.find_element(By.CLASS_NAME, class_name)
style = element.get_attribute("style")
match = re.search(r'url\("(.*?)"\)', style)
if match:
return match.group(1)
return None
bg_url = get_image_url(driver, 'geetest_bg')
slice_url = get_image_url(driver, 'geetest_slice_bg')
在实际项目中,我发现极验的图片URL经常会变化,而且有时会有防盗链措施。为了解决这个问题,我通常会做以下处理:
图片下载的代码示例如下:
python复制import requests
from io import BytesIO
from PIL import Image
session = requests.Session()
headers = {'Referer': 'https://www.geetest.com/'}
def download_image(url):
response = session.get(url, headers=headers)
return Image.open(BytesIO(response.content))
bg_image = download_image(bg_url)
slice_image = download_image(slice_url)
ddddocr是一个非常强大的OCR库,特别适合用于验证码识别。它内置了专门针对滑块验证码的识别算法,使用起来非常简单。不过在实际使用中,我发现调整参数可以显著提高识别准确率。
基础使用方法如下:
python复制import ddddocr
slide_detector = ddddocr.DdddOcr(det=False, ocr=False, show_ad=False)
with open('bg.png', 'rb') as f:
bg_bytes = f.read()
with open('slice.png', 'rb') as f:
slice_bytes = f.read()
result = slide_detector.slide_match(slice_bytes, bg_bytes, simple_target=True)
print(result['target']) # 输出缺口位置坐标
对于识别效果不佳的情况,我总结了几点优化建议:
我曾经遇到过一个特别棘手的案例,识别率始终低于50%。后来发现是因为验证码图片有噪声干扰。通过添加以下预处理代码,识别率提升到了90%以上:
python复制from PIL import ImageFilter
def preprocess_image(image):
image = image.convert('L') # 转为灰度图
image = image.filter(ImageFilter.MedianFilter()) # 中值滤波去噪
return image
processed_bg = preprocess_image(bg_image)
processed_slice = preprocess_image(slice_image)
直接移动到目标位置会被识别为机器操作,因此我们需要模拟人类的滑动轨迹。经过多次测试,我发现极验的验证系统主要检测以下几个方面:
下面是一个经过优化的滑动函数:
python复制import random
import time
from selenium.webdriver import ActionChains
def human_slide(driver, slider, distance):
actions = ActionChains(driver)
actions.click_and_hold(slider).perform()
current_pos = 0
remaining = distance
# 初始加速阶段
while current_pos < distance * 0.3:
move = random.randint(3, 6)
actions.move_by_offset(move, random.randint(-2, 2)).perform()
current_pos += move
time.sleep(random.uniform(0.01, 0.05))
# 中间减速阶段
while current_pos < distance * 0.8:
move = random.randint(2, 4)
actions.move_by_offset(move, random.randint(-1, 1)).perform()
current_pos += move
time.sleep(random.uniform(0.05, 0.1))
# 最终微调阶段
while current_pos < distance:
move = min(random.randint(1, 2), distance - current_pos)
actions.move_by_offset(move, 0).perform()
current_pos += move
time.sleep(random.uniform(0.1, 0.3))
# 小幅回拉模拟人手抖动
actions.move_by_offset(random.randint(-3, -1), 0).perform()
time.sleep(0.2)
actions.release().perform()
这个函数模拟了人类滑动验证码的典型行为:开始快速移动,中间减速,最后缓慢精确调整。我还添加了垂直方向的随机偏移和小幅回拉,使得行为更加真实。
在实际运行中,会遇到各种异常情况。为了提高脚本的稳定性,我们需要添加完善的异常处理机制。以下是我总结的常见问题及解决方案:
改进后的完整代码框架如下:
python复制MAX_RETRY = 3
def solve_captcha(driver, retry=0):
try:
# 触发验证码
trigger_captcha(driver)
# 获取图片
bg_url = get_image_url(driver, 'geetest_bg')
slice_url = get_image_url(driver, 'geetest_slice_bg')
# 下载并处理图片
bg_img = download_image(bg_url)
slice_img = download_image(slice_url)
# 识别缺口位置
position = detect_gap(bg_img, slice_img)
if position['confidence'] < 0.7 and retry < MAX_RETRY:
return solve_captcha(driver, retry + 1)
# 模拟滑动
slider = driver.find_element(By.CLASS_NAME, 'geetest_btn')
human_slide(driver, slider, position['x'])
# 验证结果
if not is_success(driver):
raise Exception("Verification failed")
except Exception as e:
if retry < MAX_RETRY:
return solve_captcha(driver, retry + 1)
raise e
将上述所有模块整合起来,我们得到一个完整的滑块验证码识别解决方案。为了便于在实际项目中使用,我建议将其封装成一个独立的类:
python复制class GeetestSolver:
def __init__(self, driver):
self.driver = driver
self.ocr = ddddocr.DdddOcr(det=False, ocr=False, show_ad=False)
self.session = requests.Session()
self.session.headers.update({'Referer': 'https://www.geetest.com/'})
def trigger_captcha(self):
# 实现触发逻辑
pass
def get_images(self):
# 实现图片获取逻辑
pass
def detect_gap(self, bg_img, slice_img):
# 实现缺口检测逻辑
pass
def slide(self, distance):
# 实现滑动逻辑
pass
def solve(self):
# 整合完整流程
self.trigger_captcha()
bg_img, slice_img = self.get_images()
position = self.detect_gap(bg_img, slice_img)
self.slide(position['x'])
return self.check_result()
在部署时,建议考虑以下几点: