שימוש ב-API של CAPTCHA עם Selenium ב-Java
בקשו תחילה את האסימון, ואז הגדירו את Chrome עם אותו פרוקסי ועם ערכי userAgent ו־secChUa שהוחזרו, לפני הניווט. הכניסו את solution.token לשדה ה־CAPTCHA של היישום שלכם, שלחו את הטופס והמתינו למצב ההצלחה של היישום — ולא רק למשימה מוכנה.
הדוגמאות משתמשות ברכיב עזר ל־REST ב־Java וב־Selenium ChromeDriver. אין בהן זיהוי CAPTCHA אוטומטי או תוסף דפדפן.
בדף הזה
הכנת פרויקט Java ואתר היעד
- 1.
הגדרת הפרויקט
השתמשו ב־Java 21 ואילך. בפרויקט Maven, הגדירו את רמת הקומפילציה של Java ל־21 והוסיפו את Gson 2.15.0 ואת
org.seleniumhq.selenium:selenium-java:4.52.0. הגדירו את Chrome ואת ChromeDriver בעזרת תיעוד Chrome של Selenium. - 2.
שמירת שני קובצי Java
שמרו את הדוגמה הראשונה בשם
src/main/java/Solve.javaואת השנייה בשםsrc/main/java/BrowserSolve.java. השאירו אותן באותה חבילה; בקבצים שסופקו אין הצהרת חבילה.BrowserSolveקורא ל־Solve.api, ולא ל־Solve.main. - 3.
הפעלת פרופיל האתר
הגדירו את שם המארח של היעד בקונסולת solvethree וודאו שהפרופיל שלו פעיל עבור סביבת העבודה שלכם. שליחת שם מארח ופעולה אינה יוצרת פרופיל. עיינו בהגדרת פרופיל אתר.
- 4.
הגדרת הסביבה
הגדירו את שלושת משתני הסביבה שלהלן בתהליך שיריץ את
BrowserSolve. שמרו את מפתח ה־API של סביבת העבודה בתהליך Java המהימן שלכם, ולא בדף או ב־JavaScript של הדפדפן.
| משתנה | ערך |
|---|---|
SOLVETHREE_API_KEY | מפתח ה־API של סביבת העבודה שלכם. |
SOLVETHREE_PROXY | כתובת URL של פרוקסי HTTP עם רשימת כתובות IP מורשות, ללא שם משתמש או סיסמה מוטמעים, נתיב, שאילתה או מקטע. |
TARGET_URL | כתובת ה־URL המלאה של דף היישום שמכיל את הטופס. |
דוגמה זו דורשת פרוקסי עם רשימת כתובות IP מורשות, משום שאינה תומכת בהעברת פרטי הזדהות לפרוקסי דרך כתובת ה־URL של הפרוקסי ב־Chrome. אותו ערך פרוקסי מועבר ל־API המשימות ול־Chrome.
הוספת Solve.java לבקשות API
Solve.api שולח בקשות מאומתות אל https://api.solvethree.com/v1, בודק את מצב ה־HTTP לפני פענוח תגובת הצלחה ומגביל כל בקשה לזמן שנותר עד למועד הסיום המקומי.
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.UUID;
public class Solve {
static final HttpClient HTTP = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(15)).build();
static final String KEY = System.getenv("SOLVETHREE_API_KEY");
static JsonObject api(String path, JsonObject body, long deadline) throws Exception {
long remaining = deadline - System.nanoTime();
if (remaining <= 0) throw new IllegalStateException("Keep the task ID and resume polling");
var builder = HttpRequest.newBuilder(URI.create("https://api.solvethree.com/v1" + path))
.timeout(Duration.ofNanos(Math.min(remaining, Duration.ofSeconds(30).toNanos())))
.header("Authorization", "Bearer " + KEY)
.header("Content-Type", "application/json");
if (body == null) builder.GET();
else builder.POST(HttpRequest.BodyPublishers.ofString(body.toString()));
var response = HTTP.send(builder.build(), HttpResponse.BodyHandlers.ofString());
if (response.statusCode() < 200 || response.statusCode() >= 300)
throw new IllegalStateException("API HTTP " + response.statusCode());
return JsonParser.parseString(response.body()).getAsJsonObject();
}
public static void main(String[] args) throws Exception {
String proxy = System.getenv("SOLVETHREE_PROXY");
if (KEY == null || proxy == null) throw new IllegalArgumentException("Set the API key and proxy");
var input = new JsonObject();
input.addProperty("site", "accounts.example.com");
input.addProperty("action", "login");
input.addProperty("proxy", proxy);
input.addProperty("requestId", UUID.randomUUID().toString());
// Save input before creation if the task must be recovered after restart.
long deadline = System.nanoTime() + Duration.ofSeconds(180).toNanos();
var task = api("/tasks?waitMs=0", input, deadline);
while (task.get("status").getAsString().equals("pending") || task.get("status").getAsString().equals("solving")) {
String id = URLEncoder.encode(task.get("id").getAsString(), StandardCharsets.UTF_8);
task = api("/tasks/" + id + "?waitMs=15000", null, deadline);
if (task.get("status").getAsString().equals("pending") || task.get("status").getAsString().equals("solving"))
Thread.sleep(1000);
}
if (!task.get("status").getAsString().equals("ready"))
throw new IllegalStateException("Task failed: " + task.get("errorCode")
+ ": " + task.get("error"));
var solution = task.getAsJsonObject("solution");
// Submit solution.token with userAgent/secChUa and the original proxy.
System.out.println("Ready task: " + task.get("id").getAsString());
}
}מתודת main העצמאית מדגימה יצירה ותשאול של משימות. שם המארח accounts.example.com והפעולה login שבה הם מצייני מיקום. לאינטגרציה עם Selenium, השתמשו בקובץ זה כרכיב עזר והריצו במקומו את BrowserSolve; הרצת שתי מתודות main תיצור משימות נפרדות.
בקשת reCAPTCHA דורשת שם מארח ב־site, את ה־action של היישום ובדיוק אחד מבין proxy או proxyCandidates. אל תוסיפו type או url. השמטת recaptchaType בוחרת ב־enterprise-v3, שבו משתמשות הדוגמאות האלה.
הפעלת Chrome עם הזהות של הפתרון המוכן
BrowserSolve גוזר את site מתוך TARGET_URL, יוצר את המשימה ומתשאל כל עוד מצבה הוא pending או solving. רק משימה במצב ready מספקת את הפתרון שבו משתמש הדפדפן.
// Save as BrowserSolve.java beside Solve.java from the Java REST quickstart.
// Add Selenium Java 4.52.0 and Gson 2.15.0 to your Maven dependencies; Java 21+.
import com.google.gson.JsonObject;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Map;
import java.util.UUID;
import org.openqa.selenium.By;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.support.ui.WebDriverWait;
public class BrowserSolve {
public static void main(String[] args) throws Exception {
String target = System.getenv("TARGET_URL");
String proxy = System.getenv("SOLVETHREE_PROXY");
URI proxyUri = URI.create(proxy);
if (proxyUri.getUserInfo() != null) throw new IllegalArgumentException("Use an IP-allowlisted proxy");
var input = new JsonObject();
input.addProperty("site", URI.create(target).getHost());
input.addProperty("action", "login");
input.addProperty("proxy", proxy);
input.addProperty("requestId", UUID.randomUUID().toString());
long deadline = System.nanoTime() + Duration.ofSeconds(180).toNanos();
var task = Solve.api("/tasks?waitMs=0", input, deadline);
while (task.get("status").getAsString().matches("pending|solving")) {
String id = URLEncoder.encode(task.get("id").getAsString(), StandardCharsets.UTF_8);
task = Solve.api("/tasks/" + id + "?waitMs=15000", null, deadline);
if (task.get("status").getAsString().matches("pending|solving")) Thread.sleep(1000);
}
if (!task.get("status").getAsString().equals("ready")) throw new IllegalStateException("Task failed");
var solution = task.getAsJsonObject("solution");
var options = new ChromeOptions();
options.addArguments("--proxy-server=" + proxy, "--user-agent=" + solution.get("userAgent").getAsString());
var browser = new ChromeDriver(options);
try {
browser.executeCdpCommand("Network.enable", Map.of());
browser.executeCdpCommand("Network.setExtraHTTPHeaders", Map.of("headers",
Map.of("sec-ch-ua", solution.get("secChUa").getAsString())));
browser.get(target);
var wait = new WebDriverWait(browser, Duration.ofSeconds(20));
var field = wait.until(driver -> driver.findElement(By.name("g-recaptcha-response")));
browser.executeScript("arguments[0].value=arguments[1]; arguments[0].dispatchEvent(new Event('input',{bubbles:true}));",
field, solution.get("token").getAsString());
browser.findElement(By.cssSelector("button[type=submit]")).click();
wait.until(driver -> driver.findElement(By.id("success"))); // Your application's success marker.
} finally { browser.quit(); }
}
}הסדר חשוב: ChromeOptions מגדיר את הפרוקסי ואת סוכן המשתמש שהוחזר, ואז ChromeDriver מגדיר את הכותרת sec-ch-ua שהוחזרה באמצעות CDP לפני browser.get(target). אל תנווטו תחילה עם זהות לא קשורה ותשנו אותה רק בעת שליחת הטוקן.
הדוגמה שולחת בכוונה proxy יחיד, כדי ש-Chrome יוכל להשתמש באותו פרוקסי בלי לבחור מבין מועמדים. הקפידו שכתובת היעד תתאים לשם המארח ולפעולה ששימשו למשימה.
התאימו את הטופס לפני הפעלת BrowserSolve
דוגמת הדפדפן מיועדת לטופס שבבעלותכם, עם שדה תגובת CAPTCHA, כפתור שליחה ורכיב הצלחה. שנו את הערכים הספציפיים ליישום לפני הפעלת BrowserSolve.main כשספריות התלות של Maven נמצאות ב-classpath שלו.
action: החליפו אתloginבפעולה שהיישום שלכם משתמש בה לשליחה זו.g-recaptcha-response: ודאו שזהו שם השדה שהטופס שלכם שולח בפועל. הדוגמה מקצה אתsolution.tokenומשגרת אירועinputשמבעבע במעלה ה-DOM.button[type=submit]: החליפו את הבורר הזה אם הוא אינו מזהה את כפתור השליחה של הטופס הרצוי.success: החליפו את מזהה הרכיב הזה בסמן שמופיע רק לאחר שהשליחה הנוכחית מצליחה.
הגדרת שדה ושיגור אירוע אינם callback אוניברסלי של CAPTCHA. אם לוגיקת הטופס ביישום שלכם אינה משתמשת בשדה או באירוע הזה, התאימו את שלב השליחה לחיבור בפועל ביישום. בדקו את הבקשה היוצאת כדי לוודא שהיא מכילה את solution.token, ולא את מזהה המשימה או טוקן ישן.
ההמתנה הסופית של WebDriver בודקת אם רכיב ההצלחה של היישום מופיע. בחרו את הסמן הזה בקפידה: רכיב שכבר היה קיים לפני השליחה אינו מוכיח דבר לגבי הבקשה החדשה. משימה מוכנה פירושה ש-solvethree יצר פתרון; תגובת היישום היא שקובעת אם היעד קיבל אותו.
טפלו בכשלים בלי ליצור משימה נוספת שלא לצורך
- ה-API מחזיר תגובת HTTP שאינה מציינת הצלחה.
בדקו את תגובת ה-HTTP בנפרד מסטטוס המשימה. Solve.api זורק חריגה לפני שהוא מתייחס אליה כאל נתוני הצלחה.
כדאי לנסות: אבחן את השגיאה בבקשה לפני ניסיון חוזר. ודאו ששדות הבקשה ופרופיל האתר הפעיל נכונים, במקום לשלוח שוב ושוב קלט לא תקין ללא שינוי.
- המשימה מגיעה לסטטוס failed.
קראו את errorCode ואת error מתגובת המשימה. החריגה הכללית של BrowserSolve אינה מציגה אותם, אף שהדוגמה העצמאית ב-Solve.java כן מציגה אותם.
כדאי לנסות: הפסיקו לתשאל את המשימה הזו וחשפו את השדות האלה בטיפול בשגיאות. על פתרונות שנכשלו ניתן החזר; אל תניחו שכל כשל נובע מבעיה ביתרת הזיכויים.
- תם הזמן הקצוב ליצירה או פג מועד הסיום המקומי.
בדקו אם כבר יש לכם מזהה משימה או ששמרתם את גוף הבקשה המקורי ואת requestId. חריגה מזמן החיבור הקצוב אינה מוכיחה שהיצירה נכשלה.
כדאי לנסות: המשיכו באחזור באמצעות מזהה המשימה, או שחזרו את היצירה באמצעות אותו requestId וקלט זהה. שמרו את גוף הבקשה לפני היצירה אם השחזור צריך להיות אפשרי גם לאחר הפעלה מחדש; הרצה חוזרת של מתודת main שסופקה יוצרת requestId חדש.
- המשימה מוכנה, אך היישום דוחה את השליחה.
בדקו את הטוקן היוצא, הפעולה, כתובת היעד, הפרוקסי, סוכן המשתמש ו-sec-ch-ua. ודאו שהיישום אכן שלח את ערך השדה שהוקצה כעת.
כדאי לנסות: תקנו את הערך שאינו תואם או את החיבור למנגנון השליחה לפני שתשלמו על פתרון נוסף. השתמשו בפרוקסי שהצליח יחד עם הזהות שהוחזרה.
מגבלת 180 השניות של ה-API והמתנת 20 השניות ליישום הן מגבלות מקומיות שנבחרו בדוגמאות, ולא התחייבויות של השירות. לפרטים נוספים על שגיאות API, ראו שגיאות CAPTCHA API.
מקורות ותיעוד API
הפעלת המשימה הראשונה שלך.
התחילו עם $5 בקרדיט לסביבת העבודה.