Middleware captcha untuk Scrapy: pecahkan captcha di spider Anda (2026)
Scrapy dibangun di sekitar request dan callback, jadi captcha tidak cocok dengan rapi di dalamnya. Cara yang bersih untuk menanganinya adalah middleware downloader: ia melihat setiap respons, mendeteksi tantangan, mendapatkan token, dan membiarkan spider mengirim form dengan token tersebut. Panduan ini membangun middleware itu untuk halaman PopularCaptcha, yang saat ini dipecahkan Scarnox secara proxyless.
Mulai dengan kredit uji coba gratis, tanpa kartu dan coba kodenya pada halaman uji sebelum Anda menjalankannya pada hal lain.
Cara kerja middleware
Middleware berjalan pada setiap respons:
- Jika respons tidak memiliki
data-sitekey, middleware meneruskan respons tersebut. - Jika ada, middleware memecahkan captcha dan mengembalikan salinan request yang sama dengan token disimpan di
meta. - Scrapy mengambil ulang request itu. Middleware melihat token di
metadan membiarkan respons lewat. - Spider membaca token dari
metadan mengirim form denganFormRequest.from_response.
Panggilan solve berjalan di thread melalui deferToThread, sehingga tidak memblokir reactor Scrapy. Request lain tetap berjalan sementara captcha sedang dipecahkan.
Pengaturan proyek
Simpan kunci Anda di environment variable, bukan di kode:
# 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
Jeda download dan konkurensi tunggal per domain memang sengaja diatur. Keduanya menjaga beban situs tetap rendah dan mengurangi jumlah captcha yang Anda temui.
Middleware
# 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, # berjalan di thread worker, bukan di 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):
# Sudah dipecahkan, atau pengiriman form yang kita buat sendiri: teruskan.
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 pada %s, sedang memecahkan", response.url)
def retry_with_token(token: str) -> Request:
# Mengembalikan Request berarti menjadwalkannya ulang. dont_filter melewati 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)
Mengembalikan Deferred dari process_response didukung oleh Scrapy. Callback mengubah token menjadi request yang di-retry, dan Scrapy menjadwalkannya seperti request biasa.
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 mengisi field form milik halaman; kita menambahkan token.
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(),
}
Selector-nya hanyalah placeholder. Ganti dengan selector sebenarnya dari situs yang Anda scrape.
Flag skip_captcha adalah yang mencegah middleware memecahkan pengiriman form lagi. Tanpanya, setiap pengiriman akan menimbulkan biaya solve tambahan.
Jalankan
export SCARNOX_API_KEY=scarnox_YOURAPIKEY
scrapy crawl search -O results.json
Perhatikan log. Baris seperti PopularCaptcha pada ..., sedang memecahkan berarti tantangan ditemukan dan token diminta. Jika Anda melihat baris itu di setiap halaman, perlambat. Captcha yang sering muncul adalah tanda bahwa situs telah menyadari crawl tersebut.
Waktu token
Token PopularCaptcha kedaluwarsa dalam sekitar dua menit. Middleware meminta retry segera setelah token tiba, dan spider mengirim di rangkaian callback yang sama. Jaga rangkaian itu tetap pendek. Jangan menambahkan download yang lambat atau pemrosesan panjang di antara token dan pengiriman form. Untuk penjelasan lebih lanjut tentang jendela waktu tersebut, lihat masa berlaku token captcha.
Error
Solve yang gagal memunculkan RuntimeError di dalam thread. Scrapy akan mencatatnya sebagai request yang gagal. Solve yang gagal tidak ditagih, jadi retry itu murah. Untuk kebijakan retry dengan batas, gunakan RetryMiddleware bawaan Scrapy dan bungkus _solve dalam loop pendek. Panduan kami tentang penanganan error solver captcha mencantumkan pola yang tahan di produksi.
Scraping secara bertanggung jawab
- Patuhi robots.txt.
ROBOTSTXT_OBEY = Truedi atas melakukannya. Biarkan tetap aktif. - Pertahankan jeda yang tinggi. Tantangan adalah tanda bahwa situs ingin Anda memperlambat. Jangan mengakali jeda untuk menyelesaikan lebih cepat.
- Baca ketentuan layanan sebelum Anda melakukan crawl. Beberapa situs melarang akses otomatis sepenuhnya.
- Tangani data pribadi dengan hati-hati. Simpan hanya field yang Anda butuhkan, dan lindungi apa yang Anda simpan.
Untuk latar belakang hukumnya, lihat apakah web scraping legal.
Harga
Scarnox menagih per solve yang berhasil, dan solve yang gagal gratis. Lihat harga terkini untuk tarifnya. Mulai dengan kredit uji coba gratis, tanpa kartu.