在爬虫开发中,验证码是常见的反爬手段,传统打码平台不仅需要付费,还存在响应延迟、隐私泄露风险。而 ddddocr(带带弟弟OCR) 作为开源免费的AI验证码识别库,基于深度学习模型,支持文字验证码、滑块验证码、点选验证码三大核心场景,无需复杂配置,一行代码即可实现识别,完美替代付费打码平台。

本文将从环境搭建、三大验证码场景实战、爬虫集成、优化技巧四个维度,教你用ddddocr打造零成本验证码识别方案,适配各类爬虫场景。

一、ddddocr 核心优势

特性 优势 对比付费打码平台
免费开源 无任何费用,可商用(遵循MIT协议) 按识别次数收费,量大成本高
本地识别 无需联网调用第三方接口,速度快(100ms内) 依赖网络,响应延迟高(500ms+)
多场景支持 文字(数字、字母、中文)、滑块、点选 部分平台仅支持单一类型
配置简单 无需训练,直接调用API 需对接平台接口,配置复杂
隐私安全 验证码图片本地处理,无泄露风险 需上传验证码图片到第三方平台,隐私暴露

二、环境准备

1. 安装依赖

ddddocr 支持 Python 3.8+,安装命令如下(无需额外安装模型文件,自动下载轻量预训练模型):

# 核心库:ddddocr(带带弟弟OCR)
pip install ddddocr==1.5.0
# 辅助库:处理图片、爬虫请求
pip install requests==2.31.0 pillow==10.3.0 selenium==4.21.0

2. 环境验证

运行以下代码,测试库是否安装成功:

import ddddocr

# 初始化OCR识别器
ocr = ddddocr.DdddOcr()
# 识别测试图片(可自行替换为本地验证码图片路径)
with open('test_captcha.png', 'rb') as f:
    img_bytes = f.read()
result = ocr.classification(img_bytes)
print(f"验证码识别结果:{result}")

若能正常输出识别结果,说明环境配置成功。

三、三大验证码场景实战

场景1:文字验证码识别(数字/字母/中文混合)

文字验证码是最常见的类型,ddddocr 对印刷体、模糊、扭曲、有干扰线的文字验证码均有较好的识别率。

实战步骤:
  1. 爬虫请求页面,获取验证码图片(本地保存或直接读取字节流);
  2. 用 ddddocr 识别图片中的文字;
  3. 将识别结果填入表单,提交登录/验证请求。
代码示例(requests 爬取+文字验证码识别):
import requests
from PIL import Image
import ddddocr
from io import BytesIO

# 目标网站(示例:某需要文字验证码登录的网站)
LOGIN_URL = "https://example.com/login"
CAPTCHA_URL = "https://example.com/get_captcha"

# 初始化OCR识别器(enable_gpu=True 可启用GPU加速,需安装CUDA)
ocr = ddddocr.DdddOcr(show_ad=False)  # show_ad=False 关闭广告

# 1. 获取验证码图片
session = requests.Session()
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"
}
captcha_response = session.get(CAPTCHA_URL, headers=headers)
# 保存验证码图片(可选,用于调试)
with open("captcha.png", "wb") as f:
    f.write(captcha_response.content)

# 2. 识别验证码(直接读取字节流,无需保存本地)
captcha_result = ocr.classification(captcha_response.content)
print(f"识别的文字验证码:{captcha_result}")

# 3. 提交登录请求
login_data = {
    "username": "你的账号",
    "password": "你的密码",
    "captcha": captcha_result  # 填入识别结果
}
response = session.post(LOGIN_URL, data=login_data, headers=headers)
if "登录成功" in response.text:
    print("登录成功!")
else:
    print("登录失败,可能是验证码识别错误,重试...")
