Как повторно использовать cookie cf_clearance в Python-скрейпере

cf_clearance cookie - How to Reuse cf_clearance Cookies in a Python Scraper

Решение проверки Cloudflare составляет лишь половину дела. За это вы получаете cookie cf_clearance, и именно этот cookie не даёт следующему запросу снова получить проверку. Он быстро истекает, привязан к большему числу параметров, чем обычно ожидают, и перестаёт работать, как только ваш клиент начинает отличаться от того, который его заслужил. В этой статье рассказано, что такое этот cookie, как его получить, что незаметно делает его недействительным и как отличить истёкший cookie от сломанного.

Что понадобится

  • Python 3.10 или новее, с HTTP-клиентом, поддерживающим сессии. В примерах используется сессия, чтобы cookie сохранялись между запросами.
  • Цель, которая действительно выдаёт проверку. Сайт, который никогда её не показывает, никогда не установит cookie, так что тестировать будет не на чем.
  • Запущенный CapSkip, либо в локальном режиме на loopback-адресе, либо в серверном режиме на машине, к которой могут обращаться ваши воркеры. Оба режима описаны в разделе Настройки подключения, так что выберите один до начала.

Что на самом деле представляет собой cookie cf_clearance

Это квитанция. Официальная документация Cloudflare описывает его как cookie, который хранит доказательство пройденной проверки, и используется для того, чтобы проверка больше не выдавалась, пока этот cookie присутствует. В нём же хранятся результаты JavaScript-детекций, и он устанавливается с атрибутами SameSite None, Secure и Partitioned, чтобы состояние сохранялось при межсайтовых запросах.

Время жизни этого cookie вы не выбираете. Оно определяется настройкой Challenge Passage на сайте, к которому вы обращаетесь, и Cloudflare прямо документирует значение по умолчанию: у cookie cf_clearance время жизни составляет 30 минут, а разумным диапазоном считается от 15 до 45 минут. Некоторые сайты сокращают этот срок. Некоторые удлиняют. Узнать значение снаружи нельзя, поэтому считайте каждую сессию clearance недолговечной и закладывайтесь на обновление, а не надейтесь, что она проживёт долго.

Отсюда вытекают три практических следствия. Cookie нужно сохранять, потому что повторное решение проверки на каждый запрос медленно и расточительно. Никогда не стоит считать, что он переживёт перезапуск. И в коде должен быть путь, который замечает, что cookie устарел, и незаметно получает новый.

Шаг 1: решите проверку, а затем сохраните весь клиент целиком

Первая ошибка, которой стоит избегать: считать токен наградой. Это не так. Токен, который вы получаете за решение проверки Turnstile, служит лишь для обмена на clearance, а ценность после этого представляет уже полученный cookie. Поэтому порядок действий такой: решить, отправить, а затем сохранить сессию, которая получила ответ.

# pip install capskip
from capskip import CapSkip

solver = CapSkip(host="127.0.0.1", port=8080)

# A challenge page needs two more values than a plain widget does.
result = solver.turnstile(
    sitekey="YOUR_SITEKEY",
    url="https://example.com/protected",
    data="YOUR_CDATA",
    pagedata="YOUR_CHLPAGEDATA",
)

token = result["code"]
agent = result["userAgent"]   # not optional, see the next section

Отправьте этот токен так же, как это сделала бы сама страница, из сессии, которую вы собираетесь сохранить. Как только придёт ответ, cookie clearance окажется в хранилище cookie этой сессии, и вы сможете сразу его прочитать.

# pip install requests
import requests

s = requests.Session()
s.headers["User-Agent"] = agent      # the exact agent the solver returned

# ... submit the token here, exactly as the challenge page does ...

clearance = s.cookies.get("cf_clearance")
print(bool(clearance))               # True once the challenge is cleared

Шаг 2: узнайте, что незаметно делает его недействительным

Cloudflare не публикует точную привязку, так что эта таблица отражает наблюдаемое поведение, а не задокументированный контракт. Оно достаточно стабильно, чтобы на него опираться, и каждая строка здесь кому-то стоила потерянного дня.

