استخدام CAPTCHA API مع Selenium بلغة C#
استدعِ solvethree باستخدام HttpClient، وانتظر حتى تصبح المهمة جاهزة، ثم ضع solution.token في حقل CAPTCHA بتطبيقك. يجب أن يستخدم Chrome الوكيل نفسه وهوية المتصفح المُعادة قبل فتح الصفحة المستهدفة.
يستخدم المثال أدناه .NET 8 وSelenium Chrome لإرسال نموذج تملكه. وهو تكامل REST مباشر، وليس SDK رسميًا لـ C# أو إضافة لـ Selenium.
في هذه الصفحة
جهّز المشروع والوجهة المستهدفة
- استخدم مشروع تطبيق وحدة تحكم .NET 8 مع تفعيل توجيهات using الضمنية الافتراضية. أضف حزمتَي NuGet
Selenium.WebDriverوSelenium.Support، كلتيهما بالإصدار4.52.0. - ثبّت Chrome محليًا. يوفّر Selenium Manager برنامج تشغيل متوافقًا مع Chrome.
- تأكد من وجود ملف تعريف موقع نشط لاسم المضيف المستهدف ومتاح لمساحة عملك. إرسال اسم مضيف وإجراء لا ينشئ هذا الملف؛ اتبع أولًا تعليمات إعداد ملف تعريف الموقع.
- استخدم وكيلًا يعتمد قائمة عناوين IP المسموح بها، دون بيانات اعتماد في عنوان URL الخاص به. يمرّر المثال الوكيل نفسه إلى solvethree وChrome.
| المتغير | ما يجب توفيره |
|---|---|
SOLVETHREE_API_KEY | مفتاح API لمساحة عملك. احتفظ به على الجهاز الموثوق الذي يشغّل الأتمتة، لا في الصفحة. |
SOLVETHREE_PROXY | عنوان URL للوكيل الذي يعتمد قائمة عناوين IP المسموح بها، والمستخدم للحل والإرسال عبر المتصفح معًا. |
TARGET_URL | عنوان URL الكامل لصفحة نموذج تطبيقك. يستخرج المثال اسم المضيف منه لحقل site في المهمة. |
غيّر action في المثال من login إذا كان تكاملك يستخدم إجراءً آخر. يعتمد المثال على القيمة الافتراضية لـ recaptchaType، وهي enterprise-v3؛ ولا يحتاج طلب المهمة إلى حقل type أو url.
أنشئ المهمة وأرسل النموذج
تجري الدالة المساعدة لواجهة API المصادقة بمفتاح مساحة عملك، وترفض استجابات HTTP غير الناجحة قبل تحليل حمولة نجاح. ينشئ البرنامج مهمة واحدة، ويستعلم دوريًا عن معرّفها ما دامت الحالة pending أو solving، ولا يشغّل Chrome إلا بعد أن تصبح المهمة ready.
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json.Nodes;
var key = Environment.GetEnvironmentVariable("SOLVETHREE_API_KEY")
?? throw new Exception("Set SOLVETHREE_API_KEY");
var proxy = Environment.GetEnvironmentVariable("SOLVETHREE_PROXY")
?? throw new Exception("Set SOLVETHREE_PROXY");
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", key);
using var deadline = new CancellationTokenSource(TimeSpan.FromSeconds(180));
async Task<JsonObject> Api(HttpMethod method, string path, JsonObject? body = null)
{
using var request = new HttpRequestMessage(method, "https://api.solvethree.com/v1" + path);
if (body is not null)
request.Content = new StringContent(body.ToJsonString(), Encoding.UTF8, "application/json");
using var response = await http.SendAsync(request, deadline.Token);
response.EnsureSuccessStatusCode();
return JsonNode.Parse(await response.Content.ReadAsStringAsync(deadline.Token))!.AsObject();
}
var requestId = Guid.NewGuid().ToString(); // Save with these inputs for recovery.
var input = new JsonObject {
["site"] = new Uri(Environment.GetEnvironmentVariable("TARGET_URL") ?? throw new Exception("Set TARGET_URL")).Host, ["action"] = "login",
["proxy"] = proxy, ["requestId"] = requestId
};
var task = await Api(HttpMethod.Post, "/tasks?waitMs=0", input);
while (task["status"]!.GetValue<string>() is "pending" or "solving")
{
var id = Uri.EscapeDataString(task["id"]!.GetValue<string>());
task = await Api(HttpMethod.Get, "/tasks/" + id + "?waitMs=15000");
if (task["status"]!.GetValue<string>() is "pending" or "solving")
await Task.Delay(1000, deadline.Token);
}
if (task["status"]!.GetValue<string>() != "ready")
throw new Exception("Task failed: " + task["errorCode"]?.GetValue<string>()
+ ": " + task["error"]?.GetValue<string>());
var solution = task["solution"]!.AsObject();
// Use solution["token"], ["userAgent"] and ["secChUa"] with the original proxy.
Console.WriteLine("Ready task: " + task["id"]!.GetValue<string>());
var target = Environment.GetEnvironmentVariable("TARGET_URL") ?? throw new Exception("Set TARGET_URL");
if (!string.IsNullOrEmpty(new Uri(proxy).UserInfo)) throw new Exception("Use an IP-allowlisted proxy");
var options = new ChromeOptions();
options.AddArgument("--proxy-server=" + proxy);
options.AddArgument("--user-agent=" + solution["userAgent"]!.GetValue<string>());
using var browser = new ChromeDriver(options);
browser.ExecuteCdpCommand("Network.enable", new Dictionary<string, object>());
browser.ExecuteCdpCommand("Network.setExtraHTTPHeaders", new Dictionary<string, object> {
["headers"] = new Dictionary<string, object> { ["sec-ch-ua"] = solution["secChUa"]!.GetValue<string>() }
});
browser.Navigate().GoToUrl(target);
var wait = new WebDriverWait(browser, TimeSpan.FromSeconds(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["token"]!.GetValue<string>());
browser.FindElement(By.CssSelector("button[type=submit]")).Click();
wait.Until(driver => driver.FindElement(By.Id("success"))); // Your application's success marker.مهلة HTTP البالغة 30 ثانية، ومهلة واجهة API البالغة 180 ثانية، وانتظار Selenium البالغ 20 ثانية هي إعدادات في هذا المثال، وليست ضمانات زمنية من الخدمة. عدّلها بما يناسب سياسة المهلات في تطبيقك.
طابق آلية تمرير الرمز مع تطبيقك
- 1.
احتفظ بالوكيل والهوية المُعادة معًا
يطبّق ChromeOptions الوكيل الأصلي و
solution.userAgent. تفعّل أوامر CDP الاتصال بالشبكة وتضبطsec-ch-uaمنsolution.secChUaقبل الانتقال. حافظ على هذا الترتيب؛ لا تفتح الصفحة أولًا ثم تغيّر الهوية. - 2.
اكتب الرمز في الموضع الذي يقرأه منه التطبيق
يعثر المثال على حقل اسمه
g-recaptcha-response، ويعيّن لهsolution.token، ويطلق حدث إدخال. استبدل آلية التمرير هذه إذا كان تطبيقك يقرأ الرمز من موضع آخر. لا تفيد تعبئة الحقل إلا إذا كان طلب التطبيق الصادر يتضمن قيمته بالفعل. - 3.
استبدل محدِّدَي الإرسال والنجاح
ينقر المثال على
button[type=submit]وينتظر عنصرًا معرّفهsuccess. استخدم محدِّدات من تطبيقك الفعلي، واختر علامة نجاح تؤكد نجاح العملية المُرسلة، لا مجرد النقر على الزر.
تعني جاهزية المهمة أن الحل متاح. أما التحقق بعد الإرسال فهو منفصل: تظل الوجهة المستهدفة هي من يقرر قبول طلب التطبيق من عدمه.
تعامل مع حالات الفشل دون إنشاء مهام غير ضرورية
- تطرح الدالة المساعدة لواجهة API استثناءً عند تلقي استجابة HTTP.
افحص حالة HTTP واستجابة الخطأ. يختلف ذلك عن وصول المهمة إلى
failed.جرّب هذا: صحّح مشكلة الطلب أو المصادقة المُبلّغ عنها قبل إعادة المحاولة. لا تواصل إعادة إرسال مدخلات غير صالحة دون تغيير.
- تصل المهمة إلى الحالة
failed. اقرأ
errorCodeوerror؛ يُعدّsite-unreachableأحد أخطاء المهمة المحتملة.جرّب هذا: توقف عن الاستعلام الدوري عن تلك المهمة وعالج السبب المُبلّغ عنه. تُردّ تكلفة محاولات الحل الفاشلة.
- المهمة جاهزة، لكن الوجهة المستهدفة ترفض الإرسال.
افحص طلب التطبيق الصادر أولًا: هل يحتوي على
solution.tokenبدلًا من معرّف المهمة أو رمز قديم؟ ثم تحقق من الوكيل ووكيل المستخدم وتلميحات العميل وعنوان URL المستهدف.جرّب هذا: صحّح آلية تمرير الرمز أو عدم تطابق الهوية قبل الدفع مقابل حل آخر.
- تنتهي مهلة Selenium بعد النقر على زر الإرسال.
افحص الصفحة واستجابة التطبيق. المحدِّد
successقيمة خاصة بالمثال، وليس إشارة قبول عامة.جرّب هذا: استبدله بشرط النجاح الفعلي في تطبيقك، أو تعامل مع الرفض الذي أعاده التطبيق.
يولّد المثال requestId، لكنه لا يحفظه لك بشكل دائم. احفظه مع مدخلات المهمة الدقيقة قبل الإنشاء إذا كنت تحتاج إلى الاستعادة بعد انتهاء المهلة. إذا انتهت مهلة الإنشاء، فأعد إرسال المدخلات نفسها بالمعرّف نفسه لاستعادة المهمة الموجودة؛ لا تولّد معرّفًا جديدًا لمجرد ضياع الاستجابة. إعادة استخدام معرّف بمدخلات مختلفة تؤدي إلى تعارض. إذا كان لديك معرّف المهمة بالفعل، فاسترجع تلك المهمة بدلًا من ذلك.
المصادر ومرجع API
شغّل مهمتك الأولى.
ابدأ برصيد قدره $5 في مساحة العمل.