Agoda_hotel_room
Pricing
Pay per usage
Agoda_hotel_room
Pricing
Pay per usage
Rating
0.0
(0)
Developer
39_Đinh Nguyễn Nhật Lâm
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
2
Monthly active users
6 days ago
Last modified
Categories
Share
Agoda Hotel Scraper
Trích xuất dữ liệu khách sạn có cấu trúc từ Agoda: thông tin khách sạn, điểm review theo hạng mục, loại phòng, tiện nghi, tọa độ và hình ảnh — bằng cách gọi JSON API nội bộ của Agoda thay vì parse HTML, giúp bền vững hơn khi giao diện thay đổi.
Actor này lấy cảm hứng từ
bovi/agoda-scraper. Đây là bản tự xây để học/tùy biến.
Kiến trúc & vai trò của Apify
Actor gồm 2 phần tách biệt: hạ tầng (Apify) và logic nghiệp vụ (code trong src/).
Apify không "biết" gì về Agoda — nó chỉ là nơi chạy code, cấp proxy và lưu kết quả. Toàn bộ
việc gọi đúng endpoint, dịch dữ liệu, xử lý lỗi là code tự viết.
flowchart LRsubgraph User["Người dùng"]U1[Nhập input trên Console/API]U2[Xem / tải Dataset]endsubgraph Apify["Apify Platform — hạ tầng"]direction TBA1[Console / API]A2["Build từ GitHub<br/>(Dockerfile + .actor/actor.json)"]A3[Container chạy Actor]A4[Proxy Residential]A5[("Dataset (lưu trữ + export)")]endsubgraph Actor["Container Actor — code tự viết (src/)"]direction TBM["main.py<br/>điều phối input → output"]AG["agoda.py<br/>logic gọi & parse Agoda"]endsubgraph AgodaSite["agoda.com — bên thứ 3"]S[Suggest API]G["GraphQL<br/>propertyDetailsSearch"]SD[GetSecondaryData]endU1 --> A1 --> A3A2 -. build image .-> A3A3 --> M --> AGAG <-->|qua proxy| A4A4 <--> SA4 <--> GA4 <--> SDM -->|push_data| A5A5 --> U2
Bên trong 1 lần lấy khách sạn (fetch_hotel) — gọi song song 2 nguồn rồi gộp lại:
sequenceDiagramparticipant Main as main.pyparticipant Client as AgodaClient (agoda.py)participant Agoda as agoda.comMain->>Client: resolve_search("Hilton Saigon")Client->>Agoda: GET GetUnifiedSuggestResultAgoda-->>Client: danh sách ứng viên + hotelIdClient-->>Main: hotelId = 47843559Main->>Client: fetch_hotel(47843559)par Nội dung chínhClient->>Agoda: POST /graphql/property (propertyDetailsSearch)Agoda-->>Client: tên, địa chỉ, review, tiện nghi, ảnh...and Phòng & lân cậnClient->>Agoda: GET GetSecondaryDataAgoda-->>Client: rooms, nearby, highlights, giờ check-in/outendClient-->>Main: record đã gộp + chuẩn hoá (40 field)Main->>Main: push_data(record) → Dataset
Ai làm gì
| Thành phần | Do ai cung cấp | Vai trò |
|---|---|---|
| Nơi chạy actor (container, trigger qua Console/API/lịch chạy) | Apify | Hạ tầng chạy code — không cần tự dựng server |
Actor.get_input() / push_data() / proxy xoay IP / log | Apify SDK (apify package) | Cầu nối giữa code Python và nền tảng: đọc input form, ghi Dataset, cấp proxy |
| Build image từ GitHub, versioning | Apify | Tự động build lại mỗi khi bấm Build (đọc Dockerfile + .actor/actor.json) |
| Kho lưu kết quả, export JSON/CSV/Excel | Apify (Dataset) | Không cần tự dựng database |
| Biết gọi đúng endpoint nào của Agoda, header nào bắt buộc | Code tự viết (src/agoda.py) | "Não" của actor — Apify hoàn toàn không biết gì về Agoda |
| Parse, chuẩn hoá, dịch dữ liệu theo ngôn ngữ | Code tự viết (src/agoda.py) | Gộp 3 nguồn Agoda thành 1 record 40 field |
| Đọc input, điều phối vòng lặp, xử lý lỗi từng khách sạn | Code tự viết (src/main.py) | Cầu nối giữa input Apify và logic Agoda |
Nói ngắn gọn: nếu thay Agoda bằng 1 trang web khác, phần Apify không đổi gì cả — chỉ cần
viết lại src/agoda.py.
Tính năng
- Nhận từ khóa tìm kiếm (tên khách sạn cụ thể), URL trang khách sạn, hoặc tên vùng/thành phố (vd "Hồ Chí Minh") làm đầu vào — với vùng, actor tự liệt kê hàng loạt khách sạn trong đó.
- Trả về metadata khách sạn: tên, địa chỉ, hạng sao, điểm review, số review, tiện nghi, phòng, tọa độ, ảnh.
- Hỗ trợ proxy (khuyên dùng Apify Residential) và giới hạn tần suất để giảm nguy cơ bị chặn.
- Bắt lỗi theo từng khách sạn: một lỗi không làm hỏng cả run.
- Có
warningsđể báo chất lượng parse (rỗng nếu bình thường).
Input
| Field | Kiểu | Mô tả |
|---|---|---|
searchTerms | array (string) | Tên khách sạn hoặc điểm đến. Hiệu quả nhất với tên cụ thể (vd "The Peninsula Bangkok"). |
propertyUrls | array (string) | URL trang khách sạn Agoda. Actor tự tách hotelId. Nếu URL có sẵn checkIn/checkOut (hoặc los)/adults/rooms, actor tự lấy GIÁ THỰC TẾ cho khách sạn đó — không cần điền checkIn ở dưới. |
hotelIds | array (string/int) | ID khách sạn Agoda nếu đã biết (vd 10715). |
locations | array (string) | Tên vùng/thành phố (vd "Hồ Chí Minh") hoặc dán trực tiếp link trang tìm kiếm Agoda (vd https://www.agoda.com/vi-vn/search?city=13170&checkIn=2026-08-01&checkOut=2026-08-03&adults=2&rooms=1) — link được ưu tiên xử lý vì đã có sẵn cityId, nhanh hơn. Nếu link có ngày/số khách, TẤT CẢ khách sạn tìm được trong vùng đó dùng chung tiêu chí này để lấy giá thực tế. Giới hạn bởi maxItemsPerLocation. |
maxItemsPerLocation | integer | Số khách sạn tối đa lấy cho mỗi vùng trong locations. 0 = không giới hạn (có trần an toàn nội bộ ~2000). Mặc định 50; ví dụ dùng thực tế: 200. |
checkIn | string | Ngày check-in mặc định (YYYY-MM-DD, chọn qua date-picker trên Console), dùng cho khách sạn không có sẵn ngày từ URL (vd hotelIds, searchTerms, hoặc locations gõ tên thường). Bỏ trống = không lấy giá (nhanh hơn). |
checkOut | string | Ngày check-out (YYYY-MM-DD, date-picker). Nếu bỏ trống, tự tính từ checkIn + lengthOfStay. |
lengthOfStay | integer | Số đêm ở lại — chỉ dùng khi không cấp checkOut. Mặc định 1. |
adults / rooms | integer | Số người lớn / số phòng, dùng cùng checkIn. Mặc định 2 / 1. |
language | string | Ngôn ngữ kết quả: en-us (mặc định), vi-vn, th-th, ko-kr, ja-jp, zh-cn, id-id. Ảnh hưởng tên phòng, tiện nghi, mô tả, địa điểm lân cận, và cả category_scores/score_distribution. |
maxItems | integer | Số bản ghi tối đa. 0 = không giới hạn. Mặc định 0. |
requestDelay | integer | Số giây chờ giữa các request (0–30). Mặc định 2. |
proxyConfiguration | object | Cấu hình proxy. Mặc định Apify Residential. |
⚠️ Giá hiện chỉ hỗ trợ tiền tệ VND. Mỗi khách sạn có
checkIn(từ URL hoặc input) sẽ tốn thêm 1 request gọi giá (room-grid), nên chạy chậm hơn khi bật tính năng này ở quy mô lớn.
Ví dụ input — tìm 1 khách sạn cụ thể
{"searchTerms": ["The Peninsula Bangkok"],"propertyUrls": [],"maxItems": 0,"requestDelay": 2,"proxyConfiguration": {"useApifyProxy": true,"apifyProxyGroups": ["RESIDENTIAL"]}}
Ví dụ input — tìm 200 khách sạn theo vùng
Cách 1 — gõ tên vùng:
{"locations": ["Hồ Chí Minh"],"maxItemsPerLocation": 200,"language": "vi-vn","requestDelay": 2,"proxyConfiguration": {"useApifyProxy": true,"apifyProxyGroups": ["RESIDENTIAL"]}}
Cách 2 — dán link tìm kiếm Agoda (copy từ trình duyệt sau khi tự tìm trên agoda.com, nhanh hơn
vì đã có sẵn cityId, không tốn thêm request đổi tên → id):
{"locations": ["https://www.agoda.com/vi-vn/search?city=13170&locale=vi-vn&textToSearch=H%E1%BB%93+Ch%C3%AD+Minh"],"maxItemsPerLocation": 200,"language": "vi-vn"}
Ví dụ input — lấy kèm giá thực tế (không còn null)
Cách đơn giản nhất: dán URL đã có sẵn ngày/số khách (copy thẳng từ thanh địa chỉ trình duyệt
sau khi bạn tự chọn ngày trên agoda.com) — actor tự đọc checkIn/checkOut/adults/rooms
từ URL, không cần khai báo lại:
{"propertyUrls": ["https://www.agoda.com/hilton-saigon/hotel/ho-chi-minh-city-vn.html?checkIn=2026-09-10&checkOut=2026-09-12&adults=2&rooms=1"],"language": "vi-vn"}
Hoặc dùng checkIn/checkOut (hoặc lengthOfStay)/adults/rooms ở input gốc — áp dụng cho
mọi khách sạn không có sẵn ngày riêng (kể cả hotelIds, searchTerms, hoặc locations gõ tên
thường):
{"locations": ["Hồ Chí Minh"],"maxItemsPerLocation": 20,"checkIn": "2026-09-10","checkOut": "2026-09-12","adults": 2,"rooms": 1,"language": "vi-vn"}
Không chọn
checkOut? Actor tự tính bằngcheckIn+lengthOfStay(mặc định1đêm) như trước đây.
⚠️ Mỗi khách sạn tìm theo vùng vẫn lấy đầy đủ 40 field (2–3 request/khách sạn, thêm 1 nếu có lấy giá) nên chạy chậm hơn nhiều so với danh sách rút gọn: 200 khách sạn × ~3 request ×
requestDelaygiây có thể mất hàng chục phút. Muốn nhanh hơn thì giảmmaxItemsPerLocationhoặc giảmrequestDelay(đánh đổi với rủi ro bị chặn).
Output (mỗi bản ghi vào Dataset)
| Field | Mô tả |
|---|---|
hotel_id / hotel_name | Định danh & tên |
accommodation_type | Loại hình (Hotel, Apartment...) |
address / city / country / area_name | Vị trí |
location_highlight | Nhấn mạnh vị trí (vd "trong nội thành") |
star_rating | Hạng sao (1–5) |
review_score / review_count / review_text | Điểm, số review, nhãn đã dịch theo language (vd "Trên cả tuyệt vời") |
category_scores | Điểm theo hạng mục — nhãn đã dịch theo language (vd {"Độ sạch sẽ": 9.6, "Vị trí": 9.7, ...}), lấy trực tiếp từ Agoda nên đúng cho mọi ngôn ngữ hỗ trợ |
score_distribution | Phân bố review theo mức — nhãn đã dịch theo language (vd {"9+ Hiếm Có": 380, ...}) |
awards | Giải thưởng (nếu có) |
price / currency | Giá phòng rẻ nhất/đêm toàn khách sạn (hiện chỉ hỗ trợ VND) — chỉ có giá trị khi cấp checkIn (qua URL hoặc input); không thì null. |
rooms_available | Còn phòng cho ngày đã chọn hay không (true/false/null). |
offer_count | Tổng số gói giá (offer) tìm được trên mọi loại phòng. |
check_in / check_out | Ngày dùng để tính giá (đã chuẩn hoá YYYY-MM-DD, null nếu không lấy giá). |
room_types | Danh sách tên loại phòng |
rooms | Chi tiết từng phòng — xem bảng riêng bên dưới. |
amenities / amenity_groups | Tiện nghi (phẳng & theo nhóm) |
highlights | Điểm nổi bật của khách sạn |
nearby_attractions | Điểm tham quan gần (kèm khoảng cách) — nguồn không có toạ độ riêng từng điểm |
nearby_essentials | Sân bay/giao thông/tiện ích: category, name, distance_km, distance_text, coordinates (toạ độ riêng của điểm đó, dạng "lat,lng") |
check_in_time / check_in_until | Giờ bắt đầu nhận phòng / giờ nhận phòng muộn nhất (vd 14:00 / 23:59) |
check_out_time / reception_open_until | Giờ trả phòng / giờ quầy tiếp tân đóng cửa (vd 12:00 / 05:30) — không phải khách sạn nào cũng có đủ 4 mục này, thiếu thì để null |
description | Mô tả đầy đủ (HTML) |
image_url / image_count / all_images | Ảnh |
coordinates | Toạ độ dạng chuỗi "lat,lng" (vd "10.775087,106.705696") — dán thẳng được vào Google Maps hoặc hầu hết ứng dụng bản đồ |
property_url | URL trang khách sạn |
warnings | Cảnh báo parse (rỗng nếu bình thường) |
scraped_at | Thời điểm crawl (ISO-8601 UTC) |
Cấu trúc từng phòng trong rooms[]
Có 2 mức chi tiết tuỳ việc actor có gọi được giá hay không (tức có checkIn hay không):
| Field | Luôn có | Chỉ có khi cấp checkIn (gọi thêm room-grid) |
|---|---|---|
name, room_id, bed, size, view | ✅ | ✅ |
price_per_night / currency / crossed_out (có giảm giá) | – | ✅ |
sold_out (còn phòng loại này hay không) | – | ✅ — phòng hết chỗ vẫn xuất hiện đầy đủ, chỉ price_per_night: null |
max_occupancy (vd "Tối đa 2 người lớn") | – | ✅ |
review_score / review_text (điểm review riêng của loại phòng, khác điểm chung khách sạn) | – | ✅ |
amenity_groups (tiện nghi theo nhóm, đầy đủ như trên trang phòng) | – | ✅ |
images (toàn bộ ảnh, không chỉ 1 ảnh đại diện) / image_count | – | ✅ |
Không có checkIn → mỗi phòng chỉ có bộ field cơ bản (name, room_id, bed, size, view,
features, image_count, thumbnail) lấy từ trang khách sạn thường, không cần gọi thêm request.
Dùng qua API
Python
from apify_client import ApifyClientclient = ApifyClient("<YOUR_API_TOKEN>")run = client.actor("<username>/agoda-scraper").call(run_input={"searchTerms": ["The Peninsula Bangkok"]})for item in client.dataset(run["defaultDatasetId"]).iterate_items():print(item)
JavaScript
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });const run = await client.actor('<username>/agoda-scraper').call({searchTerms: ['The Peninsula Bangkok'],});console.log(`Dataset: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
CLI
$echo '{ "searchTerms": ["The Peninsula Bangkok"] }' | apify call <username>/agoda-scraper --silent --output-dataset
Cơ chế hoạt động
Actor gọi trực tiếp các endpoint JSON nội bộ của Agoda (không cần trình duyệt):
- Tìm kiếm theo tên khách sạn —
GET /api/cronos/search/GetUnifiedSuggestResult/...?searchText=...→ lọcIsHotel==trueđể lấyhotelId, tên, thành phố, quốc gia. - Tìm theo vùng/thành phố (
locations) — 2 bước:- Cùng endpoint suggest ở trên, nhưng lọc entry
CityId == ObjectId != 0(thành phố thật, có khách sạn) để đổi tên vùng →cityId. POST /graphql/search(operationcitySearch, query lưu trong src/city_search_query.py) → liệt kêhotelIdtrong thành phố, phân trang tối đa 100/lần (page.pageSize/page.pageNumber). Headerag-page-type-id: 644.- Danh sách
hotelIdtìm được feed thẳng vào bước 3 (chi tiết đầy đủ) như mọi nguồn khác.
- Cùng endpoint suggest ở trên, nhưng lọc entry
- Chi tiết 1 khách sạn — 2 nguồn gộp lại:
POST /graphql/property(operationpropertyDetailsSearch, query lưu trong src/property_details_query.py) → tên, địa chỉ, review, tiện nghi, mô tả, ảnh, tọa độ. Cần bộ headerag-*bắt buộc (thiếu sẽ bị400 Missing required headers).GET /api/cronos/property/BelowFoldParams/GetSecondaryData→ rooms, room_types, nearby_attractions, nearby_essentials, highlights, location_highlight, vàcategory_scores/score_distribution(nhãn đã dịch sẵn theo ngôn ngữ, lấy từreviews.demographic— không cần tự dịch).
- Giá theo ngày check-in (tuỳ chọn, chỉ gọi khi có
checkIn) —POST /api/v1/property/room-grid(body template lưu trong src/room_grid_template.py) → giá rẻ nhất/đêm, tình trạng còn phòng, danh sách offer theo từng loại phòng. Cần headerag-initiator-api-key(định danh client cố định, không phải auth cá nhân). Hiện chỉ hỗ trợ tiền tệ VND.
Ngôn ngữ kết quả điều khiển bằng header ag-language-locale (input language) — hỗ trợ tiếng Việt đầy đủ.
Agoda không công bố API nên schema có thể đổi theo thời gian. Nếu một ngày kết quả rỗng/lỗi, mở lại một trang khách sạn Agoda, chụp request
graphql/propertytrong DevTools > Network, rồi cập nhậtQUERY/VARIABLEStrongproperty_details_query.py. Nên chạy có proxy và giữrequestDelayhợp lý để tôn trọng giới hạn của site.