captcha-detector for Python
Does this web page have a captcha? The Python version of captcha-detector: the same 69 named captcha and bot-check products plus catch-all rules, the same answers, zero dependencies.
- Check what your scraper got back: pass a
requests,httpx,aiohttp,curl_cffi, Scrapy or urllib response, or the JSON a scraper API sent, and find out whether you got the real page or a captcha, a bot check or a block page. - Every kind: checkbox widgets, invisible/score captchas, puzzles and sliders, proof-of-work, image captchas, math and text questions, full-page bot checks ("Just a moment…", "Press & Hold"), and access-denied pages.
- Real browsers too: Playwright (sync and async) and Selenium, cross-origin frames included, with
visibletelling you whether the captcha is on screen.
pip install captcha-detector
Check a response
import requests
from captcha_detector import detect_response
url = "https://shop.example/product/42"
res = requests.get(url)
r = detect_response(res)
if r.captcha or r.challenge_page or r.blocked:
print("Not the real page:", r.summary) # e.g. "Captcha found: DataDome captcha (slider) [visible]"
else:
parse(res.text)
detect_response() takes:
| Input | Example |
|---|---|
requests / httpx / curl_cffi / niquests response |
detect_response(requests.get(url)) |
| Scrapy response | detect_response(response) inside parse() |
urllib.request / urllib3 response |
detect_response(urlopen(url)) |
aiohttp response |
await detect_response_async(resp) |
HTML as str or bytes |
detect_response(html, url=url) |
A scraper API's JSON (str, dict or list) |
detect_response(api_res.json(), url=target) |
Results from scraper APIs
HTML wrapped in JSON ({"html": …}, {"result": {"content": …}} and the like) is found on its own, along with the status code, headers and URL next to it.
target = "https://shop.example/product/42"
res = requests.get("https://scraper-api.example/v1", params={"url": target, "key": KEY})
r = detect_response(res, url=target)
if r.captcha or r.challenge_page or r.blocked:
... # retry with another proxy or provider, don't parse or store this one
Pass url, the page you asked the API to scrape, not the API's own address. Most scraper APIs answer 200 whatever the site did; that's fine, the page itself gives a captcha away. If your API reports the site's real status or headers somewhere else, pass them as status= and headers=.
Check HTML
from captcha_detector import detect_html
r = detect_html(html, url="https://shop.example/", status=403, headers={"server": "cloudflare"})
In a real browser
Raw HTML only shows what the server sent, and can't tell what is on screen. In a browser the detector sees what scripts added and whether the captcha is visible.
from playwright.sync_api import sync_playwright
from captcha_detector import detect_in_page, wait_for_captcha
with sync_playwright() as p:
page = p.chromium.launch().new_page()
response = page.goto("https://www.google.com/recaptcha/api2/demo")
r = detect_in_page(page, response=response) # checks every frame, cross-origin ones too
print(r.summary) # Captcha found: reCAPTCHA (v2-checkbox) [visible]
r = wait_for_captcha(page, timeout=10, visible=True) # wait until one is on screen
Async: await detect_in_page_async(page) and await wait_for_captcha_async(page).
Selenium:
from selenium import webdriver
from captcha_detector import detect_selenium
driver = webdriver.Chrome()
driver.get("https://www.google.com/recaptcha/api2/demo")
r = detect_selenium(driver) # switches into each frame and back
Anything else that runs JavaScript can load captcha_detector.BROWSER_SCRIPT (it defines window.CaptchaDetector) or evaluate captcha_detector.expression(), then convert the result with captcha_detector.from_js().
The result
A dict (so json.dumps works) whose keys can also be read as attributes:
{
"captcha": True, # a captcha or bot check is on the page (shown, invisible or not rendered yet)
"visible": None, # on screen right now: True/False in a browser, None for raw HTML
"challenge_page": False, # the whole page is a bot check
"blocked": False, # an access-denied page
"detections": [{
"id": "recaptcha", "name": "reCAPTCHA", "vendor": "Google",
"kind": "widget", # widget | invisible | image | question | challenge-page | block-page | protection
"subtype": "v2-checkbox", "confidence": 0.99, "visible": None,
"evidence": [{"type": "scripts", "value": "https://www.google.com/recaptcha/api.js"},
{"type": "elements", "value": ".g-recaptcha"}],
"site": "https://developers.google.com/recaptcha",
}],
"summary": "Captcha found: reCAPTCHA (v2-checkbox)",
"url": "https://shop.example/", "mode": "html", "version": "1.0.1",
}
kind: "protection" means a bot-protection service is loaded (DataDome, Akamai, PerimeterX…) but no captcha is shown; it does not set captcha. When the reCAPTCHA or hCaptcha image challenge is open, details["challenge_open"] is True.
Options for every function: min_confidence (0..1, default 0.5) and signatures (your own list; SIGNATURES + mine adds to the built-in ones).
Command line
captcha-detector https://www.google.com/recaptcha/api2/demo
captcha-detector https://example.com --browser --json # needs: pip install playwright
curl -s https://example.com | captcha-detector -
curl -s "https://scraper-api.example/v1?url=..." | captcha-detector -
Options: --browser, --headful, --wait MS, --json, --fail (exit code 1 when a captcha is found), --min 0..1. Also python -m captcha_detector.
Add your own signature
from captcha_detector import SIGNATURES, detect_html
acme = {
"id": "acme-check", "name": "ACME bot check", "kind": "challenge-page",
"title": [(r"^acme says hold on", 0.9)], # regex, or (regex, weight)
"scripts": [r"cdn\.acme-shield\.example/"],
"elements": ["#acme-check", {"sel": "form", "attr": ["action"], "re": r"/acme/verify", "w": 0.9}],
"refine": lambda d, x: d if x.short else None, # optional: adjust or drop a match
}
r = detect_html(html, signatures=SIGNATURES + [acme])
Matchers: scripts, frames, elements (CSS: tag, #id, .class, [attr op "v" i], :not()), inline (inline script text), urls, title, text (short pages only, unless (re, w, "any")), cookies, headers ((name, re, w)), status. Matches combine into a confidence; the default threshold is 0.5.
To add a product for everyone, add it to src/signatures.js in the JavaScript repository: the JavaScript and Python packages share one signature database.
How it stays in step with the JavaScript package
The signature database (signatures.json), the browser script (captcha-detector.js) and the test fixtures (tests/fixtures.json) are generated from the JavaScript repository, and the test suite checks that the Python engine gives exactly the same result as the JavaScript engine on every test page. captcha_detector.SIGNATURES_VERSION is the JavaScript version they came from.
To work on both, clone the two repositories side by side:
git clone https://github.com/ScrapePass/captcha-detector
git clone https://github.com/ScrapePass/captcha-detector-python
cd captcha-detector && npm install && npm run test:python # rebuilds, exports to ../captcha-detector-python, runs its tests
A signature with a refine() step in JavaScript needs the same step in src/captcha_detector/refine.py.
Tests on their own: PYTHONPATH=src python3 -m unittest discover -s tests -t .
License
MIT
Metadata
Release files for captcha-detector 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| captcha_detector-1.0.1.tar.gz | 70.6 kB | Without provenance | Details for captcha_detector-1.0.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| captcha_detector-1.0.1-py3-none-any.whl | Python 3 | none | any | Details for captcha_detector-1.0.1-py3-none-any.whl |
Total release size: 133.5 kB
Release files / captcha_detector-1.0.1.tar.gz
| Download URL | captcha_detector-1.0.1.tar.gz |
|---|---|
| Size | 70.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6a1346fb602a7c325c3251cd19cc91edcc1cc6fb6e52ab2b559179ae2cae3d5e
|
|
BLAKE2b-256 checksum How to use checksums |
86ca4da7f8de4cc1df5e73e1781d558f30e5e37e087359e224d9908fd83c73ce
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.13
|
Release files / captcha_detector-1.0.1-py3-none-any.whl
| Download URL | captcha_detector-1.0.1-py3-none-any.whl |
|---|---|
| Size | 62.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7da3810acf8e0e09cf9b0ec3053d90ad6ab0fd7c9fc840373e7b77a95d3183ef
|
|
BLAKE2b-256 checksum How to use checksums |
a1e42ed4d984b7b0c1ec8d9234028ba016a6e46639473b20bfb526666ca6aa39
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.13
|
Security for captcha-detector 1.0.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.