請啟用 JavaScript 來查看內容

[Python爬蟲實例] 爬取"Agoda"訂房網站的搜尋住宿結果

前言

今天的 Python網路爬蟲實例 系列將來介紹 Agoda 訂房網站該如何搜尋住宿結果,看看它是如何發出請求、解析回傳資料,它 WebAPI 的查詢語言有別於我們之前遇過的 REST 風格,它是採用比較新的 GraphQL。

會寫這篇文章,是之前有網友詢問,我稍微試了一下,並將結果記錄下來,因此本篇不會有太詳細的介紹。

旅館 (來源:Unsplash)
旅館 (來源:Unsplash)

備註:此文僅教育學習,切勿用作商業用途,個人實作皆屬個人行為,本作者不負任何法律責任。

套件

此次 Python 爬蟲主要使用到的套件:

安裝

1
pip install requests

搜尋住宿

請求路徑與參數

我直接在 Agoda 首頁搜尋 "台北市" 的住宿,接下來打開瀏覽器的 開發人員工具 (F12 或 Ctrl + Shift + i)並重整網頁。

切換到 Network(網路) > Fetch/XHR 分頁,會看見一個 /search 的請求,裡面包含我們這次搜尋住宿的結果。

開發人員工具 > Network > Fetch/XHR
開發人員工具 > Network > Fetch/XHR

切到 Headers 查看請求的方式與參數。

網址是 https://www.agoda.com/graphql/search、使用 POST 方法、需要帶 payload

其實從網址就可以猜的出來,它可能是採用 GraphQL 查詢語言。

"搜尋住宿"請求的方式與參數
"搜尋住宿"請求的方式與參數

我將其整理出來,相關參數如下:

GraphQL 查詢語言會把需要的欄位全部列在 Request Body 的 json 格式字串內,所以它請求的 Request Body 會很~長~一串,我文章中就不全部列出來了,可以直接參考這個檔案(agoda_query.json)或自行用瀏覽器的開發人員工具查看。

Request URL:https://www.agoda.com/graphql/search
Request Methon:POST
Request Headers:

1
2
3
4
{
    "Content-Type": "application/json",
    "Ag-Language-Locale": "zh-tw"
}

Request Payload:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
    "operationName": "citySearch",
    "variables": {
        "CitySearchRequest": {},
        "ContentSummaryRequest": {},
        "PricingSummaryRequest": {},
        "PriceStreamMetaLabRequest": {}
    },
    "query": "query citySearch($CitySearchRequest: CitySearchRequest!, $ContentSummaryRequest: ContentSummaryRequest!, ......"
}

城市代表 ID (cityId) 可以從我們查詢時的網址列 (city) 找到:
https://www.agoda.com/zh-hk/search?city=4951&checkIn=2023-06-19&los=1&rooms=1&... 裡面的 city=4951


回傳資料

GraphQL 查詢語言的特點就是,其回傳資料的格式等同於你發出請求時所指定的欄位(也就是我們 agoda_query.json 檔案的內容)。

住宿搜尋結果會列在 ["data"]["citySearch"]["properties"] 裡面。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
  "data": {
    "citySearch": {
      "aggregation": {},
      "featuredPulseProperties": [],
      "properties": ["住宿搜尋結果會列在這裡"],
      "searchEnrichment": {},
      "searchResult": {}
    }
  }
}

範例程式

使用 Python 搭配 requests 套件的寫法如下:

* 因為請求需要夾帶的 JSON 太長,我存在另外一個檔案(agoda_query.json)內再讀取進來。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests
import json


# 因為請求需要夾帶的 JSON 太長,我存在另外一個檔案內再讀取進來
f = open('agoda_query.json')
data = json.load(f)

response = requests.post(
    'https://www.agoda.com/graphql/search',
    headers={
        'Content-Type': 'application/json',
        'Ag-Language-Locale': 'zh-tw'
    },
    json=data
)

print(response.json())

# 將回傳結果儲存在 JSON 檔案
with open('agoda_result.json', 'w') as fp:
    json.dump(response.json(), fp)

延伸練習

  1. 我們在搜尋住宿時,會給他許多不同的篩選欄位,例如價格、地點、星級、床型、評價得分…,你知道如何將這些篩選條件加入請求嗎?
    (提示:可以觀察 agoda_query.json 這個檔案內容)

  2. Agoda 網站除了找住宿以外,它還可以搜尋機票,試著自己實際找找看吧。
    (提示:它好像有一個 POST 請求 https://www.agoda.com/api/gw/flight/searchunbundled,不知道是不是)


結語

Agoda 訂房網站的 WebAPI 是採用比較新的 GraphQL,它可以精確地選擇所需的資料、避免請求過多或過少的資料,有別於我們之前遇過、常見的 REST。

關於 GraphQL 的介紹與 REST 的差異比較,如果有人想看的話(按讚或留言讓我知道),之後考慮整理一篇出來。

之後會繼續陸續寫一些網站的Python網路爬蟲實例,如果你正好是剛開始想學爬蟲的新手、想知道某網站如何爬取資料、遇到其他問題,也歡迎在底下留言,或追蹤 FB 粉專『IT空間』~ 🔔




所謂失敗,並非被打倒,而是倒地不起。


🔻 如果覺得喜歡,歡迎在下方獎勵我 5 個讚~
分享

Jia
作者
Jia
軟體工程師 - Software Engineer