คำตอบสั้น
error code ของ WebReaderAPI หมายถึงอะไร และตัวไหนควรลองใหม่?
error ของ WebReaderAPI แบ่งเป็นสามกลุ่ม และไม่มีการคิดเงินเลย ปัญหาคำขอ (invalid_url, invalid_api_key, insufficient_credits) ต้องแก้ฝั่งคุณ การตัดสินใจของไซต์ (blocked_by_robots, blocked_by_site_policy, target_refused) ถือเป็นที่สุด ความล้มเหลวชั่วคราว (fetch_failed, fetch_timeout, concurrency_limit) ลองใหม่ได้ด้วย backoff เครดิตเริ่มที่ US$0.07/1K หน้า จ่ายด้วยคริปโตโดยไม่ต้อง KYC
API สามารถคืน error code อะไรได้บ้าง?
body เป็น {"error": {"code", "message"}} เสมอ บวก target_status เมื่อการปฏิเสธมาจากไซต์
| HTTP | code | สาเหตุ | ลองอีกครั้งหรือไม่? |
|---|---|---|---|
| 400 | invalid_url | ไม่ใช่ http หรือ https, ยาวเกินไป, หรือเป็นที่อยู่ส่วนตัว, ภายในเครื่อง หรือ cloud metadata | ไม่: แก้ URL |
| 400 | invalid_parameter | พารามิเตอร์มีค่านอกช่วงหรือนอกตัวเลือกที่อนุญาต | ไม่: แก้คำขอ |
| 401 | invalid_api_key | API key หายไป ผิด หรือถูกเพิกถอน | ไม่: ตรวจสอบ API key |
| 402 | insufficient_credits | ยอดคงเหลือของคุณไม่มีเครดิตเหลือแล้ว | หลังซื้อแพ็ก |
| 403 | blocked_by_robots | robots.txt ของเว็บไซต์ไม่อนุญาตหน้านี้สำหรับ WebReaderAPI หรือสำหรับ robots ทั้งหมด | ไม่: เป็นการสิ้นสุด |
| 403 | blocked_by_site_policy | เว็บไซต์ประกาศ Content-Signal: ai-input=no | ไม่: เป็นการสิ้นสุด |
| 422 | unsupported_content | ประเภทไฟล์ที่เราไม่รองรับ (วิดีโอ, ไฟล์บีบอัด) หรือไฟล์ใหญ่เกินไป | ไม่ใช่ |
| 429 | concurrency_limit | คำขอของคุณทำงานพร้อมกันมากเกินไป | ได้ พร้อม backoff |
| 502 | target_refused | เว็บไซต์ปฏิเสธ: 403, 429, หน้า challenge หรือหน้า login; สถานะอยู่ใน target_status | ไม่: เป็นการสิ้นสุด |
| 502 | fetch_failed | เกิดข้อผิดพลาด DNS, TLS หรือการเชื่อมต่อ หรือเว็บไซต์ตอบ 404 หรือ 410 | หนึ่งหรือสองครั้ง พร้อม backoff |
| 504 | fetch_timeout | เว็บไซต์ไม่ตอบกลับภายใน timeout | ได้ พร้อม backoff หรือเพิ่ม timeout |
ทำไม robots.txt และการที่เว็บไซต์ปฏิเสธจึงถือเป็นข้อยุติ?
เพราะนั่นคือการตัดสินใจของเจ้าของเว็บไซต์ และ WebReaderAPI ถูกสร้างมาให้ปฏิบัติตามนั้น เราเคารพ robots.txt: crawler ของเราระบุตัวเองว่า WebReaderAPI ทำตามกฎที่เขียนไว้สำหรับมันหรือสำหรับ robot ทั้งหมด และจะไม่อ่านหน้าที่เว็บไซต์ไม่อนุญาต หน้าที่ถูกปฏิเสธจะไม่ถูกคิดเครดิต หากเว็บไซต์ประกาศ Content-Signal: ai-input=no ใน robots.txt ของมัน เราจะไม่อ่านหน้าของเว็บนั้นให้คุณ
เมื่อเว็บไซต์ปฏิเสธคำขอในรูปแบบอื่น เช่น 403, 429, หน้า challenge หรือฟอร์ม login คุณจะได้รับ target_refused พร้อมสถานะจากเว็บไซต์เองใน target_status หากเว็บไซต์ปฏิเสธคำขอของเรา เราจะส่งคำตอบของเว็บนั้นกลับไป: เราไม่แก้ CAPTCHA ไม่เข้าสู่ระบบบัญชี และไม่พยายามหลบเลี่ยงการบล็อกหรือ paywall การลอง URL เดิมอีกครั้งในอีกหนึ่งนาทีมักได้คำตอบเดิม ดังนั้นให้ถือว่าโค้ดเหล่านี้เป็นผลลัพธ์ ไม่ใช่ความล้มเหลว: บันทึกไว้แล้วไปต่อ
ฉันจะตรวจสอบได้อย่างไรว่าทำไมหน้าเว็บจึงถูกปฏิเสธ?
เปิด /robots.txt ของเว็บไซต์ แล้วมองหากลุ่มที่ระบุ WebReaderAPI หรือกลุ่ม * ที่ใช้กับ robots ทั้งหมด บรรทัด Disallow ที่ตรงกับ path อธิบาย blocked_by_robots บรรทัดอย่าง Content-Signal: ai-input=no อธิบาย blocked_by_site_policy
เราเก็บสำเนา robots.txt แต่ละไฟล์ไว้ไม่เกิน 24 ชั่วโมง หากเว็บไซต์เพิ่งเปิดส่วนหนึ่งให้ robots การเปลี่ยนแปลงอาจใช้เวลาถึงหนึ่งวันจึงจะมีผล เจ้าของเว็บไซต์สามารถอ่านว่า crawler ของเราทำงานอย่างไร และวิธีอนุญาตหรือบล็อกได้ที่ หน้าข้อมูล crawler ของเรา
ฉันควรลองใหม่เมื่อเกิดความล้มเหลวชั่วคราวอย่างไร?
ให้ลองใหม่เฉพาะ concurrency_limit, fetch_timeout และ fetch_failed หนึ่งหรือสองครั้ง รอก่อนการลองแต่ละครั้งและเพิ่มเวลาเป็นสองเท่าทุกครั้ง พร้อม jitter แบบสุ่มเล็กน้อยเพื่อไม่ให้ worker ที่ทำงานขนานกันลองใหม่พร้อมกัน หยุดหลังลอง 4 หรือ 5 ครั้ง
สำหรับ concurrency_limit วิธีแก้ที่ดีกว่าคือส่งคำขอให้น้อยลงในแต่ละครั้ง: ทุกบัญชีมีขีดจำกัดจำนวนหน้าที่อ่านพร้อมกัน แดชบอร์ดแสดงให้เห็น และฝ่ายสนับสนุนสามารถเพิ่มให้ได้ สำหรับ fetch_timeout ให้เพิ่ม timeout เป็นสูงสุด 60 วินาทีก่อนลองเว็บไซต์ที่ตอบช้า การลองใหม่ไม่มีค่าใช้จ่าย เพราะความพยายามที่ล้มเหลวจะไม่ถูกคิดเงิน
import os
import random
import time
import requests
API = "https://webreaderapi.com/api/v1/read"
HEADERS = {"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]}
RETRY = {"concurrency_limit", "fetch_timeout", "fetch_failed"}
FINAL = {"blocked_by_robots", "blocked_by_site_policy", "target_refused", "unsupported_content"}
def read_page(url, attempts=5, **options):
wait = 1.0
for attempt in range(1, attempts + 1):
resp = requests.get(API, headers=HEADERS, params={"url": url, **options}, timeout=75)
if resp.status_code == 200:
return resp.json()
error = resp.json().get("error", {})
code = error.get("code", "unknown")
if code in FINAL:
return {"url": url, "skipped": code, "target_status": error.get("target_status")}
if code not in RETRY or attempt == attempts or (code == "fetch_failed" and attempt >= 3):
raise RuntimeError(f"{code}: {error.get('message')}")
time.sleep(wait + random.uniform(0, wait / 2))
wait = min(wait * 2, 30)
raise RuntimeError("unreachable")
if __name__ == "__main__":
print(read_page("https://example.com/blog/first-post", max_chars=20000).get("title"))
const RETRY = new Set(['concurrency_limit', 'fetch_timeout', 'fetch_failed']);
const FINAL = new Set(['blocked_by_robots', 'blocked_by_site_policy', 'target_refused', 'unsupported_content']);
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
export async function readPage(url, options = {}, attempts = 5) {
let wait = 1000;
for (let attempt = 1; attempt <= attempts; attempt++) {
const params = new URLSearchParams({ url, ...options });
const res = await fetch(`https://webreaderapi.com/api/v1/read?${params}`, {
headers: { Authorization: `Bearer ${process.env.WEBREADER_API_KEY}` },
});
const body = await res.json();
if (res.ok) return body;
const code = body.error?.code ?? 'unknown';
if (FINAL.has(code)) return { url, skipped: code, target_status: body.error.target_status };
if (!RETRY.has(code) || attempt === attempts) throw new Error(`${code}: ${body.error?.message}`);
await sleep(wait + Math.random() * (wait / 2));
wait = Math.min(wait * 2, 30000);
}
}
แล้วข้อผิดพลาดภายใน batch หรือ crawl ล่ะ?
งานจะไม่หยุดเพราะหน้าใดหน้าหนึ่งล้มเหลว หน้านั้นจะปรากฏใน results เป็น {url, error} พร้อมรหัสเดียวกับด้านบน และไม่เสียเครดิต การ crawl ที่เจอ blocked_by_robots ในบางส่วนของเว็บก็แค่อ่านส่วนที่เหลือของเว็บต่อ
เมื่อจบงาน ให้นับรหัสข้อผิดพลาด target_refused จำนวนมากบนเว็บหนึ่งแห่งหมายความว่าเว็บนั้นไม่รับตัวอ่านอัตโนมัติ fetch_timeout จำนวนมากหมายความว่าเว็บช้า และการตั้ง timeout ที่นานขึ้นในตัวเลือกของงานอาจช่วยได้
ข้อผิดพลาด: คำถามที่พบบ่อย
ฉันถูกเรียกเก็บเงินสำหรับหน้าที่ robots.txt บล็อกหรือไม่?
ไม่ 1 เครดิต = อ่าน 1 หน้าโดยไม่ใช้เบราว์เซอร์; 2 เครดิตเมื่อหน้านั้นต้องใช้เบราว์เซอร์เพื่อเรนเดอร์ JavaScript คำขอที่ล้มเหลว หรือที่ robots.txt ของเว็บไซต์ปฏิเสธ จะไม่ถูกคิดเครดิต เช่นเดียวกับข้อผิดพลาดอื่น ๆ ทั้งหมดในหน้านี้
ถ้าฉันขอ คุณอ่านหน้าที่บล็อก crawler ของคุณได้ไหม?
ไม่ได้ เราทำตามการตัดสินใจของเจ้าของเว็บ และนโยบายเดียวกันสำหรับลูกค้าทุกคน ถ้าคุณเป็นเจ้าของเว็บ ให้เพิ่มกฎสำหรับ WebReaderAPI ใน robots.txt ของคุณ หน้า crawler แสดงวิธีทำ
ทำไมฉันจึงได้ fetch_failed สำหรับหน้าที่มีอยู่จริง?
เว็บอาจล่ม ช้าในการตอบรับการเชื่อมต่อ TLS หรือตอบ 404 ให้กับไคลเอนต์อัตโนมัติ ลองใหม่อีกหนึ่งหรือสองครั้งแบบ backoff ถ้ายังล้มเหลว ให้ตรวจสอบที่อยู่ในเบราว์เซอร์
เอเจนต์ของฉันควรบอกผู้ใช้อย่างไรหลังการปฏิเสธ?
บอกง่าย ๆ เช่น เว็บนั้นไม่อนุญาตให้อ่านหน้านั้นโดยอัตโนมัติ จากนั้นให้โมเดลใช้แหล่งอื่น การคืนรหัสข้อผิดพลาดเป็นผลลัพธ์ของ tool ก็เพียงพอสำหรับโมเดลส่วนใหญ่