python-rucaptcha 6.4.0__tar.gz → 6.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- python_rucaptcha-6.6.0/PKG-INFO +194 -0
- python_rucaptcha-6.6.0/README.md +160 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/pyproject.toml +7 -2
- python_rucaptcha-6.6.0/src/python_rucaptcha/__version__.py +1 -0
- python_rucaptcha-6.6.0/src/python_rucaptcha/altcha_captcha.py +152 -0
- python_rucaptcha-6.6.0/src/python_rucaptcha/binance_captcha.py +155 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/core/base.py +49 -27
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/core/config.py +3 -1
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/core/enums.py +29 -0
- python_rucaptcha-6.6.0/src/python_rucaptcha/core/result_handler.py +115 -0
- python_rucaptcha-6.6.0/src/python_rucaptcha/core/serializer.py +98 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/image_captcha.py +5 -5
- python_rucaptcha-6.6.0/src/python_rucaptcha/temu_captcha.py +152 -0
- python_rucaptcha-6.6.0/src/python_rucaptcha/vk_captcha.py +225 -0
- python_rucaptcha-6.6.0/src/python_rucaptcha/yandex_smart_captcha.py +246 -0
- python_rucaptcha-6.6.0/src/python_rucaptcha/yidun_captcha.py +171 -0
- python_rucaptcha-6.6.0/src/python_rucaptcha.egg-info/PKG-INFO +194 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha.egg-info/SOURCES.txt +12 -1
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha.egg-info/requires.txt +1 -1
- python_rucaptcha-6.6.0/tests/test_altcha.py +185 -0
- python_rucaptcha-6.6.0/tests/test_binance.py +197 -0
- python_rucaptcha-6.6.0/tests/test_vk_captcha.py +175 -0
- python_rucaptcha-6.6.0/tests/test_yandex_smart_captcha.py +236 -0
- python_rucaptcha-6.6.0/tests/test_yidun_captcha.py +192 -0
- python_rucaptcha-6.4.0/PKG-INFO +0 -107
- python_rucaptcha-6.4.0/README.md +0 -74
- python_rucaptcha-6.4.0/src/python_rucaptcha/__version__.py +0 -1
- python_rucaptcha-6.4.0/src/python_rucaptcha/core/result_handler.py +0 -69
- python_rucaptcha-6.4.0/src/python_rucaptcha/core/serializer.py +0 -74
- python_rucaptcha-6.4.0/src/python_rucaptcha.egg-info/PKG-INFO +0 -107
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/LICENSE +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/MANIFEST.in +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/setup.cfg +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/__init__.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/amazon_waf.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/atb_captcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/audio_captcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/bounding_box_captcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/captcha_fox.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/capy_puzzle.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/control.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/coordinates_captcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/core/__init__.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/cutcaptcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/cyber_siara_captcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/datadome_captcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/draw_around_captcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/friendly_captcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/fun_captcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/gee_test.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/grid_captcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/hcaptcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/key_captcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/lemin_captcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/mt_captcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/prosopo.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/re_captcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/rotate_captcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/tencent.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/text_captcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha/turnstile.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha.egg-info/dependency_links.txt +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/src/python_rucaptcha.egg-info/top_level.txt +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_amazon.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_audio.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_bounding_box.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_capypuzzle.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_control.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_coordinates.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_core.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_cutcaptcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_cybersiara.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_datadome.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_draw_around.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_friendly_captcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_funcaptcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_geetest.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_grid.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_hcaptcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_image.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_key_captcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_lemin.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_mtcaptcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_recaptcha.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_rotate.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_tencent.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_text.py +0 -0
- {python_rucaptcha-6.4.0 → python_rucaptcha-6.6.0}/tests/test_turnstile.py +0 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python-rucaptcha
|
|
3
|
+
Version: 6.6.0
|
|
4
|
+
Summary: Python 3.9+ RuCaptcha library with AIO module.
|
|
5
|
+
Author-email: AndreiDrang <python-captcha@pm.me>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://andreidrang.github.io/python-rucaptcha/
|
|
8
|
+
Project-URL: Documentation, https://andreidrang.github.io/python-rucaptcha/
|
|
9
|
+
Project-URL: Repository, https://github.com/AndreiDrang/python-rucaptcha
|
|
10
|
+
Project-URL: Issues, https://github.com/AndreiDrang/python-rucaptcha/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/AndreiDrang/python-rucaptcha/releases
|
|
12
|
+
Keywords: captcha,rucaptcha,2captcha,deathbycaptcha,recaptcha,geetest,hcaptcha,capypuzzle,rotatecaptcha,funcaptcha,keycaptcha,python3,recaptcha,captcha,security,tencent,atb_captcha,python-library,python-rucaptcha,rucaptcha-client,yandex,turnstile,amazon,amazon_waf,vk-captcha,fox-captcha,temu-captcha,friendly-captcha,binance-captcha
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Programming Language :: Python
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Framework :: AsyncIO
|
|
23
|
+
Classifier: Operating System :: Unix
|
|
24
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
25
|
+
Classifier: Operating System :: MacOS
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: requests>=2.21.0
|
|
30
|
+
Requires-Dist: aiohttp>=3.9.2
|
|
31
|
+
Requires-Dist: msgspec<0.21,>=0.18
|
|
32
|
+
Requires-Dist: tenacity<10,>=8
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# python-rucaptcha
|
|
36
|
+
|
|
37
|
+
[](https://badge.fury.io/py/python-rucaptcha)
|
|
38
|
+
[](https://badge.fury.io/py/python-rucaptcha)
|
|
39
|
+
[](https://pepy.tech/project/python-rucaptcha)
|
|
40
|
+
[](https://andreidrang.github.io/python-rucaptcha/)
|
|
41
|
+
|
|
42
|
+
**Python 3.9+ library to solve CAPTCHAs automatically using RuCaptcha, 2Captcha, or DeathByCaptcha services.**
|
|
43
|
+
|
|
44
|
+
## What is this?
|
|
45
|
+
|
|
46
|
+
This library automates CAPTCHA solving by connecting to third-party services. When your code encounters a CAPTCHA, python-rucaptcha sends it to the service, waits for a human to solve it, and returns the solution to your application.
|
|
47
|
+
|
|
48
|
+
**Supports 30+ CAPTCHA types:**
|
|
49
|
+
- reCAPTCHA v2/v3, hCaptcha, Cloudflare Turnstile
|
|
50
|
+
- Image captchas, Audio captchas
|
|
51
|
+
- GeeTest, KeyCaptcha, Amazon WAF, Tencent
|
|
52
|
+
- And many more...
|
|
53
|
+
|
|
54
|
+
## Quick Start
|
|
55
|
+
|
|
56
|
+
### 1. Install
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install python-rucaptcha
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 2. Get an API Key
|
|
63
|
+
|
|
64
|
+
Sign up at [RuCaptcha](https://rucaptcha.com) or [2Captcha](https://2captcha.com), then copy your API key from the dashboard.
|
|
65
|
+
|
|
66
|
+
### 3. Solve a CAPTCHA
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from python_rucaptcha import HCaptcha
|
|
70
|
+
|
|
71
|
+
# Your API key
|
|
72
|
+
key = "your_api_key_here"
|
|
73
|
+
|
|
74
|
+
# Solve hCaptcha
|
|
75
|
+
result = HCaptcha(aptcha_key=key).captcha_handler(site_url="https://example.com", site_key="abc123")
|
|
76
|
+
|
|
77
|
+
if result['code'] == 0:
|
|
78
|
+
print(f"Solved! Token: {result['token']}")
|
|
79
|
+
else:
|
|
80
|
+
print(f"Error: {result['message']}")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Solving Different CAPTCHA Types
|
|
84
|
+
|
|
85
|
+
**reCAPTCHA v2:**
|
|
86
|
+
```python
|
|
87
|
+
from python_rucaptcha import ReCaptcha
|
|
88
|
+
|
|
89
|
+
result = ReCaptcha(api_key).captcha_handler(
|
|
90
|
+
site_url="https://example.com",
|
|
91
|
+
site_key="your_site_key"
|
|
92
|
+
)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Image CAPTCHA:**
|
|
96
|
+
```python
|
|
97
|
+
from python_rucaptcha import ImageCaptcha
|
|
98
|
+
|
|
99
|
+
result = ImageCaptcha(api_key).captcha_handler(
|
|
100
|
+
image_link="https://example.com/captcha.jpg"
|
|
101
|
+
)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**ALTCHA:**
|
|
105
|
+
```python
|
|
106
|
+
from python_rucaptcha import AltchaCaptcha
|
|
107
|
+
from python_rucaptcha.core.enums import AltchaEnm
|
|
108
|
+
|
|
109
|
+
result = AltchaCaptcha(
|
|
110
|
+
rucaptcha_key=api_key,
|
|
111
|
+
websiteURL="https://example.com",
|
|
112
|
+
challengeURL="https://example.com/altcha/challenge",
|
|
113
|
+
method=AltchaEnm.AltchaTaskProxyless,
|
|
114
|
+
).captcha_handler()
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Using async:**
|
|
118
|
+
```python
|
|
119
|
+
import asyncio
|
|
120
|
+
from python_rucaptcha import HCaptcha
|
|
121
|
+
|
|
122
|
+
async def solve():
|
|
123
|
+
result = await HCaptcha(api_key).aio_captcha_handler(
|
|
124
|
+
site_url="https://example.com",
|
|
125
|
+
site_key="abc123"
|
|
126
|
+
)
|
|
127
|
+
return result
|
|
128
|
+
|
|
129
|
+
token = asyncio.run(solve())
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Supported CAPTCHA Types
|
|
133
|
+
|
|
134
|
+
| CAPTCHA | Module | Description |
|
|
135
|
+
|---------|--------|-------------|
|
|
136
|
+
| reCAPTCHA v2/v3 | `ReCaptcha` | Google reCAPTCHA |
|
|
137
|
+
| hCaptcha | `HCaptcha` | hCaptcha challenge |
|
|
138
|
+
| Cloudflare Turnstile | `Turnstile` | Cloudflare protection |
|
|
139
|
+
| Image | `ImageCaptcha` | Type the text from image |
|
|
140
|
+
| Audio | `AudioCaptcha` | Listen and type audio |
|
|
141
|
+
| GeeTest | `GeeTest` | Chinese geetest puzzles |
|
|
142
|
+
| KeyCaptcha | `KeyCaptcha` | KeyCAPTCHA service |
|
|
143
|
+
| Amazon WAF | `AmazonWaf` | AWS WAF challenge |
|
|
144
|
+
| ALTCHA | `AltchaCaptcha` | ALTCHA challenge |
|
|
145
|
+
| Binance | `BinanceCaptcha` | Token-based Binance challenge |
|
|
146
|
+
| Grid | `GridCaptcha` | Select grid cells |
|
|
147
|
+
| Coordinates | `CoordinatesCaptcha` | Click on coordinates |
|
|
148
|
+
| And 20+ more | ... | See [full docs](https://andreidrang.github.io/python-rucaptcha/) |
|
|
149
|
+
|
|
150
|
+
## Switching Services
|
|
151
|
+
|
|
152
|
+
Use the same code with different services:
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
from python_rucaptcha import HCaptcha
|
|
156
|
+
from python_rucaptcha.core.enums import ServiceEnm
|
|
157
|
+
|
|
158
|
+
# Use 2Captcha (default)
|
|
159
|
+
result = HCaptcha("2captcha_key").captcha_handler(...)
|
|
160
|
+
|
|
161
|
+
# Use RuCaptcha
|
|
162
|
+
result = HCaptcha("rucaptcha_key", service_type=ServiceEnm.RuCaptcha).captcha_handler(...)
|
|
163
|
+
|
|
164
|
+
# Use DeathByCaptcha
|
|
165
|
+
result = HCaptcha("dbc_user:dbc_pass", service_type=ServiceEnm.DeathByCaptcha).captcha_handler(...)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Testing
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
# Set your API key
|
|
172
|
+
export RUCAPTCHA_KEY="your_key_here"
|
|
173
|
+
|
|
174
|
+
# Run tests
|
|
175
|
+
make tests
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Documentation
|
|
179
|
+
|
|
180
|
+
For advanced usage, configuration options, and all CAPTCHA types, see the [full documentation](https://andreidrang.github.io/python-rucaptcha/).
|
|
181
|
+
|
|
182
|
+
## Support
|
|
183
|
+
|
|
184
|
+
- **Telegram:** [pythoncaptcha](https://t.me/pythoncaptcha)
|
|
185
|
+
- **Email:** python-captcha@pm.me
|
|
186
|
+
- **Issues:** [GitHub Issues](https://github.com/AndreiDrang/python-rucaptcha/issues)
|
|
187
|
+
|
|
188
|
+
## Changelog
|
|
189
|
+
|
|
190
|
+
See [Releases](https://github.com/AndreiDrang/python-rucaptcha/releases) for full changelog.
|
|
191
|
+
|
|
192
|
+
- **v6.0** - Refactored to use msgspec (faster), API v2, dropped Python 3.8
|
|
193
|
+
- **v5.3** - Added DeathByCaptcha support
|
|
194
|
+
- **v5.2** - Added audio CAPTCHA solving
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# python-rucaptcha
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/py/python-rucaptcha)
|
|
4
|
+
[](https://badge.fury.io/py/python-rucaptcha)
|
|
5
|
+
[](https://pepy.tech/project/python-rucaptcha)
|
|
6
|
+
[](https://andreidrang.github.io/python-rucaptcha/)
|
|
7
|
+
|
|
8
|
+
**Python 3.9+ library to solve CAPTCHAs automatically using RuCaptcha, 2Captcha, or DeathByCaptcha services.**
|
|
9
|
+
|
|
10
|
+
## What is this?
|
|
11
|
+
|
|
12
|
+
This library automates CAPTCHA solving by connecting to third-party services. When your code encounters a CAPTCHA, python-rucaptcha sends it to the service, waits for a human to solve it, and returns the solution to your application.
|
|
13
|
+
|
|
14
|
+
**Supports 30+ CAPTCHA types:**
|
|
15
|
+
- reCAPTCHA v2/v3, hCaptcha, Cloudflare Turnstile
|
|
16
|
+
- Image captchas, Audio captchas
|
|
17
|
+
- GeeTest, KeyCaptcha, Amazon WAF, Tencent
|
|
18
|
+
- And many more...
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
### 1. Install
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install python-rucaptcha
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 2. Get an API Key
|
|
29
|
+
|
|
30
|
+
Sign up at [RuCaptcha](https://rucaptcha.com) or [2Captcha](https://2captcha.com), then copy your API key from the dashboard.
|
|
31
|
+
|
|
32
|
+
### 3. Solve a CAPTCHA
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from python_rucaptcha import HCaptcha
|
|
36
|
+
|
|
37
|
+
# Your API key
|
|
38
|
+
key = "your_api_key_here"
|
|
39
|
+
|
|
40
|
+
# Solve hCaptcha
|
|
41
|
+
result = HCaptcha(aptcha_key=key).captcha_handler(site_url="https://example.com", site_key="abc123")
|
|
42
|
+
|
|
43
|
+
if result['code'] == 0:
|
|
44
|
+
print(f"Solved! Token: {result['token']}")
|
|
45
|
+
else:
|
|
46
|
+
print(f"Error: {result['message']}")
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Solving Different CAPTCHA Types
|
|
50
|
+
|
|
51
|
+
**reCAPTCHA v2:**
|
|
52
|
+
```python
|
|
53
|
+
from python_rucaptcha import ReCaptcha
|
|
54
|
+
|
|
55
|
+
result = ReCaptcha(api_key).captcha_handler(
|
|
56
|
+
site_url="https://example.com",
|
|
57
|
+
site_key="your_site_key"
|
|
58
|
+
)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Image CAPTCHA:**
|
|
62
|
+
```python
|
|
63
|
+
from python_rucaptcha import ImageCaptcha
|
|
64
|
+
|
|
65
|
+
result = ImageCaptcha(api_key).captcha_handler(
|
|
66
|
+
image_link="https://example.com/captcha.jpg"
|
|
67
|
+
)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**ALTCHA:**
|
|
71
|
+
```python
|
|
72
|
+
from python_rucaptcha import AltchaCaptcha
|
|
73
|
+
from python_rucaptcha.core.enums import AltchaEnm
|
|
74
|
+
|
|
75
|
+
result = AltchaCaptcha(
|
|
76
|
+
rucaptcha_key=api_key,
|
|
77
|
+
websiteURL="https://example.com",
|
|
78
|
+
challengeURL="https://example.com/altcha/challenge",
|
|
79
|
+
method=AltchaEnm.AltchaTaskProxyless,
|
|
80
|
+
).captcha_handler()
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Using async:**
|
|
84
|
+
```python
|
|
85
|
+
import asyncio
|
|
86
|
+
from python_rucaptcha import HCaptcha
|
|
87
|
+
|
|
88
|
+
async def solve():
|
|
89
|
+
result = await HCaptcha(api_key).aio_captcha_handler(
|
|
90
|
+
site_url="https://example.com",
|
|
91
|
+
site_key="abc123"
|
|
92
|
+
)
|
|
93
|
+
return result
|
|
94
|
+
|
|
95
|
+
token = asyncio.run(solve())
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Supported CAPTCHA Types
|
|
99
|
+
|
|
100
|
+
| CAPTCHA | Module | Description |
|
|
101
|
+
|---------|--------|-------------|
|
|
102
|
+
| reCAPTCHA v2/v3 | `ReCaptcha` | Google reCAPTCHA |
|
|
103
|
+
| hCaptcha | `HCaptcha` | hCaptcha challenge |
|
|
104
|
+
| Cloudflare Turnstile | `Turnstile` | Cloudflare protection |
|
|
105
|
+
| Image | `ImageCaptcha` | Type the text from image |
|
|
106
|
+
| Audio | `AudioCaptcha` | Listen and type audio |
|
|
107
|
+
| GeeTest | `GeeTest` | Chinese geetest puzzles |
|
|
108
|
+
| KeyCaptcha | `KeyCaptcha` | KeyCAPTCHA service |
|
|
109
|
+
| Amazon WAF | `AmazonWaf` | AWS WAF challenge |
|
|
110
|
+
| ALTCHA | `AltchaCaptcha` | ALTCHA challenge |
|
|
111
|
+
| Binance | `BinanceCaptcha` | Token-based Binance challenge |
|
|
112
|
+
| Grid | `GridCaptcha` | Select grid cells |
|
|
113
|
+
| Coordinates | `CoordinatesCaptcha` | Click on coordinates |
|
|
114
|
+
| And 20+ more | ... | See [full docs](https://andreidrang.github.io/python-rucaptcha/) |
|
|
115
|
+
|
|
116
|
+
## Switching Services
|
|
117
|
+
|
|
118
|
+
Use the same code with different services:
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from python_rucaptcha import HCaptcha
|
|
122
|
+
from python_rucaptcha.core.enums import ServiceEnm
|
|
123
|
+
|
|
124
|
+
# Use 2Captcha (default)
|
|
125
|
+
result = HCaptcha("2captcha_key").captcha_handler(...)
|
|
126
|
+
|
|
127
|
+
# Use RuCaptcha
|
|
128
|
+
result = HCaptcha("rucaptcha_key", service_type=ServiceEnm.RuCaptcha).captcha_handler(...)
|
|
129
|
+
|
|
130
|
+
# Use DeathByCaptcha
|
|
131
|
+
result = HCaptcha("dbc_user:dbc_pass", service_type=ServiceEnm.DeathByCaptcha).captcha_handler(...)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Testing
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
# Set your API key
|
|
138
|
+
export RUCAPTCHA_KEY="your_key_here"
|
|
139
|
+
|
|
140
|
+
# Run tests
|
|
141
|
+
make tests
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Documentation
|
|
145
|
+
|
|
146
|
+
For advanced usage, configuration options, and all CAPTCHA types, see the [full documentation](https://andreidrang.github.io/python-rucaptcha/).
|
|
147
|
+
|
|
148
|
+
## Support
|
|
149
|
+
|
|
150
|
+
- **Telegram:** [pythoncaptcha](https://t.me/pythoncaptcha)
|
|
151
|
+
- **Email:** python-captcha@pm.me
|
|
152
|
+
- **Issues:** [GitHub Issues](https://github.com/AndreiDrang/python-rucaptcha/issues)
|
|
153
|
+
|
|
154
|
+
## Changelog
|
|
155
|
+
|
|
156
|
+
See [Releases](https://github.com/AndreiDrang/python-rucaptcha/releases) for full changelog.
|
|
157
|
+
|
|
158
|
+
- **v6.0** - Refactored to use msgspec (faster), API v2, dropped Python 3.8
|
|
159
|
+
- **v5.3** - Added DeathByCaptcha support
|
|
160
|
+
- **v5.2** - Added audio CAPTCHA solving
|
|
@@ -66,7 +66,11 @@ keywords = [ "captcha",
|
|
|
66
66
|
"turnstile",
|
|
67
67
|
"amazon",
|
|
68
68
|
"amazon_waf",
|
|
69
|
-
"
|
|
69
|
+
"vk-captcha",
|
|
70
|
+
"fox-captcha",
|
|
71
|
+
"temu-captcha",
|
|
72
|
+
"friendly-captcha",
|
|
73
|
+
"binance-captcha"
|
|
70
74
|
]
|
|
71
75
|
license = "MIT"
|
|
72
76
|
classifiers = [
|
|
@@ -78,6 +82,7 @@ classifiers = [
|
|
|
78
82
|
"Programming Language :: Python :: 3.10",
|
|
79
83
|
"Programming Language :: Python :: 3.11",
|
|
80
84
|
"Programming Language :: Python :: 3.12",
|
|
85
|
+
"Programming Language :: Python :: 3.13",
|
|
81
86
|
"Framework :: AsyncIO",
|
|
82
87
|
"Operating System :: Unix",
|
|
83
88
|
"Operating System :: Microsoft :: Windows",
|
|
@@ -86,7 +91,7 @@ classifiers = [
|
|
|
86
91
|
dependencies = [
|
|
87
92
|
"requests>=2.21.0",
|
|
88
93
|
"aiohttp>=3.9.2",
|
|
89
|
-
"msgspec>=0.18,<0.
|
|
94
|
+
"msgspec>=0.18,<0.21",
|
|
90
95
|
"tenacity>=8,<10"
|
|
91
96
|
]
|
|
92
97
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "6.6.0"
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
from typing import Union, Optional
|
|
2
|
+
|
|
3
|
+
from .core.base import BaseCaptcha
|
|
4
|
+
from .core.enums import AltchaEnm
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AltchaCaptcha(BaseCaptcha):
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
websiteURL: str,
|
|
11
|
+
method: Union[str, AltchaEnm] = AltchaEnm.AltchaTaskProxyless,
|
|
12
|
+
challengeURL: Optional[str] = None,
|
|
13
|
+
challengeJSON: Optional[str] = None,
|
|
14
|
+
proxyType: Optional[str] = None,
|
|
15
|
+
proxyAddress: Optional[str] = None,
|
|
16
|
+
proxyPort: Optional[int] = None,
|
|
17
|
+
proxyLogin: Optional[str] = None,
|
|
18
|
+
proxyPassword: Optional[str] = None,
|
|
19
|
+
*args,
|
|
20
|
+
**kwargs,
|
|
21
|
+
):
|
|
22
|
+
"""
|
|
23
|
+
The class is used to work with ALTCHA captcha.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
rucaptcha_key: User API key
|
|
27
|
+
websiteURL: Full URL of the captcha page
|
|
28
|
+
method: Captcha type
|
|
29
|
+
challengeURL: Full URL of the page that contains ALTCHA challenge
|
|
30
|
+
challengeJSON: JSON-encoded ALTCHA challenge data
|
|
31
|
+
proxyType: Proxy type (http, https, socks4, socks5)
|
|
32
|
+
proxyAddress: Proxy IP address or hostname
|
|
33
|
+
proxyPort: Proxy port
|
|
34
|
+
proxyLogin: Proxy login
|
|
35
|
+
proxyPassword: Proxy password
|
|
36
|
+
kwargs: Not required params for task creation request
|
|
37
|
+
|
|
38
|
+
Examples:
|
|
39
|
+
>>> AltchaCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122",
|
|
40
|
+
... websiteURL="https://example.com",
|
|
41
|
+
... challengeURL="https://example.com/altcha/challenge.js",
|
|
42
|
+
... method=AltchaEnm.AltchaTaskProxyless.value,
|
|
43
|
+
... ).captcha_handler()
|
|
44
|
+
{
|
|
45
|
+
"errorId":0,
|
|
46
|
+
"status":"ready",
|
|
47
|
+
"solution":{
|
|
48
|
+
"token":"..."
|
|
49
|
+
},
|
|
50
|
+
"cost":"0.00145",
|
|
51
|
+
"ip":"1.2.3.4",
|
|
52
|
+
"createTime":1692863536,
|
|
53
|
+
"endTime":1692863556,
|
|
54
|
+
"solveCount":1,
|
|
55
|
+
"taskId": 73243152973,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
>>> await AltchaCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122",
|
|
59
|
+
... websiteURL="https://example.com",
|
|
60
|
+
... challengeJSON='{"挑战数据"}',
|
|
61
|
+
... method=AltchaEnm.AltchaTask.value,
|
|
62
|
+
... proxyType="http",
|
|
63
|
+
... proxyAddress="1.2.3.4",
|
|
64
|
+
... proxyPort=8080,
|
|
65
|
+
... ).aio_captcha_handler()
|
|
66
|
+
{
|
|
67
|
+
"errorId":0,
|
|
68
|
+
"status":"ready",
|
|
69
|
+
"solution":{
|
|
70
|
+
"token":"..."
|
|
71
|
+
},
|
|
72
|
+
"cost":"0.00145",
|
|
73
|
+
"ip":"1.2.3.4",
|
|
74
|
+
"createTime":1692863536,
|
|
75
|
+
"endTime":1692863556,
|
|
76
|
+
"solveCount":1,
|
|
77
|
+
"taskId": 73243152973,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Dict with full server response
|
|
82
|
+
|
|
83
|
+
Notes:
|
|
84
|
+
https://rucaptcha.com/api-docs/altcha
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
super().__init__(method=method, *args, **kwargs)
|
|
88
|
+
|
|
89
|
+
# XOR validation: exactly one of challengeURL or challengeJSON must be provided
|
|
90
|
+
if not (bool(challengeURL) ^ bool(challengeJSON)):
|
|
91
|
+
raise ValueError(
|
|
92
|
+
"Exactly one of 'challengeURL' or 'challengeJSON' must be provided, not both or neither"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Validate method
|
|
96
|
+
if method not in AltchaEnm.list_values():
|
|
97
|
+
raise ValueError(f"Invalid method parameter set, available - {AltchaEnm.list_values()}")
|
|
98
|
+
|
|
99
|
+
# Build task payload
|
|
100
|
+
task_data = {"websiteURL": websiteURL}
|
|
101
|
+
|
|
102
|
+
if challengeURL:
|
|
103
|
+
task_data["challengeURL"] = challengeURL
|
|
104
|
+
|
|
105
|
+
if challengeJSON:
|
|
106
|
+
task_data["challengeJSON"] = challengeJSON
|
|
107
|
+
|
|
108
|
+
# Add proxy params only for non-proxyless methods
|
|
109
|
+
if method == AltchaEnm.AltchaTask.value:
|
|
110
|
+
if not all([proxyType, proxyAddress, proxyPort]):
|
|
111
|
+
raise ValueError(
|
|
112
|
+
"Proxy parameters (proxyType, proxyAddress, proxyPort) are required for AltchaTask"
|
|
113
|
+
)
|
|
114
|
+
task_data.update(
|
|
115
|
+
{
|
|
116
|
+
"proxyType": proxyType,
|
|
117
|
+
"proxyAddress": proxyAddress,
|
|
118
|
+
"proxyPort": proxyPort,
|
|
119
|
+
}
|
|
120
|
+
)
|
|
121
|
+
if proxyLogin and proxyPassword:
|
|
122
|
+
task_data["proxyLogin"] = proxyLogin
|
|
123
|
+
task_data["proxyPassword"] = proxyPassword
|
|
124
|
+
|
|
125
|
+
self.create_task_payload["task"].update(task_data)
|
|
126
|
+
|
|
127
|
+
def captcha_handler(self, **kwargs) -> dict:
|
|
128
|
+
"""
|
|
129
|
+
Sync solving method
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
kwargs: Parameters for the `requests` library
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
Dict with full server response
|
|
136
|
+
|
|
137
|
+
Notes:
|
|
138
|
+
Check class docstirng for more info
|
|
139
|
+
"""
|
|
140
|
+
return self._processing_response(**kwargs)
|
|
141
|
+
|
|
142
|
+
async def aio_captcha_handler(self) -> dict:
|
|
143
|
+
"""
|
|
144
|
+
Async solving method
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
Dict with full server response
|
|
148
|
+
|
|
149
|
+
Notes:
|
|
150
|
+
Check class docstirng for more info
|
|
151
|
+
"""
|
|
152
|
+
return await self._aio_processing_response()
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
from typing import Union, Optional
|
|
2
|
+
|
|
3
|
+
from .core.base import BaseCaptcha
|
|
4
|
+
from .core.enums import BinanceCaptchaEnm
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class BinanceCaptcha(BaseCaptcha):
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
websiteURL: str,
|
|
11
|
+
websiteKey: str,
|
|
12
|
+
validateId: str,
|
|
13
|
+
method: Union[str, BinanceCaptchaEnm] = BinanceCaptchaEnm.BinanceTaskProxyless,
|
|
14
|
+
userAgent: Optional[str] = None,
|
|
15
|
+
proxyType: Optional[str] = None,
|
|
16
|
+
proxyAddress: Optional[str] = None,
|
|
17
|
+
proxyPort: Optional[int] = None,
|
|
18
|
+
proxyLogin: Optional[str] = None,
|
|
19
|
+
proxyPassword: Optional[str] = None,
|
|
20
|
+
*args,
|
|
21
|
+
**kwargs,
|
|
22
|
+
):
|
|
23
|
+
"""
|
|
24
|
+
The class is used to work with Binance CAPTCHA.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
rucaptcha_key: User API key
|
|
28
|
+
websiteURL: Full URL of the page where the captcha is loaded
|
|
29
|
+
websiteKey: Value of bizId, bizType, or bizCode from page requests
|
|
30
|
+
validateId: Dynamic value of validateId, securityId, or securityCheckResponseValidateId
|
|
31
|
+
method: Captcha type
|
|
32
|
+
userAgent: User-Agent string to be used when solving the captcha
|
|
33
|
+
proxyType: Proxy type (http, https, socks4, socks5)
|
|
34
|
+
proxyAddress: Proxy IP address or hostname
|
|
35
|
+
proxyPort: Proxy port
|
|
36
|
+
proxyLogin: Proxy login
|
|
37
|
+
proxyPassword: Proxy password
|
|
38
|
+
|
|
39
|
+
Examples:
|
|
40
|
+
>>> BinanceCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122",
|
|
41
|
+
... websiteURL="https://example.com/page-with-binance",
|
|
42
|
+
... websiteKey="login",
|
|
43
|
+
... validateId="cb0bfefa598...e54ecd57b",
|
|
44
|
+
... userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
|
|
45
|
+
... "(KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
|
|
46
|
+
... method=BinanceCaptchaEnm.BinanceTaskProxyless.value,
|
|
47
|
+
... ).captcha_handler()
|
|
48
|
+
{
|
|
49
|
+
"errorId":0,
|
|
50
|
+
"status":"ready",
|
|
51
|
+
"solution":{
|
|
52
|
+
"token":"captcha#09ba4905a79f44f...kc99maS943qIsquNP9D77",
|
|
53
|
+
"userAgent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
|
54
|
+
},
|
|
55
|
+
"cost":"0.00299",
|
|
56
|
+
"ip":"1.2.3.4",
|
|
57
|
+
"createTime":1692863536,
|
|
58
|
+
"endTime":1692863556,
|
|
59
|
+
"solveCount":1,
|
|
60
|
+
"taskId": 73243152973,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
>>> await BinanceCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122",
|
|
64
|
+
... websiteURL="https://example.com/page-with-binance",
|
|
65
|
+
... websiteKey="login",
|
|
66
|
+
... validateId="cb0bfefa598...e54ecd57b",
|
|
67
|
+
... method=BinanceCaptchaEnm.BinanceTask.value,
|
|
68
|
+
... proxyType="http",
|
|
69
|
+
... proxyAddress="1.2.3.4",
|
|
70
|
+
... proxyPort=8080,
|
|
71
|
+
... ).aio_captcha_handler()
|
|
72
|
+
{
|
|
73
|
+
"errorId":0,
|
|
74
|
+
"status":"ready",
|
|
75
|
+
"solution":{
|
|
76
|
+
"token":"captcha#09ba4905a79f44f...kc99maS943qIsquNP9D77",
|
|
77
|
+
"userAgent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
|
78
|
+
},
|
|
79
|
+
"cost":"0.00299",
|
|
80
|
+
"ip":"1.2.3.4",
|
|
81
|
+
"createTime":1692863536,
|
|
82
|
+
"endTime":1692863556,
|
|
83
|
+
"solveCount":1,
|
|
84
|
+
"taskId": 73243152973,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
Dict with full server response
|
|
89
|
+
|
|
90
|
+
Notes:
|
|
91
|
+
https://2captcha.com/api-docs/binance-captcha
|
|
92
|
+
|
|
93
|
+
https://rucaptcha.com/api-docs/binance-captcha
|
|
94
|
+
"""
|
|
95
|
+
super().__init__(method=method, *args, **kwargs)
|
|
96
|
+
|
|
97
|
+
# Validate method
|
|
98
|
+
if method not in BinanceCaptchaEnm.list_values():
|
|
99
|
+
raise ValueError(f"Invalid method parameter set, available - {BinanceCaptchaEnm.list_values()}")
|
|
100
|
+
|
|
101
|
+
# Build task payload
|
|
102
|
+
task_data = {
|
|
103
|
+
"websiteURL": websiteURL,
|
|
104
|
+
"websiteKey": websiteKey,
|
|
105
|
+
"validateId": validateId,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if userAgent:
|
|
109
|
+
task_data["userAgent"] = userAgent
|
|
110
|
+
|
|
111
|
+
# Add proxy params only for non-proxyless methods
|
|
112
|
+
if method == BinanceCaptchaEnm.BinanceTask.value:
|
|
113
|
+
if not all([proxyType, proxyAddress, proxyPort]):
|
|
114
|
+
raise ValueError(
|
|
115
|
+
"Proxy parameters (proxyType, proxyAddress, proxyPort) are required for BinanceTask"
|
|
116
|
+
)
|
|
117
|
+
task_data.update(
|
|
118
|
+
{
|
|
119
|
+
"proxyType": proxyType,
|
|
120
|
+
"proxyAddress": proxyAddress,
|
|
121
|
+
"proxyPort": proxyPort,
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
if proxyLogin and proxyPassword:
|
|
125
|
+
task_data["proxyLogin"] = proxyLogin
|
|
126
|
+
task_data["proxyPassword"] = proxyPassword
|
|
127
|
+
|
|
128
|
+
self.create_task_payload["task"].update(task_data)
|
|
129
|
+
|
|
130
|
+
def captcha_handler(self, **kwargs) -> dict:
|
|
131
|
+
"""
|
|
132
|
+
Sync solving method
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
kwargs: Parameters for the `requests` library
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Dict with full server response
|
|
139
|
+
|
|
140
|
+
Notes:
|
|
141
|
+
Check class docstirng for more info
|
|
142
|
+
"""
|
|
143
|
+
return self._processing_response(**kwargs)
|
|
144
|
+
|
|
145
|
+
async def aio_captcha_handler(self) -> dict:
|
|
146
|
+
"""
|
|
147
|
+
Async solving method
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
Dict with full server response
|
|
151
|
+
|
|
152
|
+
Notes:
|
|
153
|
+
Check class docstirng for more info
|
|
154
|
+
"""
|
|
155
|
+
return await self._aio_processing_response()
|