优化技巧:
  • 若验证码有干扰线/噪点:用 PIL 预处理图片(灰度化、二值化、降噪),提升识别率:
    # 图片预处理:灰度化+二值化
    img = Image.open(BytesIO(captcha_response.content)).convert("L")  # 灰度化
    threshold = 127  # 阈值,可调整
    img = img.point(lambda p: p > threshold and 255)  # 二值化
    # 转为字节流供ddddocr识别
    img_byte_arr = BytesIO()
    img.save(img_byte_arr, format='PNG')
    img_byte_arr = img_byte_arr.getvalue()
    captcha_result = ocr.classification(img_byte_arr)
    
  • 启用GPU加速:若有NVIDIA显卡,安装CUDA后,初始化时设置 ocr = ddddocr.DdddOcr(show_ad=False, enable_gpu=True),识别速度提升3-5倍。

场景2:滑块验证码识别(缺口匹配)

滑块验证码是目前最流行的反爬手段之一,核心是识别“滑块缺口位置”,计算滑块需要移动的距离。ddddocr 提供 SlideDetector 类,专门用于缺口检测。

实战步骤:
  1. 获取滑块背景图和滑块图(从网页中提取);
  2. 用 ddddocr 识别缺口在背景图中的X坐标;
  3. 用 Selenium 模拟滑块拖动(匀速+轨迹模拟,避免被检测)。
代码示例(Selenium+滑块验证码识别):
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import requests
import ddddocr
from PIL import Image
from io import BytesIO
import time

# 初始化Selenium
driver = webdriver.Chrome()
driver.get("https://example.com/slider-captcha")  # 滑块验证码页面
time.sleep(2)

# 1. 提取滑块背景图和滑块图的URL(需根据实际网页结构调整选择器)
bg_img_url = driver.find_element(By.CLASS_NAME, "bg-img").get_attribute("src")  # 背景图URL
slider_img_url = driver.find_element(By.CLASS_NAME, "slider-img").get_attribute("src")  # 滑块图URL

# 2. 下载图片(携带Cookie,避免图片403)
session = requests.Session()
# 复制Selenium的Cookie到requests
for cookie in driver.get_cookies():
    session.cookies.set(cookie['name'], cookie['value'])

# 下载背景图
bg_response = session.get(bg_img_url)
bg_img = Image.open(BytesIO(bg_response.content))
# 下载滑块图
slider_response = session.get(slider_img_url)
slider_img = Image.open(BytesIO(slider_response.content))

# 3. 用ddddocr识别缺口位置(返回缺口的X坐标)
slide_det = ddddocr.DdddOcr(det=False, ocr=False)  # 仅启用滑块检测
with open("bg.png", "wb") as f:
    f.write(bg_response.content)
with open("slider.png", "wb") as f:
    f.write(slider_response.content)

# 识别缺口(传入背景图和滑块图的字节流)
gap_x = slide_det.slide_match(slider_response.content, bg_response.content)
print(f"缺口X坐标:{gap_x}")

# 4. 模拟滑块拖动(关键:模拟人类拖动轨迹,避免匀速直线)
def simulate_slide(driver, slider_elem, target_x):
    """
    模拟滑块拖动轨迹:加速→匀速→减速
    :param slider_elem: 滑块元素
    :param target_x: 目标X坐标(缺口位置 - 滑块初始X坐标)
    """
    action = ActionChains(driver)
    # 按住滑块
    action.click_and_hold(slider_elem).perform()
    time.sleep(0.2)
    
    # 拖动轨迹:分3段,模拟加速度
    track = []
    current_x = 0
    # 加速阶段(前30%距离)
    while current_x < target_x * 0.3:
        step = int(current_x * 0.1 + 2)  # 加速度递增
        track.append(step)
        current_x += step
    # 匀速阶段(中间50%距离)
    while current_x < target_x * 0.8:
        step = 5  # 匀速
        track.append(step)
        current_x += step
    # 减速阶段(最后20%距离)
    while current_x < target_x:
        step = max(int((target_x - current_x) * 0.5), 1)  # 减速度递增
        track.append(step)
        current_x += step
    
    # 执行拖动
    for step in track:
        action.move_by_offset(xoffset=step, yoffset=0).perform()
        time.sleep(random.uniform(0.01, 0.03))  # 每步间隔随机
    
    # 松开滑块
    action.release().perform()