Что изменилосьСохраняется ли clearanceЗачем
Строка user agentНетClearance был выдан конкретной идентичности браузера
IP-адрес источникаНетCookie, переехавший на новый адрес, служит классическим признаком replay-атаки
Ваш TLS-фингерпринтОбычно нетРукопожатие считывается раньше cookie, поэтому несовпадение обнаруживается раньше
Имя хоста, на который он отправляетсяНетClearance выдаётся для каждого сайта отдельно, а не для аккаунта или сети
Течение времениТолько до истечения Challenge PassageПо умолчанию 30 минут, но сайт может это изменить
Добавление посторонних cookieДаПроверка clearance игнорирует остальные cookie

Первые три строки на самом деле выражают одно и то же правило в трёх формах: clearance принадлежит клиенту, а не вам. Поэтому закрепите ту идентичность, которая его заслужила. Это значит: один user agent, один исходящий IP и один TLS-профиль на весь срок жизни этого cookie. Смена прокси посреди сессии выбрасывает уже оплаченный clearance, и это самая частая причина, по которой рабочий скрейпер вдруг снова начинает получать проверки без видимой причины.

Именно поэтому решатель возвращает не только токен, но и user agent. Turnstile привязывает токен к идентичности браузера, который его получил, поэтому токен, отправленный с другим user agent, отклоняется, даже если сам токен полностью действителен. Используйте возвращённое значение дословно и продолжайте использовать его для каждого запроса, который несёт полученный в результате cookie. Пошаговое руководство по странице проверки Turnstile показывает, откуда берутся два дополнительных входных значения, а именно в этом чаще всего и ошибаются.

Шаг 3: сохраняйте его между запусками

Cookie clearance, который умирает вместе с вашим процессом, стоит намного меньше, чем тот, что переживает перезапуск. Храните хранилище cookie вместе с идентичностью, к которой оно привязано, ведь сам по себе cookie бесполезен, если вы загрузите его под другим user agent или через другой прокси.

# pip install requests
import json, time

def save_clearance(session, agent, proxy, path="clearance.json"):
    """Store the cookie with the identity that earned it."""
    blob = {
        "cf_clearance": session.cookies.get("cf_clearance"),
        "user_agent": agent,
        "proxy": proxy,
        "stored_at": time.time(),
    }
    with open(path, "w") as fh:
        json.dump(blob, fh)

Загрузка представляет собой зеркальное действие, с одной дополнительной проверкой. Сами устаревайте запись по возрасту, а не дожидайтесь новой проверки, потому что упреждающее обновление стоит одно решение, а реактивное сначала стоит один неудачный запрос.

# pip install requests
import json, time, requests

def load_clearance(path="clearance.json", max_age=900):
    """Return a ready session, or None if the record is too old."""
    with open(path) as fh:
        blob = json.load(fh)

    # 15 minutes, comfortably inside a 30 minute default.
    if time.time() - blob["stored_at"] > max_age:
        return None

    s = requests.Session()
    s.headers["User-Agent"] = blob["user_agent"]
    s.proxies = {"https": blob["proxy"]} if blob["proxy"] else {}
    s.cookies.set("cf_clearance", blob["cf_clearance"])
    return s

Пятнадцать минут выбраны как намеренно осторожный потолок. Значение Challenge Passage, настроенное на сайте, вам не видно, по умолчанию оно равно 30 минутам, а обновление на середине этого срока стоит одно дешёвое решение вместо сорванной партии запросов. Если решатель работает на вашем собственном оборудовании без платы за каждое решение, действовать заранее ничего не стоит.

Шаг 4: определяйте устаревший clearance, не гадая

Мёртвый clearance не сообщает о себе понятной ошибкой. Обычно вы получаете обычный на вид 403 или 200 с HTML-заглушкой вместо запрошенного JSON. Проверка одного только кода статуса не поймает второй случай, и цикл повторов, который следит лишь за кодом статуса, с радостью будет крутиться на нём бесконечно.

# pip install requests
def needs_new_clearance(response):
    """True when this response is a challenge rather than content."""
    if response.status_code in (403, 503):
        return True
    body = response.text[:4000].lower()
    markers = ("cf-turnstile", "challenge-platform", "just a moment")
    return any(m in body for m in markers)

