OCR 识别图形验证码

从基础预处理到 PaddleOCR/Selenium 4 自动化全流程(2024 版)


1. 验证码技术概述

随着网络安全意识的提升,各类网站的反爬虫体系迭代越来越快,验证码(CAPTCHA) 作为人机鉴别的第一道防线,技术形态早已跳出纯字符组合的范畴:

  1. 初代基础款:纯数字/字母、大小写混排,无明显干扰
  2. 轻量增强款:加入倾斜、旋转、干扰点线、随机背景
  3. 中文本地化款:使用常用汉字或生僻字,甚至加入成语、语义干扰
  4. 行为复杂款:如 12306 点选同类物体、点击文字补全诗句
  5. AI 辅助交互款:滑动拼图(含缺口检测)、轨迹识别验证

本教程聚焦前三种纯静态图形验证码的通用识别方案,滑动、点选等交互类验证码将另行分享。


2. 图形验证码识别技术基础

2.1 OCR 技术选型参考

OCR(光学字符识别)的核心是将图像中的视觉文字转换为可编辑文本。2024 年做轻量级爬虫,无需从零训练模型,以下方案按易用性、准确率、成本排序即可:

方案类型代表工具/API适用场景2024 现状
开源传统引擎Tesseract 5.x+纯基础、干扰极少的验证码适合练手,准确率波动较大
开源深度学习库PaddleOCR 2.x+轻量增强/中文本地化验证码国内用户首选,准确率高
商业 API百度/腾讯/阿里云 OCR轻量/中等难度验证码批量处理准确率最高,有免费额度

2.2 轻量级开发环境准备

Python 3.9+ 是目前爬虫与 OCR 最稳定的组合,依赖按需安装:

通用依赖(必装)

pip install selenium pillow numpy opencv-python

方案一:Tesseract 练手依赖

# Python 封装库
pip install pytesseract

# 安装 Tesseract 引擎(不同系统命令不同)
# Windows(需先安装 Chocolatey)
choco install tesseract
# macOS(需先安装 Homebrew)
brew install tesseract
# Linux(Debian/Ubuntu)
sudo apt install tesseract-ocr

方案二:PaddleOCR 实战推荐依赖

# CPU 版(适合个人电脑)
pip install paddleocr paddlepaddle

# GPU 版(需匹配 CUDA 版本,大批量处理推荐)
# 参考:https://www.paddlepaddle.org.cn/install/quick

3. 验证码识别全流程实战

3.1 热身:直接用 Tesseract 识别基础款

基础款验证码几乎没有干扰,无需预处理即可识别,但对倾斜、颜色等容忍度低:

import pytesseract
from PIL import Image

def tesseract_basic(image_path: str) -> str:
    """直接使用 Tesseract 识别基础款验证码"""
    img = Image.open(image_path)
    # 配置:单块文本识别,限定字母数字白名单
    config = (
        r"--psm 7 --oem 3 "
        r"-c tessedit_char_whitelist="
        r"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
    )
    return pytesseract.image_to_string(img, config=config).strip()

# 测试
if __name__ == "__main__":
    print(tesseract_basic("simple_captcha.png"))

提示--psm 7 将图像视为单行文本;whitelist 限制识别字符集,可有效减少误识别。

3.2 关键步骤:OpenCV 验证码预处理

轻量增强款验证码的主要干扰来自背景噪点、干扰线和颜色混乱。预处理的目标是将文字与背景彻底分离,只保留干净的黑白文字轮廓。

import cv2
import numpy as np

def opencv_preprocess(image_path: str) -> np.ndarray:
    """轻量增强款验证码的通用预处理流程"""
    # 1. 读取原始图像
    img = cv2.imread(image_path)

    # 2. 灰度化 —— 降低计算量,突出明暗对比
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 3. 自适应阈值二值化 —— 适用背景渐变的情况
    binary = cv2.adaptiveThreshold(
        gray, 255,
        cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
        cv2.THRESH_BINARY_INV,  # 反色,让文字变为白色(255)
        11,   # 邻域大小(奇数,11 是常用经验值)
        2     # 偏移量,控制阈值灵敏度
    )

    # 4. 中值滤波去噪 —— 清除椒盐噪点、断掉的干扰线
    denoised = cv2.medianBlur(binary, 3)

    # 5. 形态学闭操作 —— 填补文字内部小缺口
    kernel = np.ones((2, 2), np.uint8)
    processed = cv2.morphologyEx(denoised, cv2.MORPH_CLOSE, kernel)

    # 调试时可保存结果查看
    # cv2.imwrite("processed.png", processed)
    return processed

3.3 2024 实战首选:PaddleOCR 识别带预处理的验证码

PaddleOCR 内置中英文预训练模型,配合预处理后对常见干扰的鲁棒性极强,准确率可达 90% 以上。

from paddleocr import PaddleOCR
import cv2

# 全局初始化(首次运行自动下载轻量模型,约 10 MB)
ocr = PaddleOCR(use_angle_cls=True, lang="en", show_log=False)