# 找到滑块元素(根据实际网页调整选择器)
slider_elem = driver.find_element(By.CLASS_NAME, "slider-btn")
# 计算实际需要拖动的距离(缺口X坐标 - 滑块初始位置X坐标)
slider_init_x = slider_elem.location["x"]
actual_target_x = gap_x - slider_init_x - 10  # 减10是校准值(根据实际情况调整)

# 执行滑块拖动
simulate_slide(driver, slider_elem, actual_target_x)
time.sleep(2)

# 验证是否通过
if "验证成功" in driver.page_source:
    print("滑块验证码验证成功!")
else:
    print("验证失败,重试...")

driver.quit()
关键注意事项:
  • 图片尺寸校准:若网页中图片被缩放(如CSS设置width: 300px),需将识别到的gap_x按比例转换为实际网页中的坐标;
  • 轨迹模拟:匀速拖动容易被检测为机器人,必须模拟“加速→匀速→减速”的人类轨迹;
  • 重试机制:识别失败时(如缺口位置偏差),需刷新验证码重新识别,建议设置3次重试。

场景3:点选验证码识别(文字/图标点选)

点选验证码(如“按顺序点击汉字‘我、爱、中、国’”“点击所有汽车图标”)是更复杂的反爬手段,ddddocr 提供 PointDetector 类,支持文字点选和图标点选的坐标识别。

实战步骤:
  1. 获取点选验证码图片和提示文字(如“点击‘山、水、日’”);
  2. 用 ddddocr 识别提示文字对应的坐标(按顺序);
  3. 用 Selenium 模拟鼠标点击坐标(按顺序点击,避免点击过快)。
代码示例(文字点选验证码识别):
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import ddddocr
import requests
from PIL import Image
from io import BytesIO
import time

# 初始化Selenium
driver = webdriver.Chrome()
driver.get("https://example.com/point-captcha")  # 点选验证码页面
time.sleep(2)

# 1. 提取验证码图片、提示文字(根据实际网页调整)
captcha_img_elem = driver.find_element(By.CLASS_NAME, "point-captcha-img")
prompt_text = driver.find_element(By.CLASS_NAME, "prompt").text  # 提示文字:如“请按顺序点击 山、水、日”
print(f"点选提示:{prompt_text}")

# 2. 下载验证码图片(携带Cookie)
session = requests.Session()
for cookie in driver.get_cookies():
    session.cookies.set(cookie['name'], cookie['value'])

captcha_img_url = captcha_img_elem.get_attribute("src")
captcha_response = session.get(captcha_img_url)
captcha_img = Image.open(BytesIO(captcha_response.content))
# 保存图片用于调试
with open("point_captcha.png", "wb") as f:
    f.write(captcha_response.content)

# 3. 用ddddocr识别目标文字的坐标(按提示文字顺序)
# 提取提示文字中的目标(如“山、水、日”→ ["山", "水", "日"])
target_words = [word.strip() for word in prompt_text.split("点击")[-1].split("、") if word.strip()]
print(f"目标文字:{target_words}")

# 初始化点选检测器
point_det = ddddocr.DdddOcr(det=True, ocr=True)  # det=True启用目标检测,ocr=True启用文字识别
# 识别图片中所有文字的坐标(返回字典:{文字: (x, y)})
word_coords = point_det.point_word(captcha_response.content)
print(f"识别到的文字坐标:{word_coords}")

# 按提示顺序整理坐标
target_coords = []
for word in target_words:
    if word in word_coords:
        target_coords.append(word_coords[word])
    else:
        print(f"未识别到文字:{word}")

if len(target_coords) != len(target_words):
    print("点选目标识别不完整,重试...")
    driver.quit()

