/// <inheritdoc /> /// <param name="uri">Uri Address</param> public static HttpResponse GetThroughCloudflare(this HttpRequest request, Uri uri, DLog log = null, #if USE_CAPTCHA_SOLVER DSolveRecaptcha reCaptchaSolver = null, #endif CancellationToken cancellationToken = default(CancellationToken)) { return(GetThroughCloudflare(request, uri.AbsoluteUri, log, #if USE_CAPTCHA_SOLVER reCaptchaSolver, #endif cancellationToken)); }
/// <summary> /// GET request with bypassing Cloudflare JavaScript challenge. /// </summary> /// <param name="uri">Address</param> /// <param name="log">Log action</param> /// <param name="cancellationToken">Cancel protection</param> /// <returns>Returns original HttpResponse</returns> public static HttpResponse GetThroughCloudflare(this HttpRequest request, string url, DLog log = null, #if USE_CAPTCHA_SOLVER DSolveRecaptcha reCaptchaSolver = null, #endif CancellationToken cancellationToken = default(CancellationToken)) { // User-Agent is required if (string.IsNullOrEmpty(request.UserAgent)) { request.UserAgent = Http.ChromeUserAgent(); } log?.Invoke("Проверяем наличие CloudFlare по адресу: " + url); HttpResponse response = null; for (int i = 0; i < MaxRetries; i++) { string retry = $". Попытка {i + 1} из {MaxRetries}."; #region Try catch disabled /*try * {*/ #endregion response = ManualGet(request, url); if (!response.IsCloudflared()) { log?.Invoke("УСПЕХ: Cloudflare не обнаружен, работаем дальше: " + url); return(response); } log?.Invoke("Обхожу CloudFlare" + retry); if (cancellationToken != default(CancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); } response = PassClearance(request, response, url, log, cancellationToken); // JS Challange passed if (response.StatusCode == HttpStatusCode.Found) { // Т.к. ранее использовался ручной режим - нужно обработать редирект, если он есть, чтобы вернуть отфильтрованное тело запроса // TODO: иногда бывает 403 forbidden из-за cloudflare, в чем проблема? if (response.HasRedirect) { response = request.Get(response.RedirectAddress); } break; } #if USE_CAPTCHA_SOLVER if (response.StatusCode == HttpStatusCode.Forbidden && reCaptchaSolver == null) { continue; } #else if (response.StatusCode == HttpStatusCode.Forbidden) // && reCaptchaSolver == null) { continue; } #endif // Not implemented status code if (response.StatusCode != HttpStatusCode.Forbidden) { log?.Invoke( $"CloudFlare не смог пройти JS Challange, причина не ясна. Статус код: {response.StatusCode}" + retry); #if USE_CAPTCHA_SOLVER continue; #endif } #region Captcha solver #if USE_CAPTCHA_SOLVER // IF Forbidden and has recaptcha // // ReCaptcha solve required string strResponse = response.ToString(); // TODO: while for captcha solving const string siteKeyPattern = "data-sitekey=\""; if (!strResponse.Contains(siteKeyPattern)) { string error = "CloudFlare не смог пройти т.к. возращен код Forbidden, но ключ сайта для рекаптчи не найден" + retry; log?.Invoke(error); throw new CloudflareException(error); } if (reCaptchaSolver == null) { throw new CloudflareException("Cloudflare требует решение ReCaptcha, но делегат для её решения не был предоставлен"); } string siteKey = strResponse.Substring(siteKeyPattern, "\""); if (siteKey == string.Empty) { throw new CloudflareException("Cloudflare требует решение ReCaptcha, но ключ сайта не был найден в HTML коде"); } string submitRelativeUrl = strResponse.Substring("id=\"challenge-form\" action=\"", "\""); if (submitRelativeUrl == string.Empty) { throw new CloudflareException("Cloudflare требует решение ReCaptcha, но адрес публикации формы с каптчей не найден в HTML коде"); } // Build uri Form GET action var address = response.Address; var submitUri = new Uri($"{address.Scheme}://{address.Host}:{address.Port}{submitRelativeUrl}"); log?.Invoke("CloudFlare выдал рекаптчу, решаю..."); // Решаем рекаптчу и отправляем форму var rp = new RequestParams { ["g-recaptcha-response"] = reCaptchaSolver(siteKey) }; response = request.Get(submitUri, rp); // После отправки формы кидает на главную, поэтому идем по адресу какой требуется согласно функции if (response.Address.AbsoluteUri != url) { response = request.Get(url); } #endif #endregion } if (response == null) { throw new Exception("Ответ null"); } // Clearance failed. if (response.IsCloudflared()) { throw new CloudflareException(MaxRetries, "Превышен лимит попыток обхода Cloudflare"); } log?.Invoke("CloudFlare успешно пройден"); return(response); }