Scarnox · Blog

Scrapy कैप्चा मिडलवेयर: अपने spider में कैप्चा हल करें (2026)

Scrapy रिक्वेस्ट और callbacks के इर्द-गिर्द बना है, इसलिए कैप्चा उसमें साफ़ तरीके से फिट नहीं बैठता। इसे संभालने का सही तरीका एक downloader middleware है: वह हर रिस्पॉन्स देखता है, चैलेंज पहचानता है, टोकन लेता है, और spider को उसी टोकन के साथ फॉर्म सबमिट करने देता है। यह गाइड PopularCaptcha पेजों के लिए ऐसा middleware बनाती है, जिसे Scarnox आज proxyless आधार पर हल करता है।

मुफ्त ट्रायल क्रेडिट के साथ शुरू करें, कार्ड की ज़रूरत नहीं और किसी और चीज़ पर इंगित करने से पहले कोड को एक टेस्ट पेज पर आज़माएं।

मिडलवेयर कैसे काम करता है

मिडलवेयर हर रिस्पॉन्स पर चलता है:

  1. अगर रिस्पॉन्स में data-sitekey नहीं है, तो वह रिस्पॉन्स आगे जाने देता है।
  2. अगर है, तो मिडलवेयर कैप्चा हल करता है और उसी रिक्वेस्ट की एक कॉपी लौटाता है, जिसके meta में टोकन रखा होता है।
  3. Scrapy वह रिक्वेस्ट दोबारा लाता है। मिडलवेयर meta में टोकन देखता है और रिस्पॉन्स आगे जाने देता है।
  4. spider meta से टोकन पढ़ता है और FormRequest.from_response से फॉर्म सबमिट करता है।

सॉल्व कॉल deferToThread के ज़रिए एक थ्रेड में चलती है, इसलिए वह Scrapy के reactor को ब्लॉक नहीं करती। कैप्चा हल होते समय दूसरी रिक्वेस्ट आगे बढ़ती रहती हैं।

प्रोजेक्ट सेटिंग्स

अपनी key कोड में नहीं, environment variable में रखें:

# settings.py
import os

SCARNOX_API_KEY = os.environ["SCARNOX_API_KEY"]
SCARNOX_TIMEOUT = 185

DOWNLOADER_MIDDLEWARES = {
    "myproject.middlewares.PopularCaptchaMiddleware": 543,
}

ROBOTSTXT_OBEY = True
DOWNLOAD_DELAY = 3
CONCURRENT_REQUESTS_PER_DOMAIN = 1

डाउनलोड देरी और हर डोमेन पर एक समवर्ती रिक्वेस्ट जानबूझकर रखी गई हैं। ये साइट का लोड कम रखती हैं और आपको दिखने वाले चैलेंजों की संख्या घटाती हैं।

मिडलवेयर

# myproject/middlewares.py
import requests
from scrapy import Request
from scrapy.http import Response
from twisted.internet.threads import deferToThread

API_URL = "https://api.scarnox.com/api/tasks/create"


class PopularCaptchaMiddleware:
    def __init__(self, api_key: str, timeout: int):
        self.api_key = api_key
        self.timeout = timeout

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            api_key=crawler.settings.get("SCARNOX_API_KEY"),
            timeout=crawler.settings.getint("SCARNOX_TIMEOUT", 185),
        )

    def _solve(self, sitekey: str, page_url: str) -> str:
        r = requests.post(
            API_URL,
            headers={"Authorization": f"Bearer {self.api_key}"},
            json={
                "captcha_type": "PopularCaptchaTaskProxyless",
                "site_key": sitekey,
                "site_url": page_url,
            },
            timeout=self.timeout,  # वर्कर थ्रेड में चलता है, reactor में नहीं
        )
        data = r.json()
        if data.get("errorId") != 0:
            raise RuntimeError(f"{data.get('errorCode')}: {data.get('errorDescription')}")
        return data["solution"]["token"]

    def process_response(self, request: Request, response: Response, spider):
        # पहले से हल, या हमारे खुद बनाए फॉर्म सबमिट: आगे जाने दें।
        if request.meta.get("captcha_token") or request.meta.get("skip_captcha"):
            return response

        sitekey = response.css("[data-sitekey]::attr(data-sitekey)").get()
        if not sitekey:
            return response

        spider.logger.info("PopularCaptcha on %s, solving", response.url)

        def retry_with_token(token: str) -> Request:
            # Request लौटाने से वह दोबारा शेड्यूल होता है। dont_filter dupe filter से बचाता है।
            return request.replace(
                dont_filter=True,
                meta={**request.meta, "captcha_token": token},
            )

        return deferToThread(self._solve, sitekey, response.url).addCallback(retry_with_token)