# 4. 模拟鼠标点击(按顺序点击,每个点击间隔0.5-1秒)
# 注意:需将图片坐标转换为网页中的实际坐标(考虑图片在网页中的位置和缩放)
img_location = captcha_img_elem.location  # 图片在网页中的左上角坐标
img_size = captcha_img_elem.size  # 图片在网页中的显示尺寸
img_actual_size = captcha_img.size  # 图片实际尺寸(像素)

# 坐标转换:图片像素坐标 → 网页实际坐标
def convert_coords(img_x, img_y):
    # 计算缩放比例
    scale_x = img_size["width"] / img_actual_size[0]
    scale_y = img_size["height"] / img_actual_size[1]
    # 转换后的网页坐标(加上图片左上角偏移)
    web_x = img_location["x"] + img_x * scale_x
    web_y = img_location["y"] + img_y * scale_y
    return (web_x, web_y)

action = ActionChains(driver)
for idx, (x, y) in enumerate(target_coords):
    web_x, web_y = convert_coords(x, y)
    print(f"第{idx+1}次点击坐标:({web_x}, {web_y})")
    # 移动到目标坐标并点击
    action.move_by_offset(xoffset=web_x, yoffset=web_y).click().perform()
    # 重置鼠标位置(避免后续点击偏移)
    action.reset_actions()
    time.sleep(random.uniform(0.5, 1.0))  # 模拟人类点击间隔

# 提交验证(若有提交按钮)
driver.find_element(By.CLASS_NAME, "submit-btn").click()
time.sleep(2)

if "验证成功" in driver.page_source:
    print("点选验证码验证成功!")
else:
    print("验证失败,重试...")

driver.quit()
图标点选适配:

若点选验证码是图标(如“点击所有动物”),需使用 point_det.point_icon() 方法,传入图标模板图片(需提前准备图标样本),示例:

# 图标点选:识别图片中所有“动物”图标坐标
# 提前准备动物图标模板(template.png)
with open("template.png", "rb") as f:
    template_bytes = f.read()

# 识别所有匹配模板的图标坐标
icon_coords = point_det.point_icon(captcha_response.content, template_bytes)
print(f"识别到的图标坐标:{icon_coords}")  # 输出:[(x1,y1), (x2,y2), ...]

四、爬虫集成:完整案例(登录+验证码自动识别)

以下是一个完整的爬虫案例,集成“文字验证码识别+登录+数据爬取”,全程自动处理验证码:

import requests
import ddddocr
from PIL import Image
from io import BytesIO
import csv
import time
import random
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

# 初始化OCR和Session
ocr = ddddocr.DdddOcr(show_ad=False)
session = requests.Session()
# 配置请求重试
retry = Retry(total=3, backoff_factor=1, status_forcelist=[403, 500, 502, 503])
session.mount('https://', HTTPAdapter(max_retries=retry))

# 目标网站配置
LOGIN_URL = "https://example.com/login"
CAPTCHA_URL = "https://example.com/captcha"
DATA_URL = "https://example.com/data"  # 需要登录后爬取的数据页
USERNAME = "你的账号"
PASSWORD = "你的密码"

def get_captcha():
    """获取并识别文字验证码"""
    for _ in range(3):  # 最多重试3次
        try:
            # 获取验证码图片
            response = session.get(CAPTCHA_URL, headers=headers, timeout=10)
            # 图片预处理(提升识别率)
            img = Image.open(BytesIO(response.content)).convert("L")
            threshold = 130
            img = img.point(lambda p: p > threshold and 255)
            # 识别
            img_byte_arr = BytesIO()
            img.save(img_byte_arr, format='PNG')
            captcha = ocr.classification(img_byte_arr.getvalue())
            if len(captcha) == 4:  # 假设验证码是4位
                return captcha
            print(f"验证码识别长度错误:{captcha},重试...")
            time.sleep(1)
        except Exception as e:
            print(f"获取验证码失败:{e}")
            time.sleep(1)
    raise Exception("验证码识别失败,已重试3次")