Подключите эту проверку перед парсером, а не после него. Когда она возвращает true, отбрасывайте сохранённую запись, решайте капчу заново один раз и повторяйте попытку уже со свежей сессией. Не повторяйте попытку со старым cookie и увеличенной паузой, ведь дело не во времени. Та же особенность проявляется и с токенами reCAPTCHA, где окно действия ещё короче, а этому посвящена статья про срок действия токена reCAPTCHA , которая рассказывает об этой стороне вопроса.

Как запустить решатель на сервере

Cookie clearance привязаны к идентичности, поэтому парку воркеров нужен парк clearance, и каждый из них нужно получить решением. Эта работа не обязана выполняться на самом воркере. Настройки подключения покрывают оба варианта:

РежимПрослушиваетКогда использовать
Локально127.0.0.1, только это устройствоВаш скрейпер и решатель работают на одной машине
СерверВаш сетевой адрес или публичный IPВоркеры, VPS или облачная платформа обращаются через API

Укажите в SDK хост машины с решателем, и больше ничего в коде менять не нужно, так что двадцать воркеров могут использовать один и тот же экземпляр, при этом у каждого будет собственное хранилище cookie. Если вызывающие стороны находятся вне вашей сети, рекомендуется статический публичный IP. Подробности приведены в разделе Настройки подключения, а серверный режим по-прежнему работает на вашем оборудовании и без платы за использование: меняется лишь то, где выполняется решение капчи, а не то, кому принадлежит оборудование.

FAQ

Сколько живёт cookie cf_clearance?

По умолчанию тридцать минут, но владелец сайта может это изменить. Cloudflare предоставляет это как настройку Challenge Passage и рекомендует держать её в диапазоне от 15 до 45 минут, так что большинство встречающихся вам сайтов окажутся где-то в этих пределах. Прочитать настроенное значение снаружи нельзя, поэтому обновление по собственному таймеру лучше, чем ожидание новой проверки.

Можно ли использовать один cookie clearance на несколько воркеров?

Только если они разделяют идентичность, которая его заслужила, а на практике это означает один и тот же исходящий IP, один и тот же user agent и один и тот же TLS-профиль. Этого можно добиться за одним прокси, и это хороший способ сэкономить на решениях. В тот момент, когда два воркера начинают использовать разные исходящие адреса, общий cookie перестаёт работать для одного из них, и сбои выглядят случайными, пока вы не заметите, на каком воркере они происходят.

Почему мой clearance перестал работать после смены прокси?

Потому что clearance был выдан старому адресу. Cookie, приходящий с нового IP, представляет собой ровно тот паттерн, ради отлова которого существует защита от replay-атак, поэтому он отбрасывается, и проверка появляется снова. Ротацию лучше делать на границе сессии: один прокси зарабатывает один clearance, использует его до истечения срока, а следующая сессия начинается заново, с новым адресом и новым решением.

Нужен ли настоящий браузер, чтобы удерживать cookie clearance?

Нет. Его может нести любой HTTP-клиент с хранилищем cookie, пока связанная с ним идентичность остаётся неизменной. Браузер бесплатно даёт вам правдоподобное TLS-рукопожатие и правдоподобный порядок заголовков, а у обычного HTTP-клиента по умолчанию нет ни того, ни другого. Так что сложность не в самом cookie, а в согласованности, и имперсонирующий клиент обеспечивает её без затрат памяти, свойственных браузеру.

Самая короткая версия

Относитесь к cookie cf_clearance как к недолговечной квитанции, привязанной к одному клиенту. Получайте его из сессии, которая прошла проверку, храните вместе с user agent и прокси, которые его получили, и обновляйте по таймеру примерно раз в пятнадцать минут, не дожидаясь блокировки. Проверяйте тело ответа, а не только код статуса, ведь проверка может прийти с совершенно исправным кодом 200. Когда срок действия истекает, достаточно одного нового решения, а распознавание капчи , работающее на вашем собственном оборудовании, делает это достаточно дешёвым, чтобы обновлять заранее. Что касается двух форм ввода, которые может принимать решение Turnstile, читайте страницу решателя Cloudflare Turnstile , прежде чем что-либо подключать. Настройка для пула воркеров рассмотрена отдельно в разделе распознавание капчи для веб-скрейпинга, и это стоит потратить двадцать минут, если вы используете больше одного воркера.