วิธี scrape ผลการค้นหาผ่าน captcha (คู่มือ Python ปี 2026)
หน้าผลการค้นหาเป็นหนึ่งในที่แรก ๆ ที่ scraper พบ captcha เว็บไซต์เห็นการพุ่งของ request จาก address เดียว เสิร์ฟความท้าทาย และไม่คืนผลลัพธ์จนกว่าจะผ่านความท้าทายนั้น เพื่อ scrape ผลการค้นหาผ่าน captcha สคริปต์ของคุณต้องทำสามขั้นตอน: ตรวจจับความท้าทายก่อนแยกข้อมูล แก้ด้วยการเรียก API หนึ่งครั้ง และส่ง token เพื่อให้เว็บไซต์คืนหน้าผลลัพธ์จริง
คู่มือนี้แสดง flow นั้นด้วย Python โดยใช้ requests และ BeautifulSoup ครอบคลุมหน้าที่แสดง PopularCaptcha widget ซึ่งเป็นประเภทที่ Scarnox solve ได้ในวันนี้ (แบบ proxyless) เริ่มต้นด้วยเครดิตทดลองใช้ฟรี ไม่ต้องใช้บัตรเครดิต และรันโค้ดกับหน้าที่คุณเป็นเจ้าของก่อนเสมอ
ความท้าทายหน้าตาเป็นแบบไหน
หน้าค้นหามักวาง widget ไว้ในฟอร์ม container ของ widget มี attribute data-sitekey และฟอร์มจะส่งข้อมูลกลับไปยังเว็บไซต์:
<form action="/search/verify" method="post">
<input type="hidden" name="q" value="web scraping">
<input type="hidden" name="lang" value="en">
<div class="h-captcha" data-sitekey="YOUR-SITEKEY-UUID"></div>
<button type="submit">Continue</button>
</form>
โค้ดของคุณต้องการสามอย่างจาก markup นี้: sitekey, URL ของ action ของฟอร์ม และฟิลด์ที่ซ่อนซึ่งต้องส่งกลับไปพร้อม token helper ด้านล่างอ่านทั้งสามอย่าง จึงทำงานได้โดยไม่ต้องฝังค่าไว้ในโค้ด
หากวิดเจ็ตบนเป้าหมายของคุณเป็น captcha ประเภทอื่น ให้ดู ประเภทของ captcha การรองรับ reCAPTCHA และ Cloudflare Turnstile กำลังจะมาเร็ว ๆ นี้ ดังนั้นโค้ดในที่นี้ครอบคลุมเฉพาะ PopularCaptcha
การเรียก solver
request ของการ solve ส่งไปยัง Scarnox API พร้อม API key ของคุณใน header Bearer การเรียกจะรอจนกว่า token พร้อม จึงไม่มี polling loop:
import time
from urllib.parse import urljoin
import requests
from bs4 import BeautifulSoup
API_KEY = "scarnox_YOURAPIKEY"
SEARCH_URL = "https://search.example.com/search"
session = requests.Session()
session.headers["User-Agent"] = (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36"
)
def solve(sitekey: str, page_url: str) -> str:
r = requests.post(
"https://api.scarnox.com/api/tasks/create",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"captcha_type": "PopularCaptchaTaskProxyless",
"site_key": sitekey,
"site_url": page_url,
},
timeout=185, # การเรียกคืนค่าเมื่อ token พร้อม
)
data = r.json()
if data.get("errorId") != 0:
raise RuntimeError(f"{data.get('errorCode')}: {data.get('errorDescription')}")
return data["solution"]["token"]
การ solve ที่ล้มเหลวจะคืน errorId ที่ไม่ใช่ 0 และคุณจะไม่ถูกเรียกเก็บเงิน การ retry อธิบายไว้ใน การจัดการ error ของ captcha solver
ดึงหน้าและผ่าน captcha
ฟังก์ชันดึงข้อมูลตรวจหา widget ก่อน หากไม่มี ก็คืน HTML เดิม หากมี ก็ solve ความท้าทาย สร้างฟอร์มใหม่ด้วยฟิลด์ที่ซ่อนเดิม แล้วส่ง:
def fetch_results(query: str, page: int = 1) -> str:
resp = session.get(SEARCH_URL, params={"q": query, "page": page}, timeout=30)
soup = BeautifulSoup(resp.text, "html.parser")
widget = soup.select_one("[data-sitekey]")
if widget is None:
return resp.text # ไม่มีความท้าทายในหน้านี้
form = widget.find_parent("form")
if form is None:
raise RuntimeError("Captcha widget is not inside a form")
token = solve(widget["data-sitekey"], resp.url)
payload = {
field["name"]: field.get("value", "")
for field in form.select("input[name]")
}
payload["h-captcha-response"] = token
payload["g-recaptcha-response"] = token
action = urljoin(resp.url, form.get("action") or resp.url)
verified = session.post(action, data=payload, timeout=30)
if "data-sitekey" in verified.text:
raise RuntimeError("Challenge still shown after submit; token may be rejected")
return verified.text
มีสองรายละเอียดที่สำคัญ site_url คือ URL ที่ captcha ถูกแสดงจริง (resp.url ซึ่งรวม redirect ใด ๆ) ไม่ใช่ endpoint การค้นหาเปล่า ๆ และ session เก็บ cookie ระหว่าง GET กับ POST ซึ่งเว็บไซต์จำนวนมากต้องการเพื่อยอมรับ token
token หมดอายุภายในประมาณสองนาที นั่นคือเหตุผลที่การ solve เกิดขึ้นทันทีหลังจากอ่านฟอร์ม ไม่ใช่ก่อนหน้านั้น สำหรับช่วงเวลานี้ดูเพิ่มเติมที่ การหมดอายุของ captcha token
แยกผลลัพธ์
เมื่อได้หน้าผลลัพธ์จริงกลับมา การแยกข้อมูลเป็นงาน HTML ธรรมดา selector ด้านล่างเป็นตัวอย่าง ให้เปิดหน้าใน DevTools และแทนที่ด้วย selector จริง:
def parse_results(html: str) -> list[dict]:
soup = BeautifulSoup(html, "html.parser")
rows = []
for item in soup.select("div.result"):
link = item.select_one("a[href]")
heading = item.select_one("h3")
if link and heading:
rows.append({
"title": heading.get_text(strip=True),
"url": link["href"],
})
return rows
if __name__ == "__main__":
for page in range(1, 4):
html = fetch_results("web scraping", page)
for row in parse_results(html):
print(row)
time.sleep(5) # หน่วงเวลาระหว่างหน้า
เก็บผลลัพธ์ของแต่ละหน้าแยกจากกัน หากหน้าหนึ่งคืนผลลัพธ์เป็นศูนย์แถว ให้ตรวจหา widget ก่อนสรุปว่าการค้นหาไม่มีผลลัพธ์
Scrape อย่างมีความรับผิดชอบ
captcha คือสัญญาณ ไม่ใช่ช่องโหว่ ก่อนรันสคริปต์นี้กับเว็บไซต์ค้นหาใด ๆ:
- อ่าน robots.txt และข้อกำหนดการให้บริการ เว็บไซต์ค้นหาจำนวนมากห้ามการค้นหาแบบอัตโนมัติ หากข้อกำหนดห้ามไว้ อย่ารันสคริปต์
- จำกัดอัตรา
time.sleep(5)ข้างบนเป็นค่าต่ำสุด ไม่ใช่เป้าหมาย ให้ช้าลงอีกหากเห็น captcha มากขึ้น - จำกัดสิ่งที่คุณเก็บ ผลการค้นหาอาจมีชื่อ อีเมล และข้อมูลส่วนบุคคลอื่น ให้เก็บเฉพาะฟิลด์ที่ต้องใช้ และลบส่วนที่เหลือ
- ควรใช้ API ทางการ เมื่อเว็บไซต์เปิดให้ใช้ เพราะเชื่อถือได้มากกว่าและมักถูกกว่าการ scrape HTML
สำหรับด้านกฎหมาย ดู web scraping ถูกกฎหมายหรือไม่ สำหรับการหลีกเลี่ยงการถูกบล็อกตั้งแต่แรก ดู scrape โดยไม่ถูกบล็อก
ราคา
Scarnox คิดเงินต่อการ solve ที่สำเร็จ และการ solve ที่ล้มเหลวไม่มีค่าใช้จ่าย ดู ราคาปัจจุบัน สำหรับอัตราต่าง ๆ เริ่มต้นด้วยเครดิตทดลองใช้ฟรี ไม่ต้องใช้บัตรเครดิต