def login():
    """登录(自动处理验证码)"""
    captcha = get_captcha()
    print(f"识别的验证码:{captcha}")
    login_data = {
        "username": USERNAME,
        "password": PASSWORD,
        "captcha": captcha
    }
    response = session.post(LOGIN_URL, data=login_data, headers=headers, timeout=10)
    if "登录成功" in response.text:
        print("登录成功!")
        return True
    else:
        print("登录失败,验证码可能错误,重试...")
        return False

def crawl_data():
    """爬取登录后的数据"""
    response = session.get(DATA_URL, headers=headers, timeout=10)
    # 解析数据(根据实际网页结构调整)
    from bs4 import BeautifulSoup
    soup = BeautifulSoup(response.text, 'lxml')
    data_list = soup.find_all('div', class_='data-item')
    # 保存到CSV
    with open('data.csv', 'w', newline='', encoding='utf-8-sig') as f:
        writer = csv.DictWriter(f, fieldnames=['title', 'content'])
        writer.writeheader()
        for item in data_list:
            title = item.find('h3').get_text(strip=True) if item.find('h3') else ''
            content = item.find('p').get_text(strip=True) if item.find('p') else ''
            writer.writerow({'title': title, 'content': content})
    print(f"爬取完成,共{len(data_list)}条数据")

if __name__ == "__main__":
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36",
        "Referer": LOGIN_URL
    }
    # 登录重试(最多3次)
    login_success = False
    for _ in range(3):
        if login():
            login_success = True
            break
        time.sleep(2)
    if login_success:
        crawl_data()
    else:
        print("登录失败,程序退出")

五、识别率优化技巧

  1. 图片预处理
    • 文字验证码:灰度化、二值化、降噪(用PIL的ImageFilter);
    • 滑块/点选验证码:裁剪无关区域(如去除边框、提示文字区域),只保留验证核心部分。
  2. 模型选择
    • 文字验证码:若识别率低,可使用 ocr = ddddocr.DdddOcr(show_ad=False, use_gpu=True, beta=True)beta=True启用更精准的beta模型);
    • 点选验证码:若图标识别率低,可提供高质量的图标模板(建议尺寸与验证码中图标一致)。
  3. 重试机制
    • 验证码识别失败时,自动刷新验证码重新识别(建议3次重试上限);
    • 登录失败后,判断是否因验证码错误导致,触发重新识别。
  4. 避免频繁请求
    • 每次获取验证码间隔1-2秒,避免被网站判定为恶意请求;
    • 识别失败后,适当延长间隔(如2-3秒)。

六、注意事项与局限性

1. 局限性

  • 复杂验证码识别率有限:如扭曲严重的文字、模糊的图标、3D滑块,识别率可能低于90%;
  • 无训练接口:无法自定义训练模型,若目标验证码是特殊类型(如自定义图标),识别效果可能不佳;
  • 依赖图片质量:若验证码图片被压缩、拉伸严重,识别率会下降。

2. 合规与伦理

  • 仅用于合法爬虫:遵守目标网站的robots.txt协议,不得用于恶意爬取、数据泄露等非法行为;
  • 控制爬取频率:避免给目标服务器造成压力,否则可能面临法律风险;
  • 尊重网站反爬机制:验证码是网站的安全措施,若需大规模爬取,建议联系网站获取API授权。

七、总结

用ddddocr实现验证码识别,无需付费打码平台,本地识别速度快、隐私安全,完美适配爬虫开发中的三大核心验证码场景。核心优势在于“零成本+易集成”:

  1. 文字验证码:一行代码识别,配合图片预处理,识别率可达95%以上;
  2. 滑块验证码:缺口检测精准,配合轨迹模拟,通过率高;
  3. 点选验证码:支持文字/图标点选,满足复杂反爬场景。

适合个人开发者、小型项目使用,若需更高识别率(如企业级爬虫),可结合自定义训练模型(如YOLO+OCR)进一步优化。但对大多数爬虫场景而言,ddddocr已能满足需求,是替代付费打码平台的最佳选择。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