def paddle_ocr_processed(processed_img: np.ndarray) -> str:
    """识别预处理后的验证码图像"""
    result = ocr.ocr(processed_img, cls=True)
    if result and result[0]:
        # result[0][0][1] 为 (文本, 置信度)
        text, confidence = result[0][0][1]
        # 可选:过滤低置信度结果
        # if confidence < 0.8:
        #     return ""
        return text.strip()
    return ""

def full_paddle_ocr(image_path: str) -> str:
    """组合预处理与识别"""
    processed = opencv_preprocess(image_path)
    return paddle_ocr_processed(processed)

if __name__ == "__main__":
    print(full_paddle_ocr("enhanced_captcha.png"))

lang 参数说明"en" 优先英文识别;"ch" 中英文混合识别,更适合中文验证码场景。


4. 自动化登录实战(Selenium 4 + PaddleOCR)

以「scrape.center」的模拟登录验证码网站为例,将整个流程串联成一个完整的自动化脚本。

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
import time
import random
import cv2
import numpy as np
from paddleocr import PaddleOCR

# ---------- 复用前面的预处理与 OCR 函数 ----------
ocr = PaddleOCR(use_angle_cls=True, lang="en", show_log=False)

def opencv_preprocess(image_path: str) -> np.ndarray:
    img = cv2.imread(image_path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    binary = cv2.adaptiveThreshold(
        gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
        cv2.THRESH_BINARY_INV, 11, 2
    )
    denoised = cv2.medianBlur(binary, 3)
    kernel = np.ones((2, 2), np.uint8)
    return cv2.morphologyEx(denoised, cv2.MORPH_CLOSE, kernel)

def paddle_ocr_processed(processed_img: np.ndarray) -> str:
    result = ocr.ocr(processed_img, cls=True)
    if result and result[0]:
        text, _ = result[0][0][1]
        return text.strip()
    return ""

def full_paddle_ocr(image_path: str) -> str:
    return paddle_ocr_processed(opencv_preprocess(image_path))

# ---------- 自动化登录 ----------
def auto_login_scrape_center():
    # 1. 初始化浏览器并伪装基础指纹
    options = webdriver.ChromeOptions()
    options.add_argument(
        "user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
    )
    options.add_experimental_option("excludeSwitches", ["enable-automation"])
    options.add_experimental_option("useAutomationExtension", False)

    service = webdriver.ChromeService()
    driver = webdriver.Chrome(service=service, options=options)

    try:
        # 2. 打开目标网站
        driver.get("https://captcha7.scrape.center/")
        time.sleep(random.uniform(1, 2))

        # 3. 输入用户名和密码
        username = WebDriverWait(driver, 10).until(
            EC.presence_of_element_located((By.NAME, "username"))
        )
        username.send_keys("admin")
        time.sleep(random.uniform(0.5, 1))

        password = driver.find_element(By.NAME, "password")
        password.send_keys("admin")
        time.sleep(random.uniform(0.5, 1))

        # 4. 截取验证码图片并识别
        captcha_img = driver.find_element(By.CSS_SELECTOR, ".captcha img")
        captcha_img.screenshot("current_captcha.png")  # 元素级截图
        code = full_paddle_ocr("current_captcha.png")
        print(f"识别到的验证码:{code}")

        # 5. 填入验证码并提交
        captcha_input = driver.find_element(By.NAME, "captcha")
        captcha_input.send_keys(code)
        time.sleep(random.uniform(0.5, 1))

        submit_btn = driver.find_element(By.CSS_SELECTOR, "button[type='submit']")
        submit_btn.click()

        # 6. 验证登录结果
        WebDriverWait(driver, 10).until(
            EC.text_to_be_present_in_element((By.TAG_NAME, "h2"), "登录成功")
        )
        print("🎉 登录成功!")

    finally:
        time.sleep(2)
        driver.quit()

if __name__ == "__main__":
    auto_login_scrape_center()

关键技巧:使用 element.screenshot() 直接截取验证码元素,避免整页截图再裁剪,提高稳定性。


5. 轻量级反反爬技巧(2024 更新)

识别验证码只是第一步,还需配合以下策略降低风控风险:

  1. 随机延迟
    所有操作前后加入 random.uniform(a, b) 的随机等待,避免毫秒级执行。

  2. IP 轮换
    频繁请求时使用代理池(推荐付费代理,免费可用性极低):

    options.add_argument('--proxy-server=http://your_proxy_ip:port')
  3. 验证码错误重试
    PaddleOCR 并非 100% 准确,增加 3~5 次识别-提交循环,失败后刷新验证码重试。

  4. 控制请求频率
    批量爬取时,将请求频率控制在每分钟 10~20 次以内,避免触发风控。


6. 总结与展望

本教程分享了 2024 年轻量级爬虫识别静态图形验证码的全流程实践:

  • OpenCV 预处理分离文字与噪声
  • PaddleOCR 预训练模型完成高精度识别
  • Selenium 4 实现自动化登录

这套组合足以应对大多数网站的轻量增强与中文验证码。

若遇到极度扭曲、密集生僻字等复杂验证码,可尝试:

  • 调用百度、腾讯等商业 API 获取更高准确率
  • 收集该站验证码样本,训练专用的小数据集模型

参考资料