process_response से Deferred लौटाना Scrapy में समर्थित है। callback टोकन को एक दोबारा होने वाली रिक्वेस्ट में बदलता है, और Scrapy उसे किसी और रिक्वेस्ट की तरह शेड्यूल करता है।

spider

# myproject/spiders/search.py
import scrapy
from scrapy.http import FormRequest


class SearchSpider(scrapy.Spider):
    name = "search"
    start_urls = ["https://search.example.com/search?q=web+scraping"]

    def parse(self, response):
        token = response.meta.get("captcha_token")
        if token and response.css("[data-sitekey]"):
            # Scrapy फॉर्म के अपने फ़ील्ड भरता है; हम टोकन जोड़ते हैं।
            yield FormRequest.from_response(
                response,
                formdata={
                    "h-captcha-response": token,
                    "g-recaptcha-response": token,
                },
                callback=self.parse_results,
                meta={"skip_captcha": True},
                dont_filter=True,
            )
            return

        yield from self.parse_results(response)

    def parse_results(self, response):
        for item in response.css("div.result"):
            yield {
                "title": item.css("h3::text").get(),
                "url": item.css("a::attr(href)").get(),
            }

selectors प्लेसहोल्डर हैं। उन्हें उस साइट के असली selectors से बदलें जिसे आप स्क्रैप कर रहे हैं।

skip_captcha फ्लैग ही वह चीज़ है जो मिडलवेयर को फॉर्म सबमिशन फिर से हल करने से रोकती है। इसके बिना हर सबमिट पर एक और सॉल्व का खर्च आता।

चलाएं

export SCARNOX_API_KEY=scarnox_YOURAPIKEY
scrapy crawl search -O results.json

लॉग देखें। PopularCaptcha on ..., solving जैसी एक पंक्ति का मतलब है कि चैलेंज मिला और टोकन मांगा गया। अगर यह पंक्ति हर पेज पर दिखे, तो धीमे हों। बार-बार आने वाले चैलेंज इस बात का संकेत हैं कि साइट ने क्रॉल को नोटिस कर लिया है।

टोकन का समय

PopularCaptcha टोकन लगभग दो मिनट में समाप्त होता है। मिडलवेयर टोकन आते ही रिट्राई का अनुरोध करता है, और spider उसी callbacks की श्रृंखला में सबमिट करता है। श्रृंखला छोटी रखें। टोकन और फॉर्म सबमिट के बीच धीमे डाउनलोड या लंबी प्रोसेसिंग न जोड़ें। इस विंडो के बारे में अधिक जानकारी के लिए कैप्चा टोकन की समाप्ति देखें।

एरर

विफल सॉल्व थ्रेड के अंदर RuntimeError उठाता है। Scrapy उसे एक विफल रिक्वेस्ट के रूप में लॉग करेगा। विफल सॉल्व का शुल्क नहीं लगता, इसलिए रिट्राई सस्ती है। सीमाओं वाली रिट्राई नीति के लिए Scrapy का बिल्ट-इन RetryMiddleware इस्तेमाल करें और _solve को एक छोटे लूप में लपेटें। उत्पादन में टिकने वाले पैटर्न के लिए हमारी गाइड कैप्चा सॉल्वर एरर हैंडलिंग देखें।

ज़िम्मेदारी से स्क्रैप करें

कानूनी पृष्ठभूमि के लिए क्या वेब स्क्रैपिंग कानूनी है देखें।

कीमत

Scarnox हर सफल सॉल्व पर शुल्क लेता है, और विफल सॉल्व मुफ्त हैं। दरों के लिए मौजूदा कीमतें देखें। मुफ्त ट्रायल क्रेडिट के साथ शुरू करें, कार्ड की ज़रूरत नहीं।

अगले कदम