requestsで画像認証コードを自動認識する実装例

Webサイトへの自動ログインやスクレイピングを行う際、ログインフォームに画像認証コード(CAPTCHA)が要求されるケースが多いです。社内システムのテストであれば、開発チームに検証用の固定コードを発行してもらうことも可能ですが、外部サイトでは自前で画像を認識する必要があります。

そのような場面で有効なのが、画像認識をクラウドで提供するサービスを利用する方法です。代表的なサービスとして以下の2つがあります。

  • 图鉴(Tujian):http://www.ttshitu.com/
  • 超级鹰(Chaojiying):https://www.chaojiying.com/

以下では、requestslxmlを組み合わせて、图鉴のAPIを呼び出し、ログインページに表示された画像認証コードのテキストを取得する例を紹介します。画像の文字種は「4桁の英数字混在」に該当するため、APIのtypeidには3を指定します。

1. 画像認識用クライアント

APIに画像を送信するには、画像をBase64エンコードしてJSONでPOSTします。以下のように汎用の関数を用意します。

import base64
import json
from pathlib import Path
from typing import Optional
import requests

API_ENDPOINT = "http://api.ttshitu.com/predict"

def solve_captcha(
    username: str,
    password: str,
    image_path: str,
    type_id: int = 3,
) -> Optional[str]:
    image_bytes = Path(image_path).read_bytes()
    encoded = base64.b64encode(image_bytes).decode("ascii")

    payload = {
        "username": username,
        "password": password,
        "typeid": type_id,
        "image": encoded,
    }

    response = requests.post(API_ENDPOINT, json=payload, timeout=30)
    response.raise_for_status()
    result = response.json()

    if result.get("success"):
        return result["data"]["result"]
    return result.get("message", "")

2. ログインページから画像を取得して認識

古詩文網のログインページを例に、画像URLを取得・保存・認識する流れです。同じセッションを使うことで、ページ表示時と画像取得時のCookieを一致させます。

from urllib.parse import urljoin
import requests
from lxml import etree
from captcha_solver import solve_captcha

LOGIN_URL = (
    "https://so.gushiwen.cn/user/login.aspx?"
    "from=http://so.gushiwen.cn/user/collect.aspx"
)
BASE_URL = "https://so.gushiwen.cn/"

HEADERS = {
    "User-Agent": (
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
        "AppleWebKit/537.36 (KHTML, like Gecko) "
        "Chrome/98.0.4758.80 Safari/537.36"
    ),
}

def download_captcha(session: requests.Session, login_url: str) -> str:
    page = session.get(login_url, headers=HEADERS)
    page.raise_for_status()

    tree = etree.HTML(page.text)
    relative_src = tree.xpath('//*[@id="imgCode"]/@src')[0]
    image_url = urljoin(BASE_URL, relative_src)

    image_data = session.get(image_url, headers=HEADERS).content
    saved_path = "captcha.jpg"
    with open(saved_path, "wb") as fp:
        fp.write(image_data)

    return saved_path

def main():
    with requests.Session() as session:
        captcha_path = download_captcha(session, LOGIN_URL)
        recognized = solve_captcha(
            username="your_username",
            password="your_password",
            image_path=captcha_path,
            type_id=3,
        )
        print(recognized)

if __name__ == "__main__":
    main()

この処理により、ログインページに表示された画像認証コードのテキストを取得できます。取得したテキストは、後続のログインリクエストに含めて利用します。

タグ: requests lxml CAPTCHA OCR 图鉴

8月11日 22:51 投稿