|RTHK News ScraperXPath + requests + lxml
Visit RTHK
⭐ 入門Beginner · Static HTML · No JS Rendering Required

RTHK 即時新聞
XPath 爬蟲教學

RTHK(香港電台)新聞列表使用傳統靜態 HTML 渲染,class 名稱語義清晰(如 ns2-titlens2-created),是學習 XPath 的最佳入門網站。

TARGET URLhttps://news.rthk.hk/rthk/ch/latest-news.htm
XPATH FIELDS — 7 個可爬取字段
字段XPath Expression語法重點
新聞標題//h4[@class="ns2-title"]/a/text()直接文字節點
新聞連結//h4[@class="ns2-title"]/a/@href屬性選取
發布時間//div[@class="ns2-created"]/font/text()嵌套元素
新聞類別//div[@class="ns2-category"]/a/text()類別標籤
所有條目容器//div[@class="ns2-inner"]父容器選取
第1條新聞標題(//h4[@class="ns2-title"]/a/text())[1]位置 predicate
文字含「香港」的標題//h4[@class="ns2-title"]/a[contains(text(),"香港")]/text()contains() 函數
Setup📦 Cell 1 — 安裝套件
安裝所需的 Python 套件。lxml 用於 XPath 解析,requests 用於發送 HTTP 請求,BeautifulSoup 用於清洗 HTML。
# 安裝所需套件(在 Google Colab 中執行)
!pip install requests lxml beautifulsoup4
Import📚 Cell 2 — 導入套件
導入所有需要的 Python 套件,並設定基本參數。
# 導入所需套件
import requests                          # 發送 HTTP 請求
from lxml import etree                   # XPath 解析引擎
import csv                               # 儲存為 CSV 格式
import time                              # 控制請求間隔
import random                            # 隨機休息時間(防反爬)
from datetime import datetime            # 記錄爬取時間
from google.colab import files           # Google Colab 自動下載

print("✅ 所有套件導入成功")
Config⚙️ Cell 3 — 設定參數
設定爬取目標 URL、請求 Headers(模擬瀏覽器)、爬取頁數等參數。修改這裡的參數即可調整爬取行為。
# ═══════════════════════════════════════════
# 設定參數(可根據需要修改)
# ═══════════════════════════════════════════

# 目標 URL:RTHK 即時新聞列表
BASE_URL = "https://news.rthk.hk/rthk/ch/latest-news.htm"

# 請求 Headers:模擬真實瀏覽器,避免被封鎖
HEADERS = {
    "User-Agent": (
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
        "AppleWebKit/537.36 (KHTML, like Gecko) "
        "Chrome/120.0.0.0 Safari/537.36"
    ),
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Language": "zh-HK,zh;q=0.9,en;q=0.8",
    "Referer": "https://news.rthk.hk/",  # 模擬從 RTHK 首頁跳轉
}

# 請求間隔(秒):隨機休息防止被封
MIN_SLEEP = 1.5   # 最短休息時間
MAX_SLEEP = 3.5   # 最長休息時間

# 輸出 CSV 檔案名稱
OUTPUT_FILE = "rthk_news.csv"

print(f"✅ 設定完成")
print(f"   目標網址:{BASE_URL}")
print(f"   輸出檔案:{OUTPUT_FILE}")
XPath Core🔍 Cell 4 — XPath 解析函數(核心)
這是最重要的部分!使用 lxml 的 XPath 引擎解析 HTML,提取每條新聞的所有字段。每個 XPath 表達式都有詳細注釋說明其邏輯。
def parse_news_list(html_content):
    """
    使用 XPath 解析 RTHK 新聞列表頁面
    
    Args:
        html_content: 頁面的 HTML 字符串
    
    Returns:
        list: 包含所有新聞條目的字典列表
    """
    # 將 HTML 字符串解析為 lxml 可操作的樹狀結構
    # etree.HTML() 自動修復不完整的 HTML 標籤
    tree = etree.HTML(html_content)
    
    # ─── XPath 1:選取所有新聞條目的父容器 ───────────────────────
    # 每條新聞都包裹在 class="ns2-inner" 的 div 中
    # //div 表示在整個文檔中搜尋 div 元素
    # [@class="ns2-inner"] 是 predicate,篩選 class 屬性等於 "ns2-inner" 的元素
    news_items = tree.xpath('//div[@class="ns2-inner"]')
    
    print(f"   找到 {len(news_items)} 條新聞")
    
    results = []
    
    for item in news_items:
        # ─── XPath 2:提取新聞標題 ─────────────────────────────
        # .// 表示從當前節點(item)開始搜尋,而非整個文檔
        # h4[@class="ns2-title"] 選取 class="ns2-title" 的 h4 元素
        # /a 選取其子元素 a 標籤
        # /text() 提取文字內容(注意:返回列表,用 [0] 取第一個)
        title_list = item.xpath('.//h4[@class="ns2-title"]/a/text()')
        title = title_list[0].strip() if title_list else ""
        
        # ─── XPath 3:提取新聞連結 ─────────────────────────────
        # @href 表示提取 href 屬性的值(而非文字內容)
        # 屬性選取用 @ 符號,這是 XPath 的重要語法
        link_list = item.xpath('.//h4[@class="ns2-title"]/a/@href')
        link = link_list[0] if link_list else ""
        # RTHK 連結是相對路徑,需要加上域名
        if link and not link.startswith("http"):
            link = "https://news.rthk.hk" + link
        
        # ─── XPath 4:提取發布時間 ─────────────────────────────
        # div[@class="ns2-created"] 選取時間容器
        # /font 選取其內的 font 標籤(RTHK 用舊式 HTML)
        # /text() 提取時間文字
        time_list = item.xpath('.//div[@class="ns2-created"]/font/text()')
        pub_time = time_list[0].strip() if time_list else ""
        
        # ─── XPath 5:提取新聞類別 ─────────────────────────────
        # div[@class="ns2-category"] 選取類別容器
        # /a/text() 提取類別文字(如「港聞」「財經」)
        cat_list = item.xpath('.//div[@class="ns2-category"]/a/text()')
        category = cat_list[0].strip() if cat_list else ""
        
        # ─── XPath 6:提取文章 ID(從 URL 中提取)──────────────
        # 使用 substring-after() 函數從 URL 中提取文章 ID
        # 例如 /rthk/ch/component/k2/1847547-20260316.htm → 1847547-20260316
        article_id = ""
        if link:
            # Python 方式提取(XPath 也可以用 substring-after())
            parts = link.split("/k2/")
            if len(parts) > 1:
                article_id = parts[1].replace(".htm", "")
        
        # ─── XPath 7:提取摘要/副標題(如有)──────────────────
        # 有些新聞條目有副標題,用 normalize-space() 清除多餘空白
        summary_list = item.xpath('normalize-space(.//div[@class="ns2-summary"])')
        summary = summary_list if isinstance(summary_list, str) else ""
        
        # 記錄爬取時間
        scraped_at = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        
        # 只保留有標題的條目
        if title:
            results.append({
                "article_id": article_id,          # 文章 ID
                "title": title,                     # 新聞標題
                "link": link,                       # 新聞連結
                "category": category,               # 新聞類別
                "publish_time": pub_time,           # 發布時間
                "summary": summary,                 # 摘要
                "scraped_at": scraped_at,           # 爬取時間
            })
    
    return results

print("✅ XPath 解析函數定義完成")
Fetch🌐 Cell 5 — 發送請求並爬取
發送 HTTP 請求獲取 RTHK 新聞頁面,並調用 XPath 解析函數提取數據。包含錯誤處理和防反爬休息機制。
def fetch_rthk_news():
    """
    發送 HTTP 請求獲取 RTHK 新聞列表並解析
    
    Returns:
        list: 所有新聞條目的列表
    """
    print(f"📡 正在請求:{BASE_URL}")
    
    try:
        # 發送 GET 請求
        # timeout=15 設定超時時間為 15 秒
        response = requests.get(BASE_URL, headers=HEADERS, timeout=15)
        
        # 檢查 HTTP 狀態碼
        # raise_for_status() 在狀態碼非 200 時自動拋出異常
        response.raise_for_status()
        
        # 設定正確的編碼(RTHK 使用 UTF-8)
        response.encoding = "utf-8"
        
        print(f"   ✅ 請求成功,狀態碼:{response.status_code}")
        print(f"   📄 頁面大小:{len(response.text):,} 字符")
        
        # 調用 XPath 解析函數
        news_list = parse_news_list(response.text)
        
        # 防反爬:隨機休息
        sleep_time = random.uniform(MIN_SLEEP, MAX_SLEEP)
        print(f"   💤 休息 {sleep_time:.1f} 秒...")
        time.sleep(sleep_time)
        
        return news_list
        
    except requests.exceptions.Timeout:
        print("❌ 請求超時,請檢查網絡連接")
        return []
    except requests.exceptions.HTTPError as e:
        print(f"❌ HTTP 錯誤:{e}")
        return []
    except Exception as e:
        print(f"❌ 未知錯誤:{e}")
        return []


# 執行爬取
print("🚀 開始爬取 RTHK 即時新聞...")
print("=" * 50)
all_news = fetch_rthk_news()
print("=" * 50)
print(f"✅ 共爬取 {len(all_news)} 條新聞")

# 預覽前 3 條
if all_news:
    print("\n📋 預覽前 3 條新聞:")
    for i, news in enumerate(all_news[:3], 1):
        print(f"  {i}. [{news['category']}] {news['title']}")
        print(f"     時間:{news['publish_time']}")
        print(f"     連結:{news['link'][:60]}...")
Export💾 Cell 6 — 儲存 CSV 並自動下載
將爬取的數據儲存為 CSV 檔案,並在 Google Colab 中自動觸發下載。使用 utf-8-sig 編碼確保 Excel 正確顯示中文。
def save_and_download_csv(data, filename):
    """
    將數據儲存為 CSV 並在 Colab 中自動下載
    
    Args:
        data: 新聞數據列表(字典格式)
        filename: 輸出檔案名稱
    """
    if not data:
        print("❌ 沒有數據可以儲存")
        return
    
    # CSV 欄位名稱(與字典的 key 對應)
    fieldnames = [
        "article_id",    # 文章 ID
        "title",         # 新聞標題
        "link",          # 新聞連結
        "category",      # 新聞類別
        "publish_time",  # 發布時間
        "summary",       # 摘要
        "scraped_at",    # 爬取時間
    ]
    
    # 寫入 CSV 檔案
    # encoding="utf-8-sig":加入 BOM 標記,確保 Excel 正確顯示中文
    # newline="" 防止 Windows 上出現多餘空行
    with open(filename, "w", newline="", encoding="utf-8-sig") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        
        # 寫入標題行
        writer.writeheader()
        
        # 逐行寫入數據
        writer.writerows(data)
    
    print(f"✅ 已儲存 {len(data)} 條新聞到 {filename}")
    
    # ─── 在 Google Colab 中自動觸發下載 ───────────────────────
    # files.download() 是 Colab 專屬 API,會彈出下載對話框
    files.download(filename)
    print(f"📥 正在下載 {filename}...")


# 執行儲存和下載
save_and_download_csv(all_news, OUTPUT_FILE)
Analysis📊 Cell 7 — 查看結果統計
用 pandas 查看爬取結果的統計信息,包括各類別新聞數量分佈。
import pandas as pd

# 讀取剛才儲存的 CSV
df = pd.read_csv(OUTPUT_FILE, encoding="utf-8-sig")

print(f"📊 爬取結果統計")
print(f"{'='*40}")
print(f"  總新聞數:{len(df)} 條")
print(f"  欄位:{list(df.columns)}")
print()

# 各類別新聞數量
if "category" in df.columns:
    print("📂 各類別新聞數量:")
    print(df["category"].value_counts().to_string())
    print()

# 顯示前 5 筆數據
print("📋 前 5 條新聞:")
print(df[["title", "category", "publish_time"]].head().to_string(index=False))

🚀 使用步驟

  1. 1. 打開 Google Colab,新建筆記本
  2. 2. 點擊右上角「Copy All Code」,貼入第一個 Cell
  3. 3. 或按順序將每個 Cell 的代碼分別貼入不同 Cell
  4. 4. 從 Cell 1 開始逐格執行(Shift + Enter)
  5. 5. 執行 Cell 6 後,CSV 檔案會自動下載到您的電腦