Jawaban singkat
Bagaimana cara mengubah halaman web menjadi Markdown di Python?
Panggil WebReaderAPI dengan requests.get("https://webreaderapi.com/api/v1/read", params={"url": ...}) dan API key Anda di header Authorization; jawaban JSON-nya berisi konten utama halaman dalam markdown. Untuk banyak halaman, gunakan httpx.AsyncClient dengan semaphore atau job batch. Satu halaman berbiaya 1 kredit, mulai dari US$0,07/1K halaman per Oktober 2026. Tanpa KYC; bayar dengan kripto.
Apa yang perlu saya instal?
Python 3.9 atau lebih baru dan satu HTTP client. requests cukup untuk skrip yang membaca satu halaman sekaligus; httpx menambahkan dukungan async untuk banyak halaman secara paralel. Tidak perlu paket dari kami: API-nya HTTPS dan JSON biasa.
Jauhkan API key Anda dari kode. Bacalah dari environment variable WEBREADER_API_KEY, seperti yang dilakukan setiap contoh di bawah, dan beri setiap proyek API key sendiri agar Anda bisa mencabut satu tanpa menyentuh yang lain.
pip install requests httpx
export WEBREADER_API_KEY="your-key"Bagaimana cara membaca satu halaman dengan requests?
Kirim url dan opsi apa pun sebagai parameter kueri, raise saat ada error HTTP, dan baca field yang Anda butuhkan. Contoh ini menyimpan konten utama, membatasinya di 20.000 karakter untuk model, dan mencetak biaya panggilannya.
import os
import requests
API = "https://webreaderapi.com/api/v1/read"
HEADERS = {"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]}
def read(url, **options):
resp = requests.get(API, headers=HEADERS, params={"url": url, **options}, timeout=75)
if resp.status_code != 200:
error = resp.json()["error"]
raise RuntimeError(error["code"] + ": " + error["message"])
return resp.json()
page = read("https://example.com/docs/getting-started", max_chars=20000)
print(page["title"], page["word_count"], "words", page["credits_used"], "credit(s)")
print(page["markdown"][:500])
Bagaimana cara membaca banyak halaman sekaligus dengan httpx?
Gunakan satu httpx.AsyncClient dan asyncio.Semaphore yang disetel pada atau di bawah batas konkurensi Anda. Setiap akun memiliki batas berapa banyak halaman yang dibaca sekaligus; dashboard Anda menampilkannya, dan support dapat menaikkannya. Error dari satu halaman tidak boleh menghentikan yang lain, jadi contoh ini mengumpulkannya di samping hasil alih-alih me-raise.
Jika Anda melewati batas, API menjawab 429 concurrency_limit; tidak ada yang ditagih, tetapi halaman tidak dibaca. Menjaga semaphore di bawah batas lebih sederhana daripada mencoba ulang.
import asyncio
import os
import httpx
API = "https://webreaderapi.com/api/v1/read"
HEADERS = {"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]}
async def read_one(client, sem, url):
async with sem:
resp = await client.get(API, params={"url": url, "max_chars": 50000})
body = resp.json()
if resp.status_code == 200:
return url, body, None
return url, None, body["error"]["code"]
async def read_many(urls, limit=10):
sem = asyncio.Semaphore(limit)
async with httpx.AsyncClient(headers=HEADERS, timeout=75) as client:
return await asyncio.gather(*(read_one(client, sem, u) for u in urls))
if __name__ == "__main__":
urls = [f"https://example.com/blog/post-{n}" for n in range(1, 21)]
for url, page, error in asyncio.run(read_many(urls)):
print(url, "->", error or page["title"])
Kapan sebaiknya saya menggunakan batch job?
Saat Anda memiliki daftar hingga 100 URL dan tidak membutuhkan setiap halaman begitu siap. Batch job berjalan di sisi kami, jadi skrip Anda tidak menahan koneksi tetap terbuka dan tidak melakukan logika retry. Mulai dengan POST https://webreaderapi.com/api/v1/batch, lalu kumpulkan hasil dari job dengan loop cursor seperti yang ditunjukkan di halaman batch dan crawl.
Untuk satu situs penuh, mulai crawl sebagai gantinya dan biarkan mengikuti sitemap dan tautan; halaman yang sama memiliki loop lengkap yang menulis satu file .md per halaman.
import os
import requests
urls = [f"https://example.com/changelog/{year}" for year in range(2020, 2027)]
resp = requests.post(
"https://webreaderapi.com/api/v1/batch",
headers={"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]},
json={"urls": urls, "options": {"format": "markdown", "main_content": True}},
timeout=30,
)
resp.raise_for_status()
print("job:", resp.json()["job_id"])
Bagaimana cara memecah Markdown menjadi chunk untuk RAG?
Pecah berdasarkan heading dulu, lalu berdasarkan ukuran. Heading menandai tempat topik berubah, jadi chunk yang dimulai pada heading lebih mudah dicocokkan oleh retriever. Simpan judul halaman dan jalur heading di metadata setiap chunk: keduanya membantu model mengutip bagian yang tepat.
Fungsi di bawah tidak memerlukan library tambahan. Fungsi ini memotong pada heading # hingga ###, lalu memecah bagian yang panjang pada baris kosong agar tetap di bawah batas karakter. Simpan final_url bersama setiap chunk, agar dua alamat yang mengarah ke halaman yang sama tidak membuat duplikat.
import re
def chunk_markdown(page, max_chars=2000):
"""Split a /read response into chunks with a heading path and the source URL."""
chunks, path, buf = [], [], []
def flush():
text = "\n".join(buf).strip()
while text:
part = text[:max_chars]
cut = part.rfind("\n\n")
if len(text) > max_chars and cut > max_chars // 2:
part = part[:cut]
chunks.append({"text": part.strip(), "section": " > ".join(path), "title": page.get("title"), "source": page["final_url"]})
text = text[len(part):].strip()
buf.clear()
for line in page["markdown"].splitlines():
m = re.match(r"^(#{1,3}) (.+)", line)
if m:
flush()
level = len(m.group(1))
path[:] = path[: level - 1] + [m.group(2).strip()]
buf.append(line)
flush()
return chunks
Berapa biaya job Python?
Hitung halaman, bukan request. 1 kredit = 1 halaman dibaca tanpa browser; 2 kredit jika halaman memerlukan browser untuk merender JavaScript. Permintaan yang gagal, atau yang ditolak oleh robots.txt suatu situs, tidak dikenai biaya. Membaca 2.000 halaman dokumentasi tanpa browser menghabiskan sekitar 2.000 kredit, yang ditanggung 10.000 halaman (US$3,75) lima kali lipat. Kredit prabayar tidak pernah kedaluwarsa: halaman yang belum digunakan tetap ada di akun Anda sampai Anda memakainya, jadi pipeline yang berjalan sebulan sekali hanya memakai apa yang dibacanya.
Python: pertanyaan umum
Apakah ada paket Python resmi?
Tidak. Kode di halaman ini sudah cukup: WebReaderAPI adalah API HTTPS biasa, dan requests atau httpx dapat memanggilnya langsung.
Bagaimana cara menangani halaman yang ditolak robots.txt?
Periksa kode error. blocked_by_robots dan target_refused bersifat final dan gratis; catat lalu lanjutkan. Halaman errors memiliki helper retry yang hanya mengulang kode yang bisa berubah.
Bisakah saya menggunakan API dari notebook Jupyter?
Bisa. Resep requests berfungsi apa adanya. Di notebook, await read_many(urls) menggantikan asyncio.run, karena notebook sudah menjalankan event loop.
Timeout berapa yang harus dipakai klien saya?
Sedikit lebih besar dari timeout API itu sendiri: default-nya 30 detik dan maksimal 60, jadi 75 detik di sisi klien memberi ruang untuk situs yang lambat dan responsnya.