Webサイトへの自動ログインやスクレイピングを行う際、ログインフォームに画像認証コード(CAPTCHA)が要求されるケースが多いです。社内システムのテストであれば、開発チームに検証用の固定コードを発行してもらうことも可能ですが、外部サイトでは自前で画像を認識する必要があります。
そのような場面で有効なのが、画像認識をクラウドで提供するサービスを利用する方法です。代表的なサービスとして以下の2つがあります。
- 图鉴(Tujian):http://www.ttshitu.com/
- 超级鹰(Chaojiying):https://www.chaojiying.com/
以下では、requestsとlxmlを組み合わせて、图鉴の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()
この処理により、ログインページに表示された画像認証コードのテキストを取得できます。取得したテキストは、後続のログインリクエストに含めて利用します。