wulu-geetest-bypass-phrase
Offline phrase-type solver for Geetest v4 CAPTCHAs. The challenge image shows four single characters that have to be clicked in the order that spells a phrase; the solver returns the four click coordinates.
Works either way: as a plugin of
wulu-geetest-bypass, which adds
click-track generation and submission, or standalone — the package exports
solve_phrase() and imports nothing from the main package.
How it works
detect.onnx— a single-class YOLO model locates the four character boxes.- RapidOCR reads one CJK character from each box.
order.onnxscores every character at every one of the four positions; the 24 permutations are ranked by total score and the best one wins.
The characters are read from the challenge image itself, so unlike icon/word
the solver needs no question thumbnails.
Ordering
The click order is resolved in three tiers:
dict.json→table— answers verified online. Skipped when the four characters contain a duplicate, because a sorted key cannot say which box a repeated character sits in.dict.json→pending— hand-curated hints, hoisted to the front of the model ranking.order.onnx— the best-scoring permutation, with thedict.json→triedpermutations (already known to be wrong) filtered out.
If locating or recognition does not yield exactly four characters, solve_phrase
returns an empty list rather than guessing.
Accuracy
Measured 2026-10-15 on the public Geetest demo site
(54088bb07d2df3c46b79f80300b0abbe), one first guess each, no retries:
| Pipeline | Samples | First-guess pass rate |
|---|---|---|
Full — table dictionary, then order.onnx |
50 challenges | 49/50 = 98.0% |
order.onnx alone — dictionary disabled |
151 character sets | 146/151 = 96.7% |
Reproduce the first row with pytest tests/test_phrase.py -m accuracy.
Performance
Single process, CPU (CPUExecutionProvider), 40-image corpus. Locate and
recognize are timed per image, ordering per character set:
| Stage | Median | p95 |
|---|---|---|
| locate | 9.3 ms | 11.6 ms |
| recognize | 36.6 ms | 42.1 ms |
| order (dictionary hit) | 0.0 ms | 0.0 ms |
order (order.onnx) |
0.7 ms | 0.7 ms |
| end to end | 45.5 ms | 53.8 ms |
Recognition dominates. The tiered lookup is not a speed optimisation — both tiers cost the same — it is there to raise accuracy.
Model
| File | Size | Shape |
|---|---|---|
detect.onnx |
9.3 MiB | in [1, 3, 320, 320], out [1, 5, 2100] |
order.onnx |
12.4 MiB | in input_ids / attention_mask, out [batch, seq, 4] |
tokenizer.json |
0.4 MiB | tokenizer for order.onnx |
dict.json |
20 KiB | table / tried / pending |
order.onnx is a fine-tune of
uer/chinese_roberta_L-2_H-128
(Chinese RoBERTa Miniatures, pre-trained by
UER-py): a 4-way scoring head replaces the
masked-LM head, and tokenizer.json is built from the same 21128-token WordPiece
vocabulary. The upstream model card declares no license — see
THIRD_PARTY_NOTICES.md.
Download
Everything is fetched on first use into
platformdirs.user_cache_dir('wulu-geetest-bypass', appauthor=False) / 'phrase'
and SHA-256 verified against the registry in hub.py.
hub.py also runs as a script, which fetches all four files up front instead of
waiting for the first challenge:
python -m wulu_geetest_bypass_phrase.hub
If huggingface.co is unreachable, point HF_ENDPOINT at a mirror — the same
variable huggingface_hub reads, so anything already configured for it works
here too:
export HF_ENDPOINT=https://hf-mirror.com
HF_ENDPOINT is read when the package is first imported, so set it before the
solver runs.
Usage
As a plugin of the main package:
uv add wulu-geetest-bypass[phrase]
The solver registers as an entry point of the wulu_geetest_bypass.solvers
group under the phrase captcha type, and is discovered automatically by
wulu_geetest_bypass.
from wulu_geetest_bypass import Geetest
solver = Geetest(captcha_id='your_captcha_id', risk_type='phrase')
seccode = await solver.resolve()
Or standalone:
pip install wulu-geetest-bypass-phrase
from wulu_geetest_bypass_phrase import solve_phrase
clicks = solve_phrase(image_bytes) # [(x, y), ...] in 0~1, in click order
Metadata
Release files for wulu-geetest-bypass-phrase 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| wulu_geetest_bypass_phrase-0.1.0.tar.gz | 7.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| wulu_geetest_bypass_phrase-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 16.4 kB
Release files / wulu_geetest_bypass_phrase-0.1.0.tar.gz
| Download URL | wulu_geetest_bypass_phrase-0.1.0.tar.gz |
|---|---|
| Size | 7.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d20d1ce56479bc6a3fa95d811d2d50fc730d18765a8d260a092a632e9ecd6145
|
|
BLAKE2b-256 checksum How to use checksums |
e7632472fc1caaa6c4b24e8a4426744dc93e216519c007290b75788422c39cc9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / wulu_geetest_bypass_phrase-0.1.0-py3-none-any.whl
| Download URL | wulu_geetest_bypass_phrase-0.1.0-py3-none-any.whl |
|---|---|
| Size | 8.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
511fa72c1720645ba33048468abd10982ba8435a30bbfcc17dbf7d50f613103a
|
|
BLAKE2b-256 checksum How to use checksums |
24022c7ef155bdb39ea2482d7d01e52d601b6c43907d6088e09f6fc7b1a392ca
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|