diff --git a/README.md b/README.md index f614d94..d9ceadb 100644 --- a/README.md +++ b/README.md @@ -269,7 +269,6 @@ Internal factory scripts, tools, and templates are intentionally not part of thi | [octoparse-captchaai-visual-scraping-captcha](articles/octoparse-captchaai-visual-scraping-captcha/) | reCAPTCHA v2 | 🟢 Verified | Python, Node.js, PHP, Go, Java, C#, Ruby, Rust, Kotlin, Bash | [Blog](https://blog.captchaai.com/octoparse-captchaai-visual-scraping-captcha) | | [opentelemetry-tracing-captcha-pipelines](articles/opentelemetry-tracing-captcha-pipelines/) | reCAPTCHA v2 | 🟢 Verified | Python, Node.js, PHP, Go, Java, C#, Ruby, Rust, Kotlin, Bash | [Blog](https://blog.captchaai.com/opentelemetry-tracing-captcha-pipelines) | | [oxylabs-captchaai-datacenter-proxy-integration](articles/oxylabs-captchaai-datacenter-proxy-integration/) | reCAPTCHA v2 | 🟢 Verified | Python, Node.js, PHP, Go, Java, C#, Ruby, Rust, Kotlin, Bash | [Blog](https://blog.captchaai.com/oxylabs-captchaai-datacenter-proxy-integration) | -| [parsehub-captchaai-no-code-scraping](articles/parsehub-captchaai-no-code-scraping/) | reCAPTCHA v2 | 🟢 Verified | Python, Node.js, PHP, Go, Java, C#, Ruby, Rust, Kotlin, Bash | [Blog](https://blog.captchaai.com/parsehub-captchaai-no-code-scraping) | | [pharmacy-price-comparison-captcha-handling](articles/pharmacy-price-comparison-captcha-handling/) | reCAPTCHA v2 | 🟢 Verified | Python, Node.js, PHP, Go, Java, C#, Ruby, Rust, Kotlin, Bash | [Blog](https://blog.captchaai.com/pharmacy-price-comparison-captcha-handling) | | [phrase-min-max-length-parameters-image-captcha](articles/phrase-min-max-length-parameters-image-captcha/) | Image/OCR CAPTCHA | 🟢 Verified | Python, Node.js, PHP, Go, Java, C#, Ruby, Rust, Kotlin, Bash | [Blog](https://blog.captchaai.com/phrase-min-max-length-parameters-image-captcha) | | [playwright-captcha-handling](articles/playwright-captcha-handling/) | Cloudflare Turnstile | 🟢 Verified | Python, Node.js, PHP, Go, Java, C#, Ruby, Rust, Kotlin, Bash | [Blog](https://blog.captchaai.com/playwright-captcha-handling) | diff --git a/articles/parsehub-captchaai-no-code-scraping/.env.example b/articles/parsehub-captchaai-no-code-scraping/.env.example deleted file mode 100644 index 1ce3f65..0000000 --- a/articles/parsehub-captchaai-no-code-scraping/.env.example +++ /dev/null @@ -1,13 +0,0 @@ -# CaptchaAI API credentials -# Get your API key from https://captchaai.com/dashboard -CAPTCHAAI_API_KEY=YOUR_API_KEY - -# Target page configuration -# reCAPTCHA v2 sitekey from the target page -CAPTCHA_SITEKEY=YOUR_SITE_KEY -# Full URL of the page where the CAPTCHA loads -CAPTCHA_PAGEURL=https://example.com/login - -# Polling configuration (optional) -POLL_INTERVAL=5 -MAX_TIMEOUT=120 diff --git a/articles/parsehub-captchaai-no-code-scraping/.gitignore b/articles/parsehub-captchaai-no-code-scraping/.gitignore deleted file mode 100644 index f277bf9..0000000 --- a/articles/parsehub-captchaai-no-code-scraping/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -.env -__pycache__/ -node_modules/ -vendor/ -target/ -*.class -bin/ -obj/ \ No newline at end of file diff --git a/articles/parsehub-captchaai-no-code-scraping/README.md b/articles/parsehub-captchaai-no-code-scraping/README.md deleted file mode 100644 index c927f88..0000000 --- a/articles/parsehub-captchaai-no-code-scraping/README.md +++ /dev/null @@ -1,94 +0,0 @@ -# ParseHub + CaptchaAI: No-Code Scraping with CAPTCHA Solving - -Full working example for solving reCAPTCHA v2 using the CaptchaAI API. - -**Languages:** Python, Node.js, PHP, Go, Java, C#, Ruby, Rust, Kotlin, Bash - -## Related article - -This example accompanies the blog article: -**[ParseHub + CaptchaAI: No-Code Scraping with CAPTCHA Solving](https://blog.captchaai.com/parsehub-captchaai-no-code-scraping)** - -## Prerequisites - -- A CaptchaAI account with API key ([get one here](https://captchaai.com)) -- A target page with reCAPTCHA v2 for testing - -## Quick start - -1. Clone the repository: - ```bash - git clone https://github.com/CaptchaAI/CaptchaAI-Examples.git - cd CaptchaAI-Examples/articles/parsehub-captchaai-no-code-scraping - ``` - -2. Copy and edit the environment file: - ```bash - cp .env.example .env - ``` - -3. Run any language example: - -### Python -```bash -cd python && pip install -r requirements.txt && python solve.py -``` - -### Node.js -```bash -cd node && npm install && node solve.js -``` - -### PHP -```bash -cd php && composer install && php solve.php -``` - -### Go -```bash -cd go && go run solve.go -``` - -### Java -```bash -cd java && javac Solve.java && java Solve -``` - -### C# -```bash -cd csharp && dotnet run -``` - -### Ruby -```bash -cd ruby && ruby solve.rb -``` - -### Rust -```bash -cd rust && cargo run -``` - -### Kotlin -```bash -cd kotlin && kotlinc solve.kt -include-runtime -d solve.jar && java -jar solve.jar -``` - -### Bash -```bash -cd bash && chmod +x solve.sh && ./solve.sh -``` - -## How it works - -1. **Submit** — Sends the CAPTCHA parameters to `https://ocr.captchaai.com/in.php` using method `userrecaptcha` -2. **Poll** — Checks `https://ocr.captchaai.com/res.php` every 5 seconds for the result -3. **Result** — Returns the solved token/answer ready for use - -## API documentation - -- [CaptchaAI API Docs](https://captchaai.com/api-docs) - -## Related examples - -Browse more examples in the [CaptchaAI-Examples repository](https://github.com/CaptchaAI/CaptchaAI-Examples). \ No newline at end of file diff --git a/articles/parsehub-captchaai-no-code-scraping/bash/solve.sh b/articles/parsehub-captchaai-no-code-scraping/bash/solve.sh deleted file mode 100644 index 013a896..0000000 --- a/articles/parsehub-captchaai-no-code-scraping/bash/solve.sh +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env bash -# Solve reCAPTCHA v2 using the CaptchaAI API. -# -# Usage: -# chmod +x solve.sh -# ./solve.sh -# -# Requires .env file in parent directory with: -# CAPTCHAAI_API_KEY and CAPTCHA-specific variables -# -# Dependencies: curl, jq - -set -euo pipefail - -SUBMIT_URL="https://ocr.captchaai.com/in.php" -RESULT_URL="https://ocr.captchaai.com/res.php" - -# Load .env file -ENV_FILE="$(dirname "$0")/../.env" -if [[ -f "$ENV_FILE" ]]; then - while IFS='=' read -r key value; do - key=$(echo "$key" | xargs) - [[ -z "$key" || "$key" == \#* ]] && continue - value=$(echo "$value" | xargs) - export "$key=$value" - done < "$ENV_FILE" -fi - -CAPTCHAAI_API_KEY="${CAPTCHAAI_API_KEY:-}" -CAPTCHA_SITEKEY="${CAPTCHA_SITEKEY:-}" -CAPTCHA_PAGEURL="${CAPTCHA_PAGEURL:-}" -POLL_INTERVAL="${POLL_INTERVAL:-5}" -MAX_TIMEOUT="${MAX_TIMEOUT:-120}" - -AUTH_ERRORS="ERROR_WRONG_USER_KEY ERROR_KEY_DOES_NOT_EXIST IP_BANNED" -BALANCE_ERRORS="ERROR_ZERO_BALANCE" -INPUT_ERRORS="ERROR_PAGEURL ERROR_WRONG_GOOGLEKEY ERROR_BAD_PARAMETERS ERROR_BAD_TOKEN_OR_PAGEURL" -TRANSIENT_ERRORS="ERROR_SERVER_ERROR ERROR_INTERNAL_SERVER_ERROR" -SOLVE_ERRORS="ERROR_CAPTCHA_UNSOLVABLE" -PROXY_ERRORS="ERROR_BAD_PROXY ERROR_PROXY_CONNECTION_FAILED" - -contains() { [[ " $1 " == *" $2 "* ]]; } - -classify_error() { - local error="$1" - if contains "$AUTH_ERRORS" "$error"; then - echo "[!] Authentication error: $error" - echo " Check your API key at https://captchaai.com/dashboard" - elif contains "$BALANCE_ERRORS" "$error"; then - echo "[!] Balance error: $error" - echo " Top up your account at https://captchaai.com" - elif contains "$INPUT_ERRORS" "$error"; then - echo "[!] Input error: $error" - echo " Verify your sitekey and page URL are correct." - elif contains "$PROXY_ERRORS" "$error"; then - echo "[!] Proxy error: $error" - echo " Check your proxy configuration or try a different proxy." - else - echo "[!] Submission failed: $error" - fi -} - -# Validate -if [[ -z "$CAPTCHAAI_API_KEY" || "$CAPTCHAAI_API_KEY" == "YOUR_API_KEY" ]]; then - echo "[!] ERROR: CAPTCHAAI_API_KEY is not set." - echo " Copy .env.example to .env and add your real API key." - exit 1 -fi - -# Check dependencies -if ! command -v jq &> /dev/null; then - echo "[!] ERROR: jq is required but not installed." - echo " Install it: apt-get install jq / brew install jq / choco install jq" - exit 1 -fi - -# Submit -echo "[*] Submitting reCAPTCHA v2 task..." -SUBMIT_RESPONSE=$(curl -s --max-time 30 \ - "${SUBMIT_URL}?key=${CAPTCHAAI_API_KEY}&method=userrecaptcha&googlekey=${CAPTCHA_SITEKEY}&pageurl=${CAPTCHA_PAGEURL}&json=1") - -if [[ -z "$SUBMIT_RESPONSE" ]]; then - echo "[!] Network error during submission." - exit 1 -fi - -SUBMIT_STATUS=$(echo "$SUBMIT_RESPONSE" | jq -r '.status // 0') -SUBMIT_REQUEST=$(echo "$SUBMIT_RESPONSE" | jq -r '.request // "UNKNOWN_ERROR"') - -if [[ "$SUBMIT_STATUS" != "1" ]]; then - classify_error "$SUBMIT_REQUEST" - exit 1 -fi - -TASK_ID="$SUBMIT_REQUEST" -echo "[+] Task submitted. ID: $TASK_ID" - -# Poll -echo "[*] Waiting 15s before first poll..." -sleep 15 - -ELAPSED=15 -ATTEMPT=0 -BACKOFF=$POLL_INTERVAL - -while [[ $ELAPSED -lt $MAX_TIMEOUT ]]; do - ATTEMPT=$((ATTEMPT + 1)) - echo "[*] Polling for result (attempt $ATTEMPT)..." - - POLL_RESPONSE=$(curl -s --max-time 30 \ - "${RESULT_URL}?key=${CAPTCHAAI_API_KEY}&action=get&id=${TASK_ID}&json=1" 2>/dev/null || echo "") - - if [[ -z "$POLL_RESPONSE" ]]; then - echo "[!] Network error during polling." - sleep "$BACKOFF" - ELAPSED=$((ELAPSED + BACKOFF)) - BACKOFF=$((BACKOFF * 2)) - [[ $BACKOFF -gt 30 ]] && BACKOFF=30 - continue - fi - - POLL_STATUS=$(echo "$POLL_RESPONSE" | jq -r '.status // 0') - POLL_REQUEST=$(echo "$POLL_RESPONSE" | jq -r '.request // ""') - - if [[ "$POLL_STATUS" == "1" ]]; then - TOKEN="$POLL_REQUEST" - TRUNCATED="${TOKEN:0:50}" - echo "[+] Solved! Token: ${TRUNCATED}..." - echo "[+] Full token length: ${#TOKEN} characters" - exit 0 - fi - - if [[ "$POLL_REQUEST" == "CAPCHA_NOT_READY" ]]; then - echo "[*] Not ready yet, waiting ${POLL_INTERVAL}s..." - sleep "$POLL_INTERVAL" - ELAPSED=$((ELAPSED + POLL_INTERVAL)) - BACKOFF=$POLL_INTERVAL - continue - fi - if contains "$TRANSIENT_ERRORS" "$POLL_REQUEST"; then - echo "[!] Transient error: $POLL_REQUEST, retrying in ${BACKOFF}s..." - sleep "$BACKOFF" - ELAPSED=$((ELAPSED + BACKOFF)) - BACKOFF=$((BACKOFF * 2)) - [[ $BACKOFF -gt 30 ]] && BACKOFF=30 - continue - fi - if contains "$SOLVE_ERRORS" "$POLL_REQUEST"; then - echo "[!] Solve error: $POLL_REQUEST" - echo " The CAPTCHA could not be solved. Verify parameters and retry." - exit 1 - fi - if contains "$PROXY_ERRORS" "$POLL_REQUEST"; then - echo "[!] Proxy error: $POLL_REQUEST" - echo " Check your proxy configuration or try a different proxy." - exit 1 - fi - - echo "[!] Unexpected error: $POLL_REQUEST" - exit 1 -done - -echo "[!] Timeout: no solution received within ${MAX_TIMEOUT} seconds." -exit 1 diff --git a/articles/parsehub-captchaai-no-code-scraping/csharp/Solve.cs b/articles/parsehub-captchaai-no-code-scraping/csharp/Solve.cs deleted file mode 100644 index d7f2bf5..0000000 --- a/articles/parsehub-captchaai-no-code-scraping/csharp/Solve.cs +++ /dev/null @@ -1,190 +0,0 @@ -// Solve reCAPTCHA v2 using the CaptchaAI API. -// -// Usage: -// dotnet run -// -// Requires .env file in parent directory with: -// CAPTCHAAI_API_KEY and CAPTCHA-specific variables - -using System; -using System.Collections.Generic; -using System.IO; -using System.Net.Http; -using System.Text.Json; -using System.Threading.Tasks; - -class Solve -{ - private const string SubmitUrl = "https://ocr.captchaai.com/in.php"; - private const string ResultUrl = "https://ocr.captchaai.com/res.php"; - - private static readonly HashSet AuthErrors = new() - { "ERROR_WRONG_USER_KEY", "ERROR_KEY_DOES_NOT_EXIST", "IP_BANNED" }; - private static readonly HashSet BalanceErrors = new() - { "ERROR_ZERO_BALANCE" }; - private static readonly HashSet InputErrors = new() - { "ERROR_PAGEURL", "ERROR_WRONG_GOOGLEKEY", "ERROR_BAD_PARAMETERS", "ERROR_BAD_TOKEN_OR_PAGEURL" }; - private static readonly HashSet TransientErrors = new() - { "ERROR_SERVER_ERROR", "ERROR_INTERNAL_SERVER_ERROR" }; - private static readonly HashSet SolveErrors = new() - { "ERROR_CAPTCHA_UNSOLVABLE" }; - private static readonly HashSet ProxyErrors = new() - { "ERROR_BAD_PROXY", "ERROR_PROXY_CONNECTION_FAILED" }; - - private static readonly HttpClient Client = new() { Timeout = TimeSpan.FromSeconds(30) }; - - private readonly string _apiKey; - private readonly int _pollInterval; - private readonly int _maxTimeout; - private readonly string _googlekey; - private readonly string _pageurl; - - public Solve(Dictionary env) - { - _apiKey = GetEnv(env, "CAPTCHAAI_API_KEY", ""); - _googlekey = GetEnv(env, "CAPTCHA_SITEKEY", ""); - _pageurl = GetEnv(env, "CAPTCHA_PAGEURL", ""); - _pollInterval = int.Parse(GetEnv(env, "POLL_INTERVAL", "5")); - _maxTimeout = int.Parse(GetEnv(env, "MAX_TIMEOUT", "120")); - } - - private static string GetEnv(Dictionary env, string key, string fallback) - { - if (env.TryGetValue(key, out var val) && !string.IsNullOrEmpty(val)) return val; - var sysVal = Environment.GetEnvironmentVariable(key); - return !string.IsNullOrEmpty(sysVal) ? sysVal : fallback; - } - - private void ValidateConfig() - { - if (string.IsNullOrEmpty(_apiKey) || _apiKey == "YOUR_API_KEY") - { - Console.WriteLine("[!] ERROR: CAPTCHAAI_API_KEY is not set."); - Console.WriteLine(" Copy .env.example to .env and add your real API key."); - Environment.Exit(1); - } - } - - private async Task SubmitTaskAsync() - { - Console.WriteLine("[*] Submitting reCAPTCHA v2 task..."); - var query = $"key={Uri.EscapeDataString(_apiKey)}" + - $"&method=userrecaptcha" + - $"&googlekey={Uri.EscapeDataString(_googlekey)}" + - $"&pageurl={Uri.EscapeDataString(_pageurl)}" + - $"&json=1"; - - string body; - try { body = await Client.GetStringAsync($"{SubmitUrl}?{query}"); } - catch (Exception ex) - { - Console.WriteLine($"[!] Network error during submission: {ex.Message}"); - Environment.Exit(1); return ""; - } - - using var doc = JsonDocument.Parse(body); - var root = doc.RootElement; - var status = root.GetProperty("status").GetInt32(); - var request = root.GetProperty("request").ToString(); - - if (status != 1) { ClassifyError(request); Environment.Exit(1); } - - Console.WriteLine($"[+] Task submitted. ID: {request}"); - return request; - } - - private async Task PollResultAsync(string taskId) - { - Console.WriteLine("[*] Waiting 15s before first poll..."); - await Task.Delay(15000); - - var query = $"key={Uri.EscapeDataString(_apiKey)}&action=get&id={Uri.EscapeDataString(taskId)}&json=1"; - var elapsed = 15; var attempt = 0; var backoff = _pollInterval; - - while (elapsed < _maxTimeout) - { - attempt++; - Console.WriteLine($"[*] Polling for result (attempt {attempt})..."); - - string body; - try { body = await Client.GetStringAsync($"{ResultUrl}?{query}"); } - catch (Exception ex) - { - Console.WriteLine($"[!] Network error during polling: {ex.Message}"); - await Task.Delay(backoff * 1000); - elapsed += backoff; backoff = Math.Min(backoff * 2, 30); continue; - } - - using var doc = JsonDocument.Parse(body); - var root = doc.RootElement; - var status = root.GetProperty("status").GetInt32(); - var request = root.GetProperty("request").ToString(); - - if (status == 1) return request; - if (request == "CAPCHA_NOT_READY") - { - Console.WriteLine($"[*] Not ready yet, waiting {_pollInterval}s..."); - await Task.Delay(_pollInterval * 1000); - elapsed += _pollInterval; backoff = _pollInterval; continue; - } - if (TransientErrors.Contains(request)) - { - Console.WriteLine($"[!] Transient error: {request}, retrying in {backoff}s..."); - await Task.Delay(backoff * 1000); - elapsed += backoff; backoff = Math.Min(backoff * 2, 30); continue; - } - if (SolveErrors.Contains(request)) - { - Console.WriteLine($"[!] Solve error: {request}"); - Console.WriteLine(" The CAPTCHA could not be solved. Verify parameters and retry."); - Environment.Exit(1); - } - if (ProxyErrors.Contains(request)) - { - Console.WriteLine($"[!] Proxy error: {request}"); - Console.WriteLine(" Check your proxy configuration or try a different proxy."); - Environment.Exit(1); - } - Console.WriteLine($"[!] Unexpected error: {request}"); - Environment.Exit(1); - } - - Console.WriteLine($"[!] Timeout: no solution received within {_maxTimeout} seconds."); - Environment.Exit(1); return ""; - } - - private static void ClassifyError(string error) - { - if (AuthErrors.Contains(error)) { Console.WriteLine($"[!] Authentication error: {error}"); Console.WriteLine(" Check your API key at https://captchaai.com/dashboard"); } - else if (BalanceErrors.Contains(error)) { Console.WriteLine($"[!] Balance error: {error}"); Console.WriteLine(" Top up your account at https://captchaai.com"); } - else if (InputErrors.Contains(error)) { Console.WriteLine($"[!] Input error: {error}"); Console.WriteLine(" Verify your sitekey and page URL are correct."); } - else if (ProxyErrors.Contains(error)) { Console.WriteLine($"[!] Proxy error: {error}"); Console.WriteLine(" Check your proxy configuration or try a different proxy."); } - else Console.WriteLine($"[!] Submission failed: {error}"); - } - - private static Dictionary LoadEnv(string path) - { - var env = new Dictionary(); - if (!File.Exists(path)) return env; - foreach (var line in File.ReadAllLines(path)) - { - var trimmed = line.Trim(); - if (string.IsNullOrEmpty(trimmed) || trimmed.StartsWith('#')) continue; - var idx = trimmed.IndexOf('='); - if (idx > 0) env[trimmed[..idx].Trim()] = trimmed[(idx + 1)..].Trim(); - } - return env; - } - - public static async Task Main(string[] args) - { - var env = LoadEnv(Path.Combine("..", ".env")); - var solver = new Solve(env); - solver.ValidateConfig(); - var taskId = await solver.SubmitTaskAsync(); - var token = await solver.PollResultAsync(taskId); - var truncated = token.Length > 50 ? token[..50] : token; - Console.WriteLine($"[+] Solved! Token: {truncated}..."); - Console.WriteLine($"[+] Full token length: {token.Length} characters"); - } -} diff --git a/articles/parsehub-captchaai-no-code-scraping/csharp/Solve.csproj b/articles/parsehub-captchaai-no-code-scraping/csharp/Solve.csproj deleted file mode 100644 index dd4b568..0000000 --- a/articles/parsehub-captchaai-no-code-scraping/csharp/Solve.csproj +++ /dev/null @@ -1,6 +0,0 @@ - - - Exe - net8.0 - - diff --git a/articles/parsehub-captchaai-no-code-scraping/go/go.mod b/articles/parsehub-captchaai-no-code-scraping/go/go.mod deleted file mode 100644 index 64434ff..0000000 --- a/articles/parsehub-captchaai-no-code-scraping/go/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module captchaai-example - -go 1.21 diff --git a/articles/parsehub-captchaai-no-code-scraping/go/solve.go b/articles/parsehub-captchaai-no-code-scraping/go/solve.go deleted file mode 100644 index c432951..0000000 --- a/articles/parsehub-captchaai-no-code-scraping/go/solve.go +++ /dev/null @@ -1,260 +0,0 @@ -// Solve reCAPTCHA v2 using the CaptchaAI API. -// -// Usage: -// go run solve.go -// -// Requires .env file in parent directory with: -// CAPTCHAAI_API_KEY and CAPTCHA-specific variables - -package main - -import ( - "bufio" - "encoding/json" - "fmt" - "net/http" - "net/url" - "os" - "path/filepath" - "strconv" - "strings" - "time" -) - -const ( - submitURL = "https://ocr.captchaai.com/in.php" - resultURL = "https://ocr.captchaai.com/res.php" -) - -var ( - authErrors = map[string]bool{"ERROR_WRONG_USER_KEY": true, "ERROR_KEY_DOES_NOT_EXIST": true, "IP_BANNED": true} - balanceErrors = map[string]bool{"ERROR_ZERO_BALANCE": true} - inputErrors = map[string]bool{"ERROR_PAGEURL": true, "ERROR_WRONG_GOOGLEKEY": true, "ERROR_BAD_PARAMETERS": true, "ERROR_BAD_TOKEN_OR_PAGEURL": true} - transientErrors = map[string]bool{"ERROR_SERVER_ERROR": true, "ERROR_INTERNAL_SERVER_ERROR": true} - solveErrors = map[string]bool{"ERROR_CAPTCHA_UNSOLVABLE": true} - proxyErrors = map[string]bool{"ERROR_BAD_PROXY": true, "ERROR_PROXY_CONNECTION_FAILED": true} -) - -type apiResponse struct { - Status int `json:"status"` - Request any `json:"request"` -} - -func requestValue(value any) string { - switch v := value.(type) { - case nil: - return "" - case string: - return v - case float64: - return strconv.FormatInt(int64(v), 10) - case json.Number: - return v.String() - default: - return fmt.Sprintf("%v", v) - } -} - -func loadEnv(path string) map[string]string { - env := make(map[string]string) - f, err := os.Open(path) - if err != nil { - return env - } - defer f.Close() - scanner := bufio.NewScanner(f) - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - if line == "" || strings.HasPrefix(line, "#") { - continue - } - parts := strings.SplitN(line, "=", 2) - if len(parts) == 2 { - env[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1]) - } - } - return env -} - -func getEnv(env map[string]string, key, fallback string) string { - if v, ok := env[key]; ok && v != "" { - return v - } - if v := os.Getenv(key); v != "" { - return v - } - return fallback -} - -func getEnvInt(env map[string]string, key string, fallback int) int { - s := getEnv(env, key, "") - if s == "" { - return fallback - } - v, err := strconv.Atoi(s) - if err != nil { - return fallback - } - return v -} - -func classifyError(errCode string) { - switch { - case authErrors[errCode]: - fmt.Printf("[!] Authentication error: %s\n", errCode) - fmt.Println(" Check your API key at https://captchaai.com/dashboard") - case balanceErrors[errCode]: - fmt.Printf("[!] Balance error: %s\n", errCode) - fmt.Println(" Top up your account at https://captchaai.com") - case inputErrors[errCode]: - fmt.Printf("[!] Input error: %s\n", errCode) - fmt.Println(" Verify your sitekey and page URL are correct.") - case proxyErrors[errCode]: - fmt.Printf("[!] Proxy error: %s\n", errCode) - fmt.Println(" Check your proxy configuration or try a different proxy.") - case solveErrors[errCode]: - fmt.Printf("[!] Solve error: %s\n", errCode) - fmt.Println(" The CAPTCHA could not be solved. Verify parameters and retry.") - default: - fmt.Printf("[!] Submission failed: %s\n", errCode) - } -} - -func main() { - envPath := filepath.Join("..", ".env") - env := loadEnv(envPath) - - apiKey := getEnv(env, "CAPTCHAAI_API_KEY", "") - googlekey := getEnv(env, "CAPTCHA_SITEKEY", "") - pageurl := getEnv(env, "CAPTCHA_PAGEURL", "") - pollInterval := getEnvInt(env, "POLL_INTERVAL", 5) - maxTimeout := getEnvInt(env, "MAX_TIMEOUT", 120) - - // Validate - if apiKey == "" || apiKey == "YOUR_API_KEY" { - fmt.Println("[!] ERROR: CAPTCHAAI_API_KEY is not set.") - fmt.Println(" Copy .env.example to .env and add your real API key.") - os.Exit(1) - } - - // Submit - fmt.Println("[*] Submitting reCAPTCHA v2 task...") - params := url.Values{ - "key": {apiKey}, - "method": {"userrecaptcha"}, - "googlekey": {googlekey}, - "pageurl": {pageurl}, - "json": {"1"}, - } - - client := &http.Client{Timeout: 30 * time.Second} - resp, err := client.Get(submitURL + "?" + params.Encode()) - if err != nil { - fmt.Printf("[!] Network error during submission: %s\n", err) - os.Exit(1) - } - defer resp.Body.Close() - - var submitData apiResponse - if err := json.NewDecoder(resp.Body).Decode(&submitData); err != nil { - fmt.Printf("[!] Failed to parse submission response: %s\n", err) - os.Exit(1) - } - - if submitData.Status != 1 { - classifyError(requestValue(submitData.Request)) - os.Exit(1) - } - - taskID := requestValue(submitData.Request) - fmt.Printf("[+] Task submitted. ID: %s\n", taskID) - - // Poll - fmt.Println("[*] Waiting 15s before first poll...") - time.Sleep(15 * time.Second) - - pollParams := url.Values{ - "key": {apiKey}, - "action": {"get"}, - "id": {taskID}, - "json": {"1"}, - } - - elapsed := 15 - attempt := 0 - backoff := pollInterval - - for elapsed < maxTimeout { - attempt++ - fmt.Printf("[*] Polling for result (attempt %d)...\n", attempt) - - resp, err := client.Get(resultURL + "?" + pollParams.Encode()) - if err != nil { - fmt.Printf("[!] Network error during polling: %s\n", err) - time.Sleep(time.Duration(backoff) * time.Second) - elapsed += backoff - if backoff < 30 { - backoff *= 2 - } - continue - } - - var resultData apiResponse - if err := json.NewDecoder(resp.Body).Decode(&resultData); err != nil { - resp.Body.Close() - fmt.Printf("[!] Failed to parse poll response: %s\n", err) - time.Sleep(time.Duration(backoff) * time.Second) - elapsed += backoff - continue - } - resp.Body.Close() - - if resultData.Status == 1 { - token := requestValue(resultData.Request) - truncated := token - if len(truncated) > 50 { - truncated = truncated[:50] - } - fmt.Printf("[+] Solved! Token: %s...\n", truncated) - fmt.Printf("[+] Full token length: %d characters\n", len(token)) - os.Exit(0) - } - - errCode := requestValue(resultData.Request) - if errCode == "CAPCHA_NOT_READY" { - fmt.Printf("[*] Not ready yet, waiting %ds...\n", pollInterval) - time.Sleep(time.Duration(pollInterval) * time.Second) - elapsed += pollInterval - backoff = pollInterval - continue - } - - if transientErrors[errCode] { - fmt.Printf("[!] Transient error: %s, retrying in %ds...\n", errCode, backoff) - time.Sleep(time.Duration(backoff) * time.Second) - elapsed += backoff - if backoff < 30 { - backoff *= 2 - } - continue - } - - if solveErrors[errCode] { - fmt.Printf("[!] Solve error: %s\n", errCode) - fmt.Println(" The CAPTCHA could not be solved. Verify parameters and retry.") - os.Exit(1) - } - - if proxyErrors[errCode] { - fmt.Printf("[!] Proxy error: %s\n", errCode) - fmt.Println(" Check your proxy configuration or try a different proxy.") - os.Exit(1) - } - - fmt.Printf("[!] Unexpected error: %s\n", errCode) - os.Exit(1) - } - - fmt.Printf("[!] Timeout: no solution received within %d seconds.\n", maxTimeout) - os.Exit(1) -} diff --git a/articles/parsehub-captchaai-no-code-scraping/java/Solve.java b/articles/parsehub-captchaai-no-code-scraping/java/Solve.java deleted file mode 100644 index cdec87d..0000000 --- a/articles/parsehub-captchaai-no-code-scraping/java/Solve.java +++ /dev/null @@ -1,236 +0,0 @@ -/** - * Solve reCAPTCHA v2 using the CaptchaAI API. - * - * Usage: - * javac Solve.java - * java Solve - * - * Requires .env file in parent directory with: - * CAPTCHAAI_API_KEY and CAPTCHA-specific variables - */ - -import java.io.*; -import java.net.*; -import java.nio.file.*; -import java.util.*; - -public class Solve { - - private static final String SUBMIT_URL = "https://ocr.captchaai.com/in.php"; - private static final String RESULT_URL = "https://ocr.captchaai.com/res.php"; - - private static final Set AUTH_ERRORS = Set.of( - "ERROR_WRONG_USER_KEY", "ERROR_KEY_DOES_NOT_EXIST", "IP_BANNED"); - private static final Set BALANCE_ERRORS = Set.of("ERROR_ZERO_BALANCE"); - private static final Set INPUT_ERRORS = Set.of( - "ERROR_PAGEURL", "ERROR_WRONG_GOOGLEKEY", "ERROR_BAD_PARAMETERS", "ERROR_BAD_TOKEN_OR_PAGEURL"); - private static final Set TRANSIENT_ERRORS = Set.of( - "ERROR_SERVER_ERROR", "ERROR_INTERNAL_SERVER_ERROR"); - private static final Set SOLVE_ERRORS = Set.of("ERROR_CAPTCHA_UNSOLVABLE"); - private static final Set PROXY_ERRORS = Set.of( - "ERROR_BAD_PROXY", "ERROR_PROXY_CONNECTION_FAILED"); - - private final String apiKey; - private final int pollInterval; - private final int maxTimeout; - private final String googlekey; - private final String pageurl; - - public Solve(Map env) { - this.apiKey = env.getOrDefault("CAPTCHAAI_API_KEY", ""); - this.googlekey = env.getOrDefault("CAPTCHA_SITEKEY", ""); - this.pageurl = env.getOrDefault("CAPTCHA_PAGEURL", ""); - this.pollInterval = Integer.parseInt(env.getOrDefault("POLL_INTERVAL", "5")); - this.maxTimeout = Integer.parseInt(env.getOrDefault("MAX_TIMEOUT", "120")); - } - - private void validateConfig() { - if (apiKey.isEmpty() || "YOUR_API_KEY".equals(apiKey)) { - System.out.println("[!] ERROR: CAPTCHAAI_API_KEY is not set."); - System.out.println(" Copy .env.example to .env and add your real API key."); - System.exit(1); - } - } - - private String httpGet(String urlStr) throws IOException { - URL url = URI.create(urlStr).toURL(); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - conn.setRequestMethod("GET"); - conn.setConnectTimeout(30000); - conn.setReadTimeout(30000); - try (BufferedReader reader = new BufferedReader( - new InputStreamReader(conn.getInputStream()))) { - StringBuilder sb = new StringBuilder(); - String line; - while ((line = reader.readLine()) != null) sb.append(line); - return sb.toString(); - } - } - - private String submitTask() { - System.out.println("[*] Submitting reCAPTCHA v2 task..."); - String params = String.format( - "key=%s&method=userrecaptcha&googlekey=%s&pageurl=%s&json=1", - URLEncoder.encode(apiKey, java.nio.charset.StandardCharsets.UTF_8), - URLEncoder.encode(googlekey, java.nio.charset.StandardCharsets.UTF_8), - URLEncoder.encode(pageurl, java.nio.charset.StandardCharsets.UTF_8)); - - String body; - try { - body = httpGet(SUBMIT_URL + "?" + params); - } catch (IOException e) { - System.out.println("[!] Network error during submission: " + e.getMessage()); - System.exit(1); - return ""; - } - - int status = parseStatus(body); - String request = parseRequest(body); - - if (status != 1) { - classifyError(request); - System.exit(1); - } - - System.out.println("[+] Task submitted. ID: " + request); - return request; - } - - private String pollResult(String taskId) { - System.out.println("[*] Waiting 15s before first poll..."); - sleep(15); - - String params = String.format( - "key=%s&action=get&id=%s&json=1", - URLEncoder.encode(apiKey, java.nio.charset.StandardCharsets.UTF_8), - URLEncoder.encode(taskId, java.nio.charset.StandardCharsets.UTF_8)); - - int elapsed = 15, attempt = 0, backoff = pollInterval; - - while (elapsed < maxTimeout) { - attempt++; - System.out.printf("[*] Polling for result (attempt %d)...%n", attempt); - - String body; - try { - body = httpGet(RESULT_URL + "?" + params); - } catch (IOException e) { - System.out.println("[!] Network error during polling: " + e.getMessage()); - sleep(backoff); - elapsed += backoff; - backoff = Math.min(backoff * 2, 30); - continue; - } - - int status = parseStatus(body); - String request = parseRequest(body); - - if (status == 1) return request; - - if ("CAPCHA_NOT_READY".equals(request)) { - System.out.printf("[*] Not ready yet, waiting %ds...%n", pollInterval); - sleep(pollInterval); - elapsed += pollInterval; - backoff = pollInterval; - continue; - } - if (TRANSIENT_ERRORS.contains(request)) { - System.out.printf("[!] Transient error: %s, retrying in %ds...%n", request, backoff); - sleep(backoff); - elapsed += backoff; - backoff = Math.min(backoff * 2, 30); - continue; - } - if (SOLVE_ERRORS.contains(request)) { - System.out.println("[!] Solve error: " + request); - System.out.println(" The CAPTCHA could not be solved. Verify parameters and retry."); - System.exit(1); - } - if (PROXY_ERRORS.contains(request)) { - System.out.println("[!] Proxy error: " + request); - System.out.println(" Check your proxy configuration or try a different proxy."); - System.exit(1); - } - System.out.println("[!] Unexpected error: " + request); - System.exit(1); - } - - System.out.printf("[!] Timeout: no solution received within %d seconds.%n", maxTimeout); - System.exit(1); - return ""; - } - - private static void classifyError(String error) { - if (AUTH_ERRORS.contains(error)) { - System.out.println("[!] Authentication error: " + error); - System.out.println(" Check your API key at https://captchaai.com/dashboard"); - } else if (BALANCE_ERRORS.contains(error)) { - System.out.println("[!] Balance error: " + error); - System.out.println(" Top up your account at https://captchaai.com"); - } else if (INPUT_ERRORS.contains(error)) { - System.out.println("[!] Input error: " + error); - System.out.println(" Verify your sitekey and page URL are correct."); - } else if (PROXY_ERRORS.contains(error)) { - System.out.println("[!] Proxy error: " + error); - System.out.println(" Check your proxy configuration or try a different proxy."); - } else { - System.out.println("[!] Submission failed: " + error); - } - } - - private static int parseStatus(String json) { - int idx = json.indexOf("\"status\":"); - if (idx == -1) return 0; - String sub = json.substring(idx + 9).trim(); - return sub.startsWith("1") ? 1 : 0; - } - - private static String parseRequest(String json) { - int idx = json.indexOf("\"request\":"); - if (idx == -1) return "UNKNOWN_ERROR"; - String sub = json.substring(idx + 10).trim(); - if (sub.startsWith("\"")) { - int end = sub.indexOf("\"", 1); - if (end > 0) return sub.substring(1, end); - } - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < sub.length(); i++) { - char c = sub.charAt(i); - if (Character.isDigit(c)) sb.append(c); else break; - } - return sb.toString(); - } - - private static void sleep(int seconds) { - try { Thread.sleep(seconds * 1000L); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } - } - - private static Map loadEnv(String path) { - Map env = new HashMap<>(); - try { - for (String line : Files.readAllLines(Path.of(path))) { - line = line.trim(); - if (line.isEmpty() || line.startsWith("#")) continue; - String[] parts = line.split("=", 2); - if (parts.length == 2) env.put(parts[0].trim(), parts[1].trim()); - } - } catch (IOException e) { /* fall back to system env */ } - for (Map.Entry entry : System.getenv().entrySet()) { - if (entry.getValue() != null && !entry.getValue().isEmpty()) { - env.putIfAbsent(entry.getKey(), entry.getValue()); - } - } - return env; - } - - public static void main(String[] args) { - Map env = loadEnv("../.env"); - Solve solver = new Solve(env); - solver.validateConfig(); - String taskId = solver.submitTask(); - String token = solver.pollResult(taskId); - String truncated = token.length() > 50 ? token.substring(0, 50) : token; - System.out.println("[+] Solved! Token: " + truncated + "..."); - System.out.println("[+] Full token length: " + token.length() + " characters"); - } -} diff --git a/articles/parsehub-captchaai-no-code-scraping/kotlin/solve.kt b/articles/parsehub-captchaai-no-code-scraping/kotlin/solve.kt deleted file mode 100644 index f1c13fa..0000000 --- a/articles/parsehub-captchaai-no-code-scraping/kotlin/solve.kt +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Solve reCAPTCHA v2 using the CaptchaAI API. - * - * Usage: - * kotlinc solve.kt -include-runtime -d solve.jar - * java -jar solve.jar - * - * Requires .env file in parent directory with: - * CAPTCHAAI_API_KEY and CAPTCHA-specific variables - */ - -import java.io.BufferedReader -import java.io.File -import java.io.InputStreamReader -import java.net.HttpURLConnection -import java.net.URI -import java.net.URLEncoder -import kotlin.system.exitProcess - -val SUBMIT_URL = "https://ocr.captchaai.com/in.php" -val RESULT_URL = "https://ocr.captchaai.com/res.php" - -val AUTH_ERRORS = setOf("ERROR_WRONG_USER_KEY", "ERROR_KEY_DOES_NOT_EXIST", "IP_BANNED") -val BALANCE_ERRORS = setOf("ERROR_ZERO_BALANCE") -val INPUT_ERRORS = setOf("ERROR_PAGEURL", "ERROR_WRONG_GOOGLEKEY", "ERROR_BAD_PARAMETERS", "ERROR_BAD_TOKEN_OR_PAGEURL") -val TRANSIENT_ERRORS = setOf("ERROR_SERVER_ERROR", "ERROR_INTERNAL_SERVER_ERROR") -val SOLVE_ERRORS = setOf("ERROR_CAPTCHA_UNSOLVABLE") -val PROXY_ERRORS = setOf("ERROR_BAD_PROXY", "ERROR_PROXY_CONNECTION_FAILED") - -fun loadEnv(path: String): Map { - val env = mutableMapOf() - val file = File(path) - if (!file.exists()) return env - file.readLines().forEach { line -> - val trimmed = line.trim() - if (trimmed.isEmpty() || trimmed.startsWith("#")) return@forEach - val idx = trimmed.indexOf('=') - if (idx > 0) env[trimmed.substring(0, idx).trim()] = trimmed.substring(idx + 1).trim() - } - return env -} - -fun getEnv(env: Map, key: String, fallback: String = ""): String { - val fromFile = env[key] - if (!fromFile.isNullOrEmpty()) return fromFile - val fromSys = System.getenv(key) - if (!fromSys.isNullOrEmpty()) return fromSys - return fallback -} - -fun httpGet(url: String): String { - val conn = URI(url).toURL().openConnection() as HttpURLConnection - conn.requestMethod = "GET" - conn.connectTimeout = 30000 - conn.readTimeout = 30000 - return BufferedReader(InputStreamReader(conn.inputStream)).readText() -} - -fun parseStatus(json: String): Int { - val regex = """"status"\s*:\s*(\d+)""".toRegex() - return regex.find(json)?.groupValues?.get(1)?.toIntOrNull() ?: 0 -} - -fun parseRequest(json: String): String { - val strRegex = """"request"\s*:\s*"([^"]+)"""".toRegex() - strRegex.find(json)?.groupValues?.get(1)?.let { return it } - val numRegex = """"request"\s*:\s*(\d+)""".toRegex() - numRegex.find(json)?.groupValues?.get(1)?.let { return it } - return "UNKNOWN_ERROR" -} - -fun classifyError(error: String) { - when { - error in AUTH_ERRORS -> { println("[!] Authentication error: $error"); println(" Check your API key at https://captchaai.com/dashboard") } - error in BALANCE_ERRORS -> { println("[!] Balance error: $error"); println(" Top up your account at https://captchaai.com") } - error in INPUT_ERRORS -> { println("[!] Input error: $error"); println(" Verify your sitekey and page URL are correct.") } - error in PROXY_ERRORS -> { println("[!] Proxy error: $error"); println(" Check your proxy configuration or try a different proxy.") } - else -> println("[!] Submission failed: $error") - } -} - -fun main() { - val env = loadEnv("../.env") - val apiKey = getEnv(env, "CAPTCHAAI_API_KEY") - val googlekey = getEnv(env, "CAPTCHA_SITEKEY") - val pageurl = getEnv(env, "CAPTCHA_PAGEURL") - val pollInterval = getEnv(env, "POLL_INTERVAL", "5").toInt() - val maxTimeout = getEnv(env, "MAX_TIMEOUT", "120").toInt() - - if (apiKey.isEmpty() || apiKey == "YOUR_API_KEY") { - println("[!] ERROR: CAPTCHAAI_API_KEY is not set.") - println(" Copy .env.example to .env and add your real API key.") - exitProcess(1) - } - // Submit - println("[*] Submitting reCAPTCHA v2 task...") - val enc = java.nio.charset.StandardCharsets.UTF_8.name() - val submitParams = "key=${URLEncoder.encode(apiKey, enc)}&method=userrecaptcha&googlekey=${URLEncoder.encode(googlekey, enc)}&pageurl=${URLEncoder.encode(pageurl, enc)}&json=1" - - val submitBody: String - try { submitBody = httpGet("$SUBMIT_URL?$submitParams") } - catch (e: Exception) { println("[!] Network error during submission: ${e.message}"); exitProcess(1) } - - if (parseStatus(submitBody) != 1) { classifyError(parseRequest(submitBody)); exitProcess(1) } - val taskId = parseRequest(submitBody) - println("[+] Task submitted. ID: $taskId") - - // Poll - println("[*] Waiting 15s before first poll...") - Thread.sleep(15_000) - - val pollParams = "key=${URLEncoder.encode(apiKey, enc)}&action=get&id=${URLEncoder.encode(taskId, enc)}&json=1" - var elapsed = 15; var attempt = 0; var backoff = pollInterval - - while (elapsed < maxTimeout) { - attempt++ - println("[*] Polling for result (attempt $attempt)...") - - val pollBody: String - try { pollBody = httpGet("$RESULT_URL?$pollParams") } - catch (e: Exception) { - println("[!] Network error during polling: ${e.message}") - Thread.sleep(backoff * 1000L); elapsed += backoff; backoff = minOf(backoff * 2, 30); continue - } - - val rStatus = parseStatus(pollBody); val rRequest = parseRequest(pollBody) - if (rStatus == 1) { - val truncated = if (rRequest.length > 50) rRequest.substring(0, 50) else rRequest - println("[+] Solved! Token: $truncated..."); println("[+] Full token length: ${rRequest.length} characters") - exitProcess(0) - } - if (rRequest == "CAPCHA_NOT_READY") { - println("[*] Not ready yet, waiting ${pollInterval}s...") - Thread.sleep(pollInterval * 1000L); elapsed += pollInterval; backoff = pollInterval; continue - } - if (rRequest in TRANSIENT_ERRORS) { - println("[!] Transient error: $rRequest, retrying in ${backoff}s...") - Thread.sleep(backoff * 1000L); elapsed += backoff; backoff = minOf(backoff * 2, 30); continue - } - if (rRequest in SOLVE_ERRORS) { println("[!] Solve error: $rRequest"); println(" The CAPTCHA could not be solved. Verify parameters and retry."); exitProcess(1) } - if (rRequest in PROXY_ERRORS) { println("[!] Proxy error: $rRequest"); println(" Check your proxy configuration or try a different proxy."); exitProcess(1) } - println("[!] Unexpected error: $rRequest"); exitProcess(1) - } - - println("[!] Timeout: no solution received within $maxTimeout seconds."); exitProcess(1) -} diff --git a/articles/parsehub-captchaai-no-code-scraping/node/package.json b/articles/parsehub-captchaai-no-code-scraping/node/package.json deleted file mode 100644 index 98d7632..0000000 --- a/articles/parsehub-captchaai-no-code-scraping/node/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "captchaai-example-parsehub-captchaai-no-code-scraping", - "version": "1.0.0", - "description": "ParseHub + CaptchaAI: No-Code Scraping with CAPTCHA Solving - CaptchaAI example", - "main": "solve.js", - "scripts": { - "start": "node solve.js" - }, - "dependencies": { - "axios": "^1.6.0", - "dotenv": "^16.3.0" - }, - "license": "MIT" -} \ No newline at end of file diff --git a/articles/parsehub-captchaai-no-code-scraping/node/solve.js b/articles/parsehub-captchaai-no-code-scraping/node/solve.js deleted file mode 100644 index 00d490b..0000000 --- a/articles/parsehub-captchaai-no-code-scraping/node/solve.js +++ /dev/null @@ -1,151 +0,0 @@ -/** - * Solve reCAPTCHA v2 using the CaptchaAI API. - * - * Usage: - * npm install - * cp ../.env.example ../.env // then edit with your credentials - * node solve.js - */ - -require("dotenv").config({ path: require("path").join(__dirname, "..", ".env") }); -const axios = require("axios"); - -const SUBMIT_URL = "https://ocr.captchaai.com/in.php"; -const RESULT_URL = "https://ocr.captchaai.com/res.php"; - -const API_KEY = process.env.CAPTCHAAI_API_KEY || ""; -const googlekey = process.env.CAPTCHA_SITEKEY || ""; -const pageurl = process.env.CAPTCHA_PAGEURL || ""; -const POLL_INTERVAL = parseInt(process.env.POLL_INTERVAL || "5", 10); -const MAX_TIMEOUT = parseInt(process.env.MAX_TIMEOUT || "120", 10); - -const AUTH_ERRORS = new Set(["ERROR_WRONG_USER_KEY", "ERROR_KEY_DOES_NOT_EXIST", "IP_BANNED"]); -const BALANCE_ERRORS = new Set(["ERROR_ZERO_BALANCE"]); -const INPUT_ERRORS = new Set(["ERROR_PAGEURL", "ERROR_WRONG_GOOGLEKEY", "ERROR_BAD_PARAMETERS", "ERROR_BAD_TOKEN_OR_PAGEURL"]); -const TRANSIENT_ERRORS = new Set(["ERROR_SERVER_ERROR", "ERROR_INTERNAL_SERVER_ERROR"]); -const SOLVE_ERRORS = new Set(["ERROR_CAPTCHA_UNSOLVABLE"]); - -function sleep(seconds) { - return new Promise((resolve) => setTimeout(resolve, seconds * 1000)); -} - -function validateConfig() { - if (!API_KEY || API_KEY === "YOUR_API_KEY") { - console.log("[!] ERROR: CAPTCHAAI_API_KEY is not set."); - console.log(" Copy .env.example to .env and add your real API key."); - process.exit(1); - } -} - -async function submitTask() { - console.log("[*] Submitting reCAPTCHA v2 task..."); - - const params = { - key: API_KEY, - method: "userrecaptcha", - googlekey, - pageurl, - json: "1", - }; - - let resp; - try { - resp = await axios.get(SUBMIT_URL, { params, timeout: 30000 }); - } catch (err) { - console.log(`[!] Network error during submission: ${err.message}`); - process.exit(1); - } - - const data = resp.data; - if (data.status !== 1) { - const error = data.request || "UNKNOWN_ERROR"; - if (AUTH_ERRORS.has(error)) { - console.log(`[!] Authentication error: ${error}`); - console.log(" Check your API key at https://captchaai.com/dashboard"); - } else if (BALANCE_ERRORS.has(error)) { - console.log(`[!] Balance error: ${error}`); - console.log(" Top up your account at https://captchaai.com"); - } else if (INPUT_ERRORS.has(error)) { - console.log(`[!] Input error: ${error}`); - console.log(" Verify your sitekey and page URL are correct."); - } else { - console.log(`[!] Submission failed: ${error}`); - } - process.exit(1); - } - - const taskId = data.request; - console.log(`[+] Task submitted. ID: ${taskId}`); - return taskId; -} - -async function pollResult(taskId) { - console.log("[*] Waiting 15s before first poll..."); - await sleep(15); - - const params = { key: API_KEY, action: "get", id: taskId, json: "1" }; - let elapsed = 15; - let attempt = 0; - let backoff = POLL_INTERVAL; - - while (elapsed < MAX_TIMEOUT) { - attempt++; - console.log(`[*] Polling for result (attempt ${attempt})...`); - - let resp; - try { - resp = await axios.get(RESULT_URL, { params, timeout: 30000 }); - } catch (err) { - console.log(`[!] Network error during polling: ${err.message}`); - await sleep(backoff); - elapsed += backoff; - backoff = Math.min(backoff * 2, 30); - continue; - } - - const data = resp.data; - - if (data.status === 1) { - return data.request; - } - - const error = data.request || ""; - if (error === "CAPCHA_NOT_READY") { - console.log(`[*] Not ready yet, waiting ${POLL_INTERVAL}s...`); - await sleep(POLL_INTERVAL); - elapsed += POLL_INTERVAL; - backoff = POLL_INTERVAL; - continue; - } - - if (TRANSIENT_ERRORS.has(error)) { - console.log(`[!] Transient error: ${error}, retrying in ${backoff}s...`); - await sleep(backoff); - elapsed += backoff; - backoff = Math.min(backoff * 2, 30); - continue; - } - - if (SOLVE_ERRORS.has(error)) { - console.log(`[!] Solve error: ${error}`); - console.log(" The CAPTCHA could not be solved. Verify parameters and retry."); - process.exit(1); - } - - console.log(`[!] Unexpected error: ${error}`); - process.exit(1); - } - - console.log(`[!] Timeout: no solution received within ${MAX_TIMEOUT} seconds.`); - process.exit(1); -} - -async function main() { - validateConfig(); - const taskId = await submitTask(); - const token = await pollResult(taskId); - console.log(`[+] Solved! Token: ${token.substring(0, 50)}...`); - console.log(`[+] Full token length: ${token.length} characters`); -} - -main(); diff --git a/articles/parsehub-captchaai-no-code-scraping/php/composer.json b/articles/parsehub-captchaai-no-code-scraping/php/composer.json deleted file mode 100644 index ec9bc82..0000000 --- a/articles/parsehub-captchaai-no-code-scraping/php/composer.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "captchaai/example-parsehub-captchaai-no-code-scraping", - "description": "ParseHub + CaptchaAI: No-Code Scraping with CAPTCHA Solving - CaptchaAI example", - "require": { - "vlucas/phpdotenv": "^5.5" - } -} \ No newline at end of file diff --git a/articles/parsehub-captchaai-no-code-scraping/php/solve.php b/articles/parsehub-captchaai-no-code-scraping/php/solve.php deleted file mode 100644 index 60798f0..0000000 --- a/articles/parsehub-captchaai-no-code-scraping/php/solve.php +++ /dev/null @@ -1,143 +0,0 @@ -load(); - -define('SUBMIT_URL', 'https://ocr.captchaai.com/in.php'); -define('RESULT_URL', 'https://ocr.captchaai.com/res.php'); - -$apiKey = $_ENV['CAPTCHAAI_API_KEY'] ?? ''; -$googlekey = $_ENV['CAPTCHA_SITEKEY'] ?? ''; -$pageurl = $_ENV['CAPTCHA_PAGEURL'] ?? ''; -$pollInterval = (int)($_ENV['POLL_INTERVAL'] ?? 5); -$maxTimeout = (int)($_ENV['MAX_TIMEOUT'] ?? 120); - -$authErrors = ['ERROR_WRONG_USER_KEY', 'ERROR_KEY_DOES_NOT_EXIST', 'IP_BANNED']; -$balanceErrors = ['ERROR_ZERO_BALANCE']; -$inputErrors = ['ERROR_PAGEURL', 'ERROR_WRONG_GOOGLEKEY', 'ERROR_BAD_PARAMETERS', 'ERROR_BAD_TOKEN_OR_PAGEURL']; -$transientErrors = ['ERROR_SERVER_ERROR', 'ERROR_INTERNAL_SERVER_ERROR']; -$solveErrors = ['ERROR_CAPTCHA_UNSOLVABLE']; - -// --- Validate configuration --- -if (empty($apiKey) || $apiKey === 'YOUR_API_KEY') { - echo "[!] ERROR: CAPTCHAAI_API_KEY is not set.\n"; - echo " Copy .env.example to .env and add your real API key.\n"; - exit(1); -} - -// --- Submit task --- -echo "[*] Submitting reCAPTCHA v2 task...\n"; -$submitParams = http_build_query([ - 'key' => $apiKey, - 'method' => 'userrecaptcha', - 'googlekey' => $googlekey, - 'pageurl' => $pageurl, - 'json' => '1', -]); - -$submitResponse = @file_get_contents(SUBMIT_URL . '?' . $submitParams); -if ($submitResponse === false) { - echo "[!] Network error during submission.\n"; - exit(1); -} - -$submitData = json_decode($submitResponse, true); -if (($submitData['status'] ?? 0) !== 1) { - $error = $submitData['request'] ?? 'UNKNOWN_ERROR'; - if (in_array($error, $authErrors)) { - echo "[!] Authentication error: $error\n"; - echo " Check your API key at https://captchaai.com/dashboard\n"; - } elseif (in_array($error, $balanceErrors)) { - echo "[!] Balance error: $error\n"; - echo " Top up your account at https://captchaai.com\n"; - } elseif (in_array($error, $inputErrors)) { - echo "[!] Input error: $error\n"; - echo " Verify your sitekey and page URL are correct.\n"; - } else { - echo "[!] Submission failed: $error\n"; - } - exit(1); -} - -$taskId = $submitData['request']; -echo "[+] Task submitted. ID: $taskId\n"; - -// --- Poll for result --- -echo "[*] Waiting 15s before first poll...\n"; -sleep(15); - -$elapsed = 15; -$attempt = 0; -$backoff = $pollInterval; - -while ($elapsed < $maxTimeout) { - $attempt++; - echo "[*] Polling for result (attempt $attempt)...\n"; - - $resultParams = http_build_query([ - 'key' => $apiKey, - 'action' => 'get', - 'id' => $taskId, - 'json' => '1', - ]); - - $resultResponse = @file_get_contents(RESULT_URL . '?' . $resultParams); - if ($resultResponse === false) { - echo "[!] Network error during polling.\n"; - sleep($backoff); - $elapsed += $backoff; - $backoff = min($backoff * 2, 30); - continue; - } - - $resultData = json_decode($resultResponse, true); - - if (($resultData['status'] ?? 0) === 1) { - $token = $resultData['request']; - echo "[+] Solved! Token: " . substr($token, 0, 50) . "...\n"; - echo "[+] Full token length: " . strlen($token) . " characters\n"; - echo "\n"; - echo "Next step: inject this token into the target page's\n"; - echo "g-recaptcha-response hidden field and submit the form.\n"; - exit(0); - } - - $error = $resultData['request'] ?? ''; - if ($error === 'CAPCHA_NOT_READY') { - echo "[*] Not ready yet, waiting {$pollInterval}s...\n"; - sleep($pollInterval); - $elapsed += $pollInterval; - $backoff = $pollInterval; - continue; - } - - if (in_array($error, $transientErrors)) { - echo "[!] Transient error: $error, retrying in {$backoff}s...\n"; - sleep($backoff); - $elapsed += $backoff; - $backoff = min($backoff * 2, 30); - continue; - } - - if (in_array($error, $solveErrors)) { - echo "[!] Solve error: $error\n"; - echo " The CAPTCHA could not be solved. Verify parameters and retry.\n"; - exit(1); - } - - echo "[!] Unexpected error: $error\n"; - exit(1); -} - -echo "[!] Timeout: no solution received within {$maxTimeout} seconds.\n"; -exit(1); diff --git a/articles/parsehub-captchaai-no-code-scraping/python/requirements.txt b/articles/parsehub-captchaai-no-code-scraping/python/requirements.txt deleted file mode 100644 index f49b6b8..0000000 --- a/articles/parsehub-captchaai-no-code-scraping/python/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -requests>=2.28.0 -python-dotenv>=1.0.0 diff --git a/articles/parsehub-captchaai-no-code-scraping/python/solve.py b/articles/parsehub-captchaai-no-code-scraping/python/solve.py deleted file mode 100644 index ab3f070..0000000 --- a/articles/parsehub-captchaai-no-code-scraping/python/solve.py +++ /dev/null @@ -1,154 +0,0 @@ -"""Solve reCAPTCHA v2 using the CaptchaAI API. - -Usage: - pip install -r requirements.txt - cp ../.env.example ../.env # then edit with your credentials - python solve.py -""" - -import os -import sys -import time - -import requests -from dotenv import load_dotenv - -# Load environment variables from the parent .env file -load_dotenv(dotenv_path=os.path.join(os.path.dirname(__file__), "..", ".env")) - -SUBMIT_URL = "https://ocr.captchaai.com/in.php" -RESULT_URL = "https://ocr.captchaai.com/res.php" - -API_KEY = os.getenv("CAPTCHAAI_API_KEY", "") -CAPTCHA_SITEKEY = os.getenv("CAPTCHA_SITEKEY", "") -CAPTCHA_PAGEURL = os.getenv("CAPTCHA_PAGEURL", "") -POLL_INTERVAL = int(os.getenv("POLL_INTERVAL", "5")) -MAX_TIMEOUT = int(os.getenv("MAX_TIMEOUT", "120")) - -AUTH_ERRORS = {"ERROR_WRONG_USER_KEY", "ERROR_KEY_DOES_NOT_EXIST", "IP_BANNED"} -BALANCE_ERRORS = {"ERROR_ZERO_BALANCE"} -INPUT_ERRORS = {"ERROR_PAGEURL", "ERROR_WRONG_GOOGLEKEY", "ERROR_BAD_PARAMETERS", "ERROR_BAD_TOKEN_OR_PAGEURL"} -TRANSIENT_ERRORS = {"ERROR_SERVER_ERROR", "ERROR_INTERNAL_SERVER_ERROR"} -SOLVE_ERRORS = {"ERROR_CAPTCHA_UNSOLVABLE"} - - -def validate_config() -> None: - """Check that required environment variables are set.""" - if not API_KEY or API_KEY == "YOUR_API_KEY": - print("[!] ERROR: CAPTCHAAI_API_KEY is not set.") - print(" Copy .env.example to .env and add your real API key.") - sys.exit(1) - - -def submit_task() -> str: - """Submit the CAPTCHA task and return the task ID.""" - print("[*] Submitting reCAPTCHA v2 task...") - payload = { - "key": API_KEY, - "method": "userrecaptcha", - "googlekey": CAPTCHA_SITEKEY, - "pageurl": CAPTCHA_PAGEURL, - "json": "1", - } - - try: - resp = requests.get(SUBMIT_URL, params=payload, timeout=30) - resp.raise_for_status() - except requests.RequestException as exc: - print(f"[!] Network error during submission: {exc}") - sys.exit(1) - - data = resp.json() - - if data.get("status") != 1: - error = data.get("request", "UNKNOWN_ERROR") - if error in AUTH_ERRORS: - print(f"[!] Authentication error: {error}") - print(" Check your API key at https://captchaai.com/dashboard") - elif error in BALANCE_ERRORS: - print(f"[!] Balance error: {error}") - print(" Top up your account at https://captchaai.com") - elif error in INPUT_ERRORS: - print(f"[!] Input error: {error}") - print(" Verify your sitekey and page URL are correct.") - else: - print(f"[!] Submission failed: {error}") - sys.exit(1) - - task_id = data["request"] - print(f"[+] Task submitted. ID: {task_id}") - return task_id - - -def poll_result(task_id: str) -> str: - """Poll for the CAPTCHA solution with timeout and retry logic.""" - print("[*] Waiting 15s before first poll...") - time.sleep(15) - - params = { - "key": API_KEY, - "action": "get", - "id": task_id, - "json": "1", - } - - elapsed = 15 - attempt = 0 - backoff = POLL_INTERVAL - - while elapsed < MAX_TIMEOUT: - attempt += 1 - print(f"[*] Polling for result (attempt {attempt})...") - - try: - resp = requests.get(RESULT_URL, params=params, timeout=30) - resp.raise_for_status() - except requests.RequestException as exc: - print(f"[!] Network error during polling: {exc}") - time.sleep(backoff) - elapsed += backoff - backoff = min(backoff * 2, 30) - continue - - data = resp.json() - - if data.get("status") == 1: - return data["request"] - - error = data.get("request", "") - if error == "CAPCHA_NOT_READY": - print(f"[*] Not ready yet, waiting {POLL_INTERVAL}s...") - time.sleep(POLL_INTERVAL) - elapsed += POLL_INTERVAL - backoff = POLL_INTERVAL - continue - - if error in TRANSIENT_ERRORS: - print(f"[!] Transient error: {error}, retrying in {backoff}s...") - time.sleep(backoff) - elapsed += backoff - backoff = min(backoff * 2, 30) - continue - - if error in SOLVE_ERRORS: - print(f"[!] Solve error: {error}") - print(" The CAPTCHA could not be solved. Verify parameters and retry.") - sys.exit(1) - - print(f"[!] Unexpected error: {error}") - sys.exit(1) - - print(f"[!] Timeout: no solution received within {MAX_TIMEOUT} seconds.") - sys.exit(1) - - -def main() -> None: - validate_config() - task_id = submit_task() - token = poll_result(task_id) - print(f"[+] Solved! Token: {token[:50]}...") - print(f"[+] Full token length: {len(token)} characters") - - -if __name__ == "__main__": - main() diff --git a/articles/parsehub-captchaai-no-code-scraping/ruby/Gemfile b/articles/parsehub-captchaai-no-code-scraping/ruby/Gemfile deleted file mode 100644 index 7ffdf63..0000000 --- a/articles/parsehub-captchaai-no-code-scraping/ruby/Gemfile +++ /dev/null @@ -1,3 +0,0 @@ -source "https://rubygems.org" - -# No external gems needed — uses Ruby stdlib (net/http, json, uri) diff --git a/articles/parsehub-captchaai-no-code-scraping/ruby/solve.rb b/articles/parsehub-captchaai-no-code-scraping/ruby/solve.rb deleted file mode 100644 index 0f5d5d8..0000000 --- a/articles/parsehub-captchaai-no-code-scraping/ruby/solve.rb +++ /dev/null @@ -1,168 +0,0 @@ -# Solve reCAPTCHA v2 using the CaptchaAI API. -# -# Usage: -# ruby solve.rb -# -# Requires .env file in parent directory with: -# CAPTCHAAI_API_KEY and CAPTCHA-specific variables - -require "net/http" -require "uri" -require "json" - -SUBMIT_URL = "https://ocr.captchaai.com/in.php" -RESULT_URL = "https://ocr.captchaai.com/res.php" - -AUTH_ERRORS = %w[ERROR_WRONG_USER_KEY ERROR_KEY_DOES_NOT_EXIST IP_BANNED].freeze -BALANCE_ERRORS = %w[ERROR_ZERO_BALANCE].freeze -INPUT_ERRORS = %w[ERROR_PAGEURL ERROR_WRONG_GOOGLEKEY ERROR_BAD_PARAMETERS ERROR_BAD_TOKEN_OR_PAGEURL].freeze -TRANSIENT_ERRORS = %w[ERROR_SERVER_ERROR ERROR_INTERNAL_SERVER_ERROR].freeze -SOLVE_ERRORS = %w[ERROR_CAPTCHA_UNSOLVABLE].freeze -PROXY_ERRORS = %w[ERROR_BAD_PROXY ERROR_PROXY_CONNECTION_FAILED].freeze - -def load_env(path) - env = {} - return env unless File.exist?(path) - - File.readlines(path).each do |line| - line = line.strip - next if line.empty? || line.start_with?("#") - key, value = line.split("=", 2) - env[key.strip] = value&.strip || "" - end - env -end - -def get_env(env, key, fallback = "") - val = env[key] - return val if val && !val.empty? - sys_val = ENV[key] - return sys_val if sys_val && !sys_val.empty? - fallback -end - -env = load_env(File.join(__dir__, "..", ".env")) - -API_KEY = get_env(env, "CAPTCHAAI_API_KEY") -GOOGLEKEY = get_env(env, "CAPTCHA_SITEKEY") -PAGEURL = get_env(env, "CAPTCHA_PAGEURL") -POLL_INTERVAL = get_env(env, "POLL_INTERVAL", "5").to_i -MAX_TIMEOUT = get_env(env, "MAX_TIMEOUT", "120").to_i - -def classify_error(error) - if AUTH_ERRORS.include?(error) - puts "[!] Authentication error: #{error}" - puts " Check your API key at https://captchaai.com/dashboard" - elsif BALANCE_ERRORS.include?(error) - puts "[!] Balance error: #{error}" - puts " Top up your account at https://captchaai.com" - elsif INPUT_ERRORS.include?(error) - puts "[!] Input error: #{error}" - puts " Verify your sitekey and page URL are correct." - elsif PROXY_ERRORS.include?(error) - puts "[!] Proxy error: #{error}" - puts " Check your proxy configuration or try a different proxy." - else - puts "[!] Submission failed: #{error}" - end -end - -# Validate -if API_KEY.empty? || API_KEY == "YOUR_API_KEY" - puts "[!] ERROR: CAPTCHAAI_API_KEY is not set." - puts " Copy .env.example to .env and add your real API key." - exit(1) -end - -# Submit -puts "[*] Submitting reCAPTCHA v2 task..." -submit_uri = URI.parse(SUBMIT_URL) -submit_uri.query = URI.encode_www_form( - key: API_KEY, - method: "userrecaptcha", - googlekey: GOOGLEKEY, - pageurl: PAGEURL, - json: "1" -) - -begin - response = Net::HTTP.get_response(submit_uri) - data = JSON.parse(response.body) -rescue StandardError => e - puts "[!] Network error during submission: #{e.message}" - exit(1) -end - -if data["status"] != 1 - classify_error(data["request"] || "UNKNOWN_ERROR") - exit(1) -end - -task_id = data["request"] -puts "[+] Task submitted. ID: #{task_id}" - -# Poll -puts "[*] Waiting 15s before first poll..." -sleep(15) - -elapsed = 15 -attempt = 0 -backoff = POLL_INTERVAL - -while elapsed < MAX_TIMEOUT - attempt += 1 - puts "[*] Polling for result (attempt #{attempt})..." - - poll_uri = URI.parse(RESULT_URL) - poll_uri.query = URI.encode_www_form(key: API_KEY, action: "get", id: task_id, json: "1") - - begin - response = Net::HTTP.get_response(poll_uri) - result = JSON.parse(response.body) - rescue StandardError => e - puts "[!] Network error during polling: #{e.message}" - sleep(backoff) - elapsed += backoff - backoff = [backoff * 2, 30].min - next - end - - if result["status"] == 1 - token = result["request"] - puts "[+] Solved! Token: #{token[0, 50]}..." - puts "[+] Full token length: #{token.length} characters" - exit(0) - end - - error = result["request"] || "" - if error == "CAPCHA_NOT_READY" - puts "[*] Not ready yet, waiting #{POLL_INTERVAL}s..." - sleep(POLL_INTERVAL) - elapsed += POLL_INTERVAL - backoff = POLL_INTERVAL - next - end - if TRANSIENT_ERRORS.include?(error) - puts "[!] Transient error: #{error}, retrying in #{backoff}s..." - sleep(backoff) - elapsed += backoff - backoff = [backoff * 2, 30].min - next - end - if SOLVE_ERRORS.include?(error) - puts "[!] Solve error: #{error}" - puts " The CAPTCHA could not be solved. Verify parameters and retry." - exit(1) - end - if PROXY_ERRORS.include?(error) - puts "[!] Proxy error: #{error}" - puts " Check your proxy configuration or try a different proxy." - exit(1) - end - - puts "[!] Unexpected error: #{error}" - exit(1) -end - -puts "[!] Timeout: no solution received within #{MAX_TIMEOUT} seconds." -exit(1) diff --git a/articles/parsehub-captchaai-no-code-scraping/rust/Cargo.lock b/articles/parsehub-captchaai-no-code-scraping/rust/Cargo.lock deleted file mode 100644 index c371182..0000000 --- a/articles/parsehub-captchaai-no-code-scraping/rust/Cargo.lock +++ /dev/null @@ -1,675 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "captchaai-example" -version = "1.0.0" -dependencies = [ - "serde_json", - "ureq", - "urlencoding", -] - -[[package]] -name = "cc" -version = "1.2.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" -dependencies = [ - "find-msvc-tools", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "flate2" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "getrandom" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "icu_collections" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" -dependencies = [ - "displaydoc", - "potential_utf", - "utf8_iter", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" - -[[package]] -name = "icu_properties" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" - -[[package]] -name = "icu_provider" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "libc" -version = "0.2.185" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" - -[[package]] -name = "litemap" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" - -[[package]] -name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" - -[[package]] -name = "memchr" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "potential_utf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" -dependencies = [ - "zerovec", -] - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom", - "libc", - "untrusted", - "windows-sys", -] - -[[package]] -name = "rustls" -version = "0.23.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f9466fb2c14ea04357e91413efb882e2a6d4a406e625449bc0a5d360d53a21" -dependencies = [ - "log", - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pki-types" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" -dependencies = [ - "zeroize", -] - -[[package]] -name = "rustls-webpki" -version = "0.103.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8279bb85272c9f10811ae6a6c547ff594d6a7f3c6c6b02ee9726d1d0dcfcdd06" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.149" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "simd-adler32" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tinystr" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "ureq" -version = "2.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" -dependencies = [ - "base64", - "flate2", - "log", - "once_cell", - "rustls", - "rustls-pki-types", - "url", - "webpki-roots 0.26.11", -] - -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "webpki-roots" -version = "0.26.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" -dependencies = [ - "webpki-roots 1.0.7", -] - -[[package]] -name = "webpki-roots" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "writeable" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" - -[[package]] -name = "yoke" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zerofrom" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" - -[[package]] -name = "zerotrie" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zmij" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/articles/parsehub-captchaai-no-code-scraping/rust/Cargo.toml b/articles/parsehub-captchaai-no-code-scraping/rust/Cargo.toml deleted file mode 100644 index 1d684ba..0000000 --- a/articles/parsehub-captchaai-no-code-scraping/rust/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "captchaai-example" -version = "1.0.0" -edition = "2021" - -[dependencies] -ureq = "2" -serde_json = "1" -urlencoding = "2" diff --git a/articles/parsehub-captchaai-no-code-scraping/rust/src/main.rs b/articles/parsehub-captchaai-no-code-scraping/rust/src/main.rs deleted file mode 100644 index de3b953..0000000 --- a/articles/parsehub-captchaai-no-code-scraping/rust/src/main.rs +++ /dev/null @@ -1,169 +0,0 @@ -// Solve reCAPTCHA v2 using the CaptchaAI API. -// -// Usage: -// cargo run -// -// Requires .env file in parent directory with: -// CAPTCHAAI_API_KEY and CAPTCHA-specific variables - -use std::collections::HashSet; -use std::fs; -use std::thread; -use std::time::Duration; - -fn load_env(path: &str) -> std::collections::HashMap { - let mut env = std::collections::HashMap::new(); - if let Ok(contents) = fs::read_to_string(path) { - for line in contents.lines() { - let line = line.trim(); - if line.is_empty() || line.starts_with('#') { continue; } - if let Some((key, value)) = line.split_once('=') { - env.insert(key.trim().to_string(), value.trim().to_string()); - } - } - } - env -} - -fn get_env(env: &std::collections::HashMap, key: &str, fallback: &str) -> String { - if let Some(val) = env.get(key) { if !val.is_empty() { return val.clone(); } } - if let Ok(val) = std::env::var(key) { if !val.is_empty() { return val; } } - fallback.to_string() -} - -fn classify_error(error: &str, auth: &HashSet<&str>, balance: &HashSet<&str>, - input: &HashSet<&str>, proxy: &HashSet<&str>) { - if auth.contains(error) { - println!("[!] Authentication error: {}", error); - println!(" Check your API key at https://captchaai.com/dashboard"); - } else if balance.contains(error) { - println!("[!] Balance error: {}", error); - println!(" Top up your account at https://captchaai.com"); - } else if input.contains(error) { - println!("[!] Input error: {}", error); - println!(" Verify your sitekey and page URL are correct."); - } else if proxy.contains(error) { - println!("[!] Proxy error: {}", error); - println!(" Check your proxy configuration or try a different proxy."); - } else { - println!("[!] Submission failed: {}", error); - } -} - -fn main() { - let env = load_env("../.env"); - - let api_key = get_env(&env, "CAPTCHAAI_API_KEY", ""); - let googlekey = get_env(&env, "CAPTCHA_SITEKEY", ""); - let pageurl = get_env(&env, "CAPTCHA_PAGEURL", ""); - let poll_interval: u64 = get_env(&env, "POLL_INTERVAL", "5").parse().unwrap_or(5); - let max_timeout: u64 = get_env(&env, "MAX_TIMEOUT", "120").parse().unwrap_or(120); - - let auth_errors: HashSet<&str> = ["ERROR_WRONG_USER_KEY", "ERROR_KEY_DOES_NOT_EXIST", "IP_BANNED"].into(); - let balance_errors: HashSet<&str> = ["ERROR_ZERO_BALANCE"].into(); - let input_errors: HashSet<&str> = ["ERROR_PAGEURL", "ERROR_WRONG_GOOGLEKEY", "ERROR_BAD_PARAMETERS", "ERROR_BAD_TOKEN_OR_PAGEURL"].into(); - let transient_errors: HashSet<&str> = ["ERROR_SERVER_ERROR", "ERROR_INTERNAL_SERVER_ERROR"].into(); - let solve_errors: HashSet<&str> = ["ERROR_CAPTCHA_UNSOLVABLE"].into(); - let proxy_errors: HashSet<&str> = ["ERROR_BAD_PROXY", "ERROR_PROXY_CONNECTION_FAILED"].into(); - - if api_key.is_empty() || api_key == "YOUR_API_KEY" { - println!("[!] ERROR: CAPTCHAAI_API_KEY is not set."); - println!(" Copy .env.example to .env and add your real API key."); - std::process::exit(1); - } - - // Submit - println!("[*] Submitting reCAPTCHA v2 task..."); - let submit_url = format!( - "https://ocr.captchaai.com/in.php?key={}&method=userrecaptcha&googlekey={}&pageurl={}&json=1", - urlencoding::encode(&api_key), urlencoding::encode(&googlekey), urlencoding::encode(&pageurl) - ); - - let client = ureq::AgentBuilder::new() - .timeout_connect(Duration::from_secs(30)) - .timeout_read(Duration::from_secs(30)) - .build(); - - let submit_resp = match client.get(&submit_url).call() { - Ok(resp) => resp.into_string().unwrap_or_default(), - Err(e) => { println!("[!] Network error during submission: {}", e); std::process::exit(1); } - }; - - let submit_data: serde_json::Value = serde_json::from_str(&submit_resp).unwrap_or_default(); - let status = submit_data["status"].as_i64().unwrap_or(0); - let request = submit_data["request"].as_str().unwrap_or("UNKNOWN_ERROR"); - - if status != 1 { - classify_error(request, &auth_errors, &balance_errors, &input_errors, &proxy_errors); - std::process::exit(1); - } - - let task_id = request.to_string(); - println!("[+] Task submitted. ID: {}", task_id); - - // Poll - println!("[*] Waiting 15s before first poll..."); - thread::sleep(Duration::from_secs(15)); - - let mut elapsed: u64 = 15; - let mut attempt: u32 = 0; - let mut backoff = poll_interval; - - while elapsed < max_timeout { - attempt += 1; - println!("[*] Polling for result (attempt {})...", attempt); - - let poll_url = format!( - "https://ocr.captchaai.com/res.php?key={}&action=get&id={}&json=1", - urlencoding::encode(&api_key), urlencoding::encode(&task_id) - ); - - let poll_resp = match client.get(&poll_url).call() { - Ok(resp) => resp.into_string().unwrap_or_default(), - Err(e) => { - println!("[!] Network error during polling: {}", e); - thread::sleep(Duration::from_secs(backoff)); - elapsed += backoff; backoff = std::cmp::min(backoff * 2, 30); continue; - } - }; - - let result: serde_json::Value = serde_json::from_str(&poll_resp).unwrap_or_default(); - let r_status = result["status"].as_i64().unwrap_or(0); - let r_request = result["request"].as_str().unwrap_or(""); - - if r_status == 1 { - let token = r_request; - let truncated = if token.len() > 50 { &token[..50] } else { token }; - println!("[+] Solved! Token: {}...", truncated); - println!("[+] Full token length: {} characters", token.len()); - std::process::exit(0); - } - - if r_request == "CAPCHA_NOT_READY" { - println!("[*] Not ready yet, waiting {}s...", poll_interval); - thread::sleep(Duration::from_secs(poll_interval)); - elapsed += poll_interval; backoff = poll_interval; continue; - } - if transient_errors.contains(r_request) { - println!("[!] Transient error: {}, retrying in {}s...", r_request, backoff); - thread::sleep(Duration::from_secs(backoff)); - elapsed += backoff; backoff = std::cmp::min(backoff * 2, 30); continue; - } - if solve_errors.contains(r_request) { - println!("[!] Solve error: {}", r_request); - println!(" The CAPTCHA could not be solved. Verify parameters and retry."); - std::process::exit(1); - } - if proxy_errors.contains(r_request) { - println!("[!] Proxy error: {}", r_request); - println!(" Check your proxy configuration or try a different proxy."); - std::process::exit(1); - } - - println!("[!] Unexpected error: {}", r_request); - std::process::exit(1); - } - - println!("[!] Timeout: no solution received within {} seconds.", max_timeout); - std::process::exit(1); -} \ No newline at end of file