台灣銀行牌告匯率爬蟲

import re

import requests
import pandas as pd
from bs4 import BeautifulSoup


### 設定網址
url = "https://rate.bot.com.tw/xrt"

### 舊版--失敗
# headers = {
#     "User-Agent": "Mozilla/5.0"
# }
### 新版--成功
headers = {
    "User-Agent": (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 (KHTML, like Gecko) "
        "Chrome/151.0.0.0 Safari/537.36"
    ),
    "Accept": (
        "text/html,application/xhtml+xml,application/xml;"
        "q=0.9,image/avif,image/webp,*/*;q=0.8"
    ),
    "Accept-Language": "zh-TW,zh;q=0.9,en;q=0.8"
}


### 發送 HTTP 請求
response = requests.get(
    url,
    headers=headers,
    timeout=10
) # 向臺灣銀行網站發送 GET 請求,最多等待 10 秒

response.raise_for_status() # 如果請求失敗 (4xx、5xx),直接拋出例外

print("狀態碼:", response.status_code)


### 建立 BeautifulSoup
soup = BeautifulSoup(
    response.text,
    "lxml"
) # 將取得的 HTML 原始碼交給 BeautifulSoup 解析


### 抓取最新掛牌時間
time_element = soup.select_one(
    ".time"
)

if time_element:
    update_time = time_element.get_text(
        strip=True
    )
else:
    update_time = ""

print("最新掛牌時間:", update_time)


### 找到匯率表格中的所有資料列
rows = soup.select(
    "table.table tbody tr"
) # 每一個 <tr> 代表一種外幣

print("幣別數量:", len(rows))


### 建立匯率資料串列
rate_list = []


### 逐列抓取匯率
for row in rows:
    currency_element = row.select_one(
        "td.currency div.hidden-phone" # 抓取電腦版的
    ) # 指定抓取 <div>,避免抓到沒有文字的 <br>

    cash_rates = row.select(
        "td.rate-content-cash"
    )

    sight_rates = row.select(
        "td.rate-content-sight"
    )

    if not currency_element:
        continue

    currency_text = currency_element.get_text(
        " ",
        strip=True
    ) # 例如:美金 (USD)

    match = re.match(
        r"(.+?)\s*\(([A-Z]+)\)",
        currency_text
    )

    if match:
        currency_name = match.group(1).strip()
        currency_code = match.group(2).strip()
    else:
        currency_name = currency_text
        currency_code = ""

    if len(cash_rates) >= 2:
        cash_buy = cash_rates[0].get_text(strip=True)
        cash_sell = cash_rates[1].get_text(strip=True)
    else:
        cash_buy = ""
        cash_sell = ""

    if len(sight_rates) >= 2:
        sight_buy = sight_rates[0].get_text(strip=True)
        sight_sell = sight_rates[1].get_text(strip=True)
    else:
        sight_buy = ""
        sight_sell = ""

    rate_list.append({
        "幣別": currency_name,
        "代碼": currency_code,
        "現金買入": cash_buy,
        "現金賣出": cash_sell,
        "即期買入": sight_buy,
        "即期賣出": sight_sell,
        "掛牌時間": update_time
    })


### 顯示匯率資料
print("\n臺灣銀行牌告匯率:")

for rate in rate_list:
    print()
    print("幣別:", rate["幣別"])
    print("代碼:", rate["代碼"])
    print("現金買入:", rate["現金買入"])
    print("現金賣出:", rate["現金賣出"])
    print("即期買入:", rate["即期買入"])
    print("即期賣出:", rate["即期賣出"])


### 轉換成 DataFrame
rate_df = pd.DataFrame(rate_list)

print("\n匯率資料表:")
print(rate_df)


### 儲存成 CSV
rate_df.to_csv(
    "臺灣銀行牌告匯率.csv",
    index=False,
    encoding="utf-8-sig"
) # 避免使用 Excel 開啟中文 CSV 時出現亂碼


### 執行完成
print("\n資料抓取完成")
print("共抓到:", len(rate_list), "種外幣")
print("已產生:臺灣銀行牌告匯率.csv")