Crawling
Yozh Crawler menjelajahi sebuah situs mulai dari satu URL awal dan mengalirkan setiap halaman yang ditemukan melalui SSE. Yozh Crawler bertanggung jawab atas proses penemuan — penjelajahan awal, penghapusan duplikat, cakupan, ekstraksi tautan, upaya ulang, dan etika akses — sementara pengambilan setiap halaman diserahkan kepada Yozh Scraper melalui HTTP. Hanya ada satu stack Playwright yang perlu dioperasikan, dan setiap fitur scraper (proxy, stealth, sesi, aturan ekstraksi) juga berlaku untuk halaman yang di-crawl.
- Predikat cakupan —
same-domain/subdomains/all/regex, dengan pola include & exclude serta batas atas dan bawah yang tetapmax_depth/max_pagesbatas atas yang tetap. - Penghapusan duplikat berdasarkan sidik jari — URL-URL dikanonisasikan lalu di-hash (SHA1) sehingga permintaan yang urutannya diubah dan varian dengan tanda garis miring di akhir digabungkan menjadi satu kunjungan.
- Sistem round-robin per-domain — satu host yang lambat atau berukuran besar tidak dapat menghabiskan kuota pekerja yang seharusnya dialokasikan untuk host lainnya.
- Pembatas laju adaptif — sebuah token bucket global per domain (dengan jitter) yang digunakan bersama oleh semua pekerjaan;
429mengurangi setengah RPS selama periode pendinginan. - Kondisi sesi — Sistem penilaian ala Crawlee:
401/403/429menghentikan sesi, kesalahan meningkatkan skornya, keberhasilan menurunkan skornya; melakukan rotasi saat skor ≥ 3 atau setelah 50 kali penggunaan. - Streaming SSE — setiap tugas menghasilkan
stats,page,page_error, dan acaradone/cancelled. Tindaklanjuti temuan segera setelah muncul.
:8000 dan crawler di :8001.Mulai cepat
Crawler tersebut dihubungkan ke root docker-compose.yml dan muncul bersamaan dengan scraper:
docker compose up --build # scraper → http://localhost:8000 # crawler → http://localhost:8001
Atau jalankan secara lokal tanpa Docker — arahkan SCRAPER_URL ke scraper yang sedang berjalan:
cd yozh-crawler pip install -r requirements.txt cp .env.example .env # edit SCRAPER_URL if needed python -m uvicorn src.main:app --reload --port 8001
Penggunaan dasar
Kirim permintaan perayapan dengan seed_url dan sebuah scope. Panggilan tersebut langsung mengembalikan job_id segera; proses perayapan kemudian berjalan di latar belakang melalui tugas-tugas pekerja.
curl -X POST http://localhost:8001/api/v1/crawl
-H "Content-Type: application/json"
-d '{
"seed_url": "https://example.com",
"scope": {"mode": "same-domain", "max_depth": 2, "max_pages": 50,
"per_domain_rps": 1.0, "per_domain_concurrency": 1},
"scrape_options": {"proxy_type": "none"},
"enable_scraping": false
}'
Responsnya hanyalah handle tugas:
{ "job_id": "crawl_abc123" }
Dari sini, Anda bisa menonton siaran langsung acara atau mengakses data lengkapnya.
Penemuan vs panen — tombol enable_scraping toggle
Mesin perayapan yang sama beroperasi dalam dua arah. Sebuah nilai boolean menentukan pilihan antara pemetaan penemuan yang efisien dan pengumpulan data dengan muatan penuh — dan setiap mode menggunakan konfigurasi proxy-nya sendiri, sehingga tidak terjadi tumpang tindih.
Peta penjelajahan
Proses pemindaian ringan. Scraper tetap memproses setiap halaman (sehingga tautan yang dibuat dengan JavaScript dapat terdeteksi), namun crawler hanya menyimpan kerangka dasarnya — sangat cocok untuk peta situs, audit tautan, dan deteksi perubahan.
- Menjaga
url·parent_url·depth·status·took_ms - Menampilkan HTML mentah, tangkapan layar, dan data yang diekstraksi
- Menggunakan yang lebih murah
crawl_proxy - Muatan terkecil, kecepatan merayap tercepat
Panen penuh
Setiap halaman yang dikunjungi disimpan beserta ScrapeResponse — HTML mentah, tangkapan layar (opsional), serta bidang apa pun dari aturan ekstraksi Anda. Lakukan perayapan dan penataan dalam satu proses.
- Menampilkan isi lengkap
ScrapeResponseper halaman -
raw_html·screenshot· diekstraksidata - Kegunaan
scrape_options.proxy_* - Lolos
extractaturan untuk menghasilkan JSON yang bersih
crawl_proxy hanya digunakan jika enable_scraping=false; scrape_options.proxy_* digunakan ketika true. Jika crawl_proxy adalah null, scrape_options proksi akan digunakan terlepas dari mode yang digunakan.Cakupan perayapan
Cakupan menentukan batasan proses perayapan: di mana proses perayapan diperbolehkan berjalan, dan seberapa intens proses perayapan diperbolehkan mengakses setiap host. Ini adalah scope objek pada permintaan tersebut.
| Bidang | Tipe | Bawaan | Deskripsi |
|---|---|---|---|
mode | same-domain · subdomains · all · regex | same-domain | Pertandingan mana saja yang termasuk dalam cakupan. regex cocok dengan include_patterns. |
include_patterns | string[] | [] | Pola regex yang harus dipenuhi oleh sebuah URL agar dapat dimasukkan ke dalam antrian (digunakan oleh regex mode / sebagai daftar putih). |
exclude_patterns | string[] | [] | Pola regex yang mengabaikan sebuah URL meskipun URL tersebut sebenarnya berada dalam cakupan. |
max_depth | int | 3 | Kedalaman tautan maksimum dari node awal (node awal berada pada kedalaman 0). |
max_pages | int | 500 | Batas maksimum jumlah halaman yang dikunjungi sebelum proses perayapan selesai. |
per_domain_rps | float | 1.0 | Laju pengisian ulang token-bucket per domain (permintaan per detik). |
per_domain_concurrency | int | 1 | Jumlah maksimum permintaan selama penerbangan ke satu domain. |
subdomains. Metode ini menggunakan dua label terakhir sebagai domain yang dapat didaftarkan, sehingga menghasilkan kecocokan berlebih pada host dalam Daftar Suffiks Publik seperti github.io atau co.uk. Gunakan same-domain atau regex untuk yang tersebut.Hasil streaming (SSE)
Setiap tugas menampilkan aliran Server-Sent Events. Bacalah aliran tersebut untuk mengambil tindakan pada halaman-halaman begitu halaman-halaman tersebut terdeteksi, alih-alih menunggu proses perayapan selesai.
curl -N http://localhost:8001/api/v1/crawl/crawl_abc123/events
Aliran tersebut mengeluarkan lima jenis peristiwa:
| Peristiwa | Kapan |
|---|---|
stats | Ringkasan kemajuan berkala (telah dikunjungi / dalam antrian / gagal / dilewati deduplikasi / di luar cakupan / dicoba ulang). |
page | Satu per URL yang dikunjungi. Menyimpan metadata lengkap ScrapeResponse dalam mode panen, metadata ringkas dalam mode penemuan. |
page_error | Sebuah URL yang gagal meskipun sudah dicoba berulang kali. |
done | Terminal — proses perayapan selesai dengan lancar. |
cancelled | Terminal — proses perayapan dibatalkan. |
Sebuah page Acara dalam mode penemuan terlihat seperti ini:
{
"event": "page",
"url": "https://example.com/docs/intro",
"parent_url": "https://example.com/docs",
"depth": 2,
"status_code": 200,
"took_ms": 812,
"scrape_response": null
}
Status & hasil
Lebih suka metode polling? Catatan pekerjaan dapat diakses kapan saja — termasuk saat proses crawling sedang berlangsung, dengan halaman-halaman yang telah ditemukan sejauh ini. /results adalah alias dari titik akhir pekerjaan, yang dipertahankan untuk kesesuaian dengan scraper.
curl http://localhost:8001/api/v1/crawl/crawl_abc123 curl http://localhost:8001/api/v1/crawl/crawl_abc123/results
{
"job_id": "crawl_abc123",
"status": "running",
"stats": {
"visited": 47, "queued": 12, "failed": 0,
"dedup_skipped": 9, "out_of_scope": 31, "retries_total": 2
},
"pages": [
{ "url": "https://example.com/", "parent_url": null,
"depth": 0, "status_code": 200, "took_ms": 640 }
]
}
status melewati queued → running → done (atau failed / cancelled).Membatalkan proses perayapan
Hentikan pekerjaan yang sedang berjalan dengan DELETE. Pembatalan lembut adalah pengaturan default dan pilihan yang aman.
# soft — stop scheduling, let in-flight requests drain curl -X DELETE "http://localhost:8001/api/v1/crawl/crawl_abc123?hard=false" # hard — abort in-flight asyncio tasks immediately curl -X DELETE "http://localhost:8001/api/v1/crawl/crawl_abc123?hard=true"
?hard=true akan membatalkan permintaan yang sedang diproses oleh crawler, tetapi scraper tidak memiliki cara untuk mengetahuinya — proses rendering halamannya tetap selesai di sisinya. Sebaiknya gunakan pembatalan lunak kecuali Anda benar-benar harus menghentikannya sekarang juga.MCP
fastapi-mcp dipasang di /mcp sebagai titik akhir HTTP yang dapat dialirkan. Arahkan Claude atau Cursor ke sana dan alat-alat tersebut akan muncul secara otomatis:
healthcreate_crawlget_crawlget_crawl_resultscancel_crawl
"open-crawler": {
"type": "http",
"url": "http://localhost:8001/mcp"
}
stream_crawl_events titik akhir SSE sengaja tidak dimasukkan ke dalam MCP — respons streaming tidak sesuai dengan alat berbasis permintaan/respons.Panduan Konfigurasi
Permintaan — CrawlRequest
| Bidang | Tipe | Bawaan | Deskripsi |
|---|---|---|---|
seed_url | string (URL) | wajib | URL tunggal yang menjadi titik awal proses perayapan. |
scope | CrawlScope | defaults | Batas & kesopanan — lihat Ruang lingkup perayapan. |
scrape_options | ScrapeOptions | defaults | Diteruskan secara utuh ke scraper per halaman (url dimasukkan). |
crawl_proxy | ScrapeOptions · null | null | Proxy murah yang digunakan dalam mode penelusuran. Hanya proxy_type / proxy_pool_id / proxy_geo yang dibaca. |
enable_scraping | bool | false | Tetap pertahankan jumlah ScrapeResponse per halaman (true) atau hanya metadata penelusuran (false). |
Per halaman — ScrapeOptions (terpilih)
| Bidang | Tipe | Bawaan | Deskripsi |
|---|---|---|---|
proxy_type | none · mobile · res_static · res_rotating · dc_static · … | none | Kumpulan proxy yang dilalui oleh rute pengambilan halaman. |
device | desktop · mobile | desktop | Viewport / Profil UA. |
render | bool | true | Render menggunakan browser (diperlukan untuk tautan yang dibuat dengan JS). |
stealth | bool | true | Terapkan penguatan anti-deteksi. |
wait_until | domcontentloaded · networkidle | domcontentloaded | Ketika halaman tersebut dianggap sudah siap. |
screenshot | bool | false | Ambil tangkapan layar (mode panen). |
extract | ExtractRule · null | null | Aturan bidang CSS/XPath → terstruktur data per halaman. |
session_id | string · null | null | Gunakan kembali sesi scraper yang sudah terotentikasi — lihat Pengambilan data yang terotentikasi. |
Variabel lingkungan
| Variabel | Bawaan | Catatan |
|---|---|---|
SCRAPER_URL | http://web-scraper:8000 | Scraper hulu (gunakan nama layanan Docker di dalam jaringan Compose). |
WORKERS | 2 | Jumlah pekerjaan crawl paralel. |
QUEUE_MAXSIZE | 200 | Kedalaman antrian pekerjaan yang tertunda. |
JOB_TIMEOUT_MS | 3_600_000 | Batas waktu jam dinding untuk satu tugas perayapan. |
SCRAPER_JOB_TIMEOUT_MS | 120_000 | Batas waktu permintaan scraper per halaman. |
MAX_RETRIES | 3 | Jumlah upaya ulang per permintaan saat terjadi kegagalan sementara. |
RETRY_HTTP_CODES | [408,429,500,502,503,504] | Kode status yang memicu upaya ulang. |
RETRY_BACKOFF_MAX | 30.0 | Batas atas untuk backoff eksponensial (detik). |
SESSION_MAX_ERROR_SCORE | 3.0 | Ambang batas penghentian sesi. |
SESSION_MAX_USAGE | 50 | Putar sesi setelah digunakan sebanyak N kali. |
SESSION_BLOCKED_CODES | [401,403,429] | Kode yang langsung mengakhiri sesi. |
Referensi API
| Metode | Jalur | Tujuan |
|---|---|---|
| POST | /api/v1/crawl | Buat pekerjaan. Mengembalikan {"job_id":"…"}. |
| GET | /api/v1/crawl/{id} | Catatan pekerjaan — status, statistik, halaman yang sudah dikerjakan. |
| GET | /api/v1/crawl/{id}/results | Alias dari catatan pekerjaan. |
| GET | /api/v1/crawl/{id}/events | Alur SSE (stats/page/page_error/done/cancelled). |
| DELETE | /api/v1/crawl/{id}?hard=bool | Batalkan pekerjaan (pembatalan lunak atau keras). |
| GET | /api/v1/health | Kesehatan + jangkauan pengikis. |
Rincian penting
robots.txt tidak dikonfirmasi. Crawler akan menjelajahi setiap URL yang termasuk dalam cakupan, terlepas dari /robots.txt. Anda bertanggung jawab untuk mematuhi aturan robot dan ketentuan situs pada target yang bukan milik Anda — gunakan exclude_patterns dan batasan laju agar tetap sopan.POST /sessions + POST /sessions/{id}/login), lalu kirimkan scrape_options.session_id. Scraper mengirim dan menerima cookie serta status penyimpanan pada setiap permintaan, sehingga setiap halaman yang dijelajahi akan melihat status yang telah diautentikasi.