Captcha solver ใน C# ด้วย HttpClient: ตัวอย่าง .NET (2026)
ใน C# captcha solver คือคลาสขนาดเล็กที่ห่อ HttpClient คุณส่ง sitekey และ URL ของหน้าไปยัง Scarnox API อ่าน token จาก JSON ที่ตอบกลับ และ submit ไปยังฟอร์มของเว็บ คู่มือนี้สร้างคลาสนั้นสำหรับ .NET 8 สำหรับ PopularCaptcha ซึ่ง Scarnox แก้ได้ในวันนี้แบบ proxyless
เริ่มด้วยเครดิตทดลองใช้ฟรี ไม่ต้องใช้บัตร และลองโค้ดกับหน้าที่คุณควบคุมก่อนนำไปใช้กับสิ่งอื่น
สิ่งที่ต้องมี
- .NET 8 SDK (
dotnet --version) - Scarnox API key ที่อ่านจาก environment variable
- sitekey ของ widget PopularCaptcha ในหน้า หากต้องการความช่วยเหลือในการหา ดูวิธีหา sitekey ของ captcha
สร้างโปรเจกต์
dotnet new console -n CaptchaDemo
cd CaptchaDemo
ไม่ต้องใช้แพ็กเกจเพิ่มเติม System.Net.Http และ System.Text.Json เป็นส่วนหนึ่งของ framework อยู่แล้ว
คลาส solver
คลาสด้านล่างเป็นเจ้าของ HttpClient หนึ่งตัวที่มี Bearer header และ timeout 185 วินาที มันคืน token หรือโยน exception พร้อมข้อความจาก API
// ScarnoxSolver.cs
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json.Serialization;
public sealed class ScarnoxSolver : IDisposable
{
private const string ApiUrl = "https://api.scarnox.com/api/tasks/create";
private readonly HttpClient _http;
public ScarnoxSolver(string apiKey)
{
_http = new HttpClient { Timeout = TimeSpan.FromSeconds(185) };
_http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
}
public async Task<string> SolveAsync(
string sitekey,
string pageUrl,
bool invisible = false,
CancellationToken ct = default)
{
var body = new Dictionary<string, object>
{
["captcha_type"] = "PopularCaptchaTaskProxyless",
["site_key"] = sitekey,
["site_url"] = pageUrl,
};
if (invisible)
{
body["is_invisible"] = true;
}
// การเรียกนี้จะส่งกลับเมื่อ token พร้อมแล้ว ไม่มีการ polling
using var resp = await _http.PostAsJsonAsync(ApiUrl, body, ct);
var data = await resp.Content.ReadFromJsonAsync<CreateTaskResponse>(cancellationToken: ct)
?? throw new InvalidOperationException(
$"HTTP {(int)resp.StatusCode}: empty response");
if (data.ErrorId != 0)
{
throw new InvalidOperationException($"{data.ErrorCode}: {data.ErrorDescription}");
}
return data.Solution?.Token
?? throw new InvalidOperationException("Response had no token");
}
public void Dispose() => _http.Dispose();
}
public sealed class CreateTaskResponse
{
[JsonPropertyName("errorId")]
public int ErrorId { get; set; }
[JsonPropertyName("errorCode")]
public string? ErrorCode { get; set; }
[JsonPropertyName("errorDescription")]
public string? ErrorDescription { get; set; }
[JsonPropertyName("solution")]
public Solution? Solution { get; set; }
}
public sealed class Solution
{
[JsonPropertyName("token")]
public string? Token { get; set; }
}
การตรวจ errorId คือบรรทัดที่สำคัญที่สุด API ส่ง 0 เมื่อสำเร็จ ค่าอื่นใดจะมาพร้อม errorCode และ errorDescription และคลาสนี้แสดงทั้งสองค่า
submit token
token ส่งไปยังฟอร์มของเว็บ ใช้ HttpClient ตัวที่สองพร้อม cookie container เพื่อให้ session ที่โหลดฟอร์มถูกใช้ซ้ำในคำขอ POST
// Program.cs
using System.Net;
var apiKey = Environment.GetEnvironmentVariable("SCARNOX_API_KEY")
?? throw new InvalidOperationException("Set SCARNOX_API_KEY");
const string pageUrl = "https://example.com/signup";
const string sitekey = "YOUR-SITEKEY-UUID";
using var solver = new ScarnoxSolver(apiKey);
string token = "";
// ลองใหม่เมื่อเกิดความล้มเหลวชั่วคราว การแก้ที่ล้มเหลวจะไม่ถูกเรียกเก็บเงิน
for (int attempt = 1; attempt <= 3; attempt++)
{
try
{
token = await solver.SolveAsync(sitekey, pageUrl);
break;
}
catch (InvalidOperationException ex) when (attempt < 3)
{
Console.Error.WriteLine($"Attempt {attempt} failed: {ex.Message}");
await Task.Delay(TimeSpan.FromSeconds(attempt));
}
}
var cookies = new CookieContainer();
var handler = new HttpClientHandler { CookieContainer = cookies, UseCookies = true };
using var site = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(60) };
// GET ก่อน เพื่อให้เว็บตั้งค่า cookie ของ session
await site.GetAsync(pageUrl);
using var form = new FormUrlEncodedContent(new Dictionary<string, string>
{
["email"] = "you@example.com",
["h-captcha-response"] = token,
["g-recaptcha-response"] = token,
});
using var response = await site.PostAsync(pageUrl, form);
Console.WriteLine($"Status: {(int)response.StatusCode}");
รันโปรแกรม:
set SCARNOX_API_KEY=scarnox_YOURAPIKEY
dotnet run
บน PowerShell ให้ใช้ $env:SCARNOX_API_KEY = "scarnox_YOURAPIKEY" แทน
ทำไมจึงจัดโครงสร้างแบบนี้
**หนึ่ง HttpClient ต่อหนึ่ง solver** HttpClient ถูกออกแบบให้ใช้ซ้ำ ไม่ใช่สร้างใหม่ทุกครั้งที่เรียก solver เป็นเจ้าของหนึ่งตัวและ dispose เมื่อจบ
timeout 185 วินาที API บล็อกจนกว่า token จะพร้อม timeout ที่สั้นกว่าจะยกเลิกการแก้ที่กำลังจะเสร็จ
การ deserialize แบบมี type ReadFromJsonAsync แมป JSON ไปยัง CreateTaskResponse ทำให้การตรวจ error และ token อ่านเหมือน C# ปกติ
การ submit ฟอร์ม เว็บได้รับทั้ง h-captcha-response และ g-recaptcha-response เพราะเว็บอ่านอย่างใดอย่างหนึ่ง cookie container เก็บ session ไว้ระหว่าง GET และ POST
การจับเวลา
token หมดอายุภายในประมาณสองนาที ให้ solve ทันทีก่อน submit หากคุณ solve ไว้ล่วงหน้าในชุดงานแล้ว submit ทีหลังมาก token จะเก่าไปแล้ว อายุของ captcha token อธิบายรายละเอียด
widget แบบ invisible
ส่ง invisible: true ให้ SolveAsync เมื่อ widget เป็นแบบ invisible คำขอจะมี is_invisible: true หน้าบางหน้าต้องมี JavaScript callback ที่ไคลเอนต์ฝั่งเซิร์ฟเวอร์ไม่สามารถเรียกได้ ในกรณีนั้น เครื่องมือ browser automation เป็นตัวเลือกที่ดีกว่า คู่มือ invisible captcha ครอบคลุมรูปแบบต่าง ๆ
การ scrape อย่างรับผิดชอบ
หากโปรแกรมของคุณ scrape หน้าสาธารณะ ให้ตรวจสอบ robots.txt และข้อกำหนดของเว็บก่อน ใช้การหน่วงเวลาระหว่างคำขอ และเก็บเฉพาะข้อมูลส่วนบุคคลที่จำเป็น สำหรับแง่กฎหมาย ดู การ scrape เว็บถูกกฎหมายหรือไม่
ราคา
Scarnox คิดเงินต่อการแก้ที่สำเร็จ การแก้ที่ล้มเหลวไม่มีค่าใช้จ่าย ดูราคาปัจจุบัน สำหรับอัตรา และเริ่มด้วยเครดิตทดลองใช้ฟรี ไม่ต้องใช้บัตร