Python

爬蟲入門:讀懂 robots.txt,用 BeautifulSoup 合規抓資料

網頁爬蟲要先讀 robots.txt 確認合規,再用 requests 抓頁面、BeautifulSoup 搭配 CSS 選擇器解析出你要的資料。真正能用的爬蟲還要加逾時設定、例外處理與禮貌的請求間隔,才不會把伺服器擠爆或被封鎖,抓到的資料最後存成 CSV 或 SQLite。
爬蟲入門:讀懂 robots.txt,用 BeautifulSoup 合規抓資料

爬蟲入門:讀懂 robots.txt,用 BeautifulSoup 合規抓資料

很多有用的資料網站都沒有開放 API,競品分析、價格監控、新聞彙整常常得靠爬蟲自己動手抓。但 AI 給的爬蟲程式碼常常「能跑一次」就沒了,網站結構一改就壞、被反爬蟲機制擋下來,你需要看得懂、改得動。這一課從最容易被忽略的合規檢查開始,一路教到 BeautifulSoup 解析、CSS 選擇器定位、反爬蟲應對,最後把資料存成可用的格式。

動手之前先想清楚三件事:可以爬嗎(robots.txt 有沒有禁止、服務條款允不允許)、有沒有更簡單的方法(官方 API、公開資料集)、頻率合理嗎(不要讓伺服器過載)。這三個問題會貫穿整堂課。

你將學到什麼

先讀 robots.txt

抓資料前先確認網站允不允許爬,這是爬蟲的合規底線。

requests + BeautifulSoup

用 requests 抓頁面,BeautifulSoup 把 HTML 轉成可以操作的物件。

CSS 選擇器精準定位

用瀏覽器複製 CSS Selector,直接貼進程式碼就能用。

反爬蟲機制怎麼應對

User-Agent、頻率限制、JavaScript 動態載入各有合理的應對方式。

存成可用的格式

少量資料存 CSV,資料量大或需要查詢就用 SQLite。

三道遞進題自我檢驗

讀懂、改寫、抓錯,練到能獨立寫出生產等級的爬蟲。

爬之前先讀 robots.txt

robots.txt 是網站對爬蟲的使用說明書,放在網站根目錄,告訴你哪些路徑可以爬、哪些不行。這是爬蟲的倫理底線,也是法律風險的第一道防護,動手寫程式之前一定要先看過。

檢查 robots.txt這個路徑允許爬嗎?允許不允許發送請求requests + BeautifulSoup 解析不要抓取換個合規的資料來源爬之前先問一句:這個路徑允許爬嗎?
動手爬之前先分岔:robots.txt 允許才發送請求,不允許就换來源。
# https://example.com/robots.txt

User-agent: *         # 對所有爬蟲
Disallow: /admin/     # 不能爬 /admin/ 路徑
Disallow: /private/   # 不能爬 /private/ 路徑
Allow: /public/       # 允許爬 /public/ 路徑
Crawl-delay: 2        # 請求間隔至少 2 秒

User-agent: Googlebot # 只對 Google 爬蟲
Allow: /              # 允許爬全站

用 Python 讀 robots.txt 不用自己解析文字,標準函式庫就有現成工具:

from urllib.robotparser import RobotFileParser

rp = RobotFileParser()
rp.set_url("https://example.com/robots.txt")
rp.read()

can_fetch = rp.can_fetch("*", "https://example.com/products")
print(f"可以爬 /products:{can_fetch}")  # True 或 False
即使 robots.txt 允許,還要確認

① 服務條款是否禁止自動化存取。② 爬取的資料是否涉及個資(個資法)。③ 是否會對伺服器造成過度負載,請求頻率要合理。

BeautifulSoup:把 HTML 變成 Python 物件

BeautifulSoup(pip install beautifulsoup4)把 HTML 字串解析成可以用 Python 操作的樹狀結構,搭配 requests 就是完整的爬蟲工具鏈。

import requests, time
from bs4 import BeautifulSoup

headers = {"User-Agent": "Mozilla/5.0 學術研究用途"}

def fetch_page(url: str) -> BeautifulSoup:
    """取得並解析單一頁面。"""
    r = requests.get(url, headers=headers, timeout=10)
    r.raise_for_status()
    return BeautifulSoup(r.text, "html.parser")

soup = fetch_page("https://example.com/products")

# 選取元素
title = soup.find("h1")                     # 第一個 h1
items = soup.find_all("div", class_="item") # 所有 class="item" 的 div
link  = soup.find("a", href=True)           # 第一個有 href 屬性的 a 標籤

# 取出內容
print(title.get_text(strip=True))  # 純文字
print(link["href"])                # 屬性值

# 批次爬取時加間隔,尊重伺服器
time.sleep(2)                      # 兩秒間隔

CSS 選擇器:精準定位元素

BeautifulSoup 支援 CSS 選擇器語法(.select()),和瀏覽器開發者工具的「複製 selector」功能直接對接,是最快速定位元素的方法。

# .select() 回傳 list;.select_one() 回傳第一個或 None

# 標籤
soup.select("h2")              # 所有 h2

# class
soup.select(".product-name")   # class="product-name"

# id
soup.select("#main-title")     # id="main-title"

# 後代(空格)
soup.select("div.card h3")     # div.card 裡面的 h3

# 屬性
soup.select("a[href]")             # 有 href 屬性的 a
soup.select('a[href^="https"]')    # href 開頭是 https 的 a

# 組合範例:抓商品清單
products = soup.select("ul.product-list li")
for p in products:
    name  = p.select_one(".name")
    price = p.select_one(".price")
    if name and price:
        print(name.get_text(strip=True), price.get_text(strip=True))
小技巧:用瀏覽器複製 CSS Selector

在 Chrome 開發者工具裡,對目標元素按右鍵,選 Copy 再選 Copy selector,貼到 .select_one() 裡就能直接用,省去猜 HTML 結構的時間。

反爬蟲機制與常見應對

現代網站通常有反爬蟲機制。了解這些機制,是為了讓你的爬蟲更禮貌、更穩定,不是為了繞過限制去爬不該爬的資料

反爬蟲機制症狀合理應對
User-Agent 封鎖回傳 403 或空頁面設置合理的 User-Agent 並說明用途
頻率限制429 Too Many Requests加請求間隔,秒數落在 2 到 5 秒之間
IP 封鎖連線拒絕或逾時降低頻率,或確認是否有官方 API
JavaScript 動態載入HTML 裡看不到資料用 Playwright 或 Selenium,或找 XHR API
需要登入回傳登入頁面使用 requests.Session 帶 cookie
CAPTCHA出現驗證圖片考慮是否有官方授權或 API 替代
遇到 JavaScript 動態載入

用 Chrome 開發者工具的 Network 分頁找 XHR,看網站是不是直接呼叫 API 取資料。如果有,直接用 requests 呼叫那個 API,比用 Playwright 模擬瀏覽器簡單十倍。

整合:爬完存到哪裡

爬蟲的最後一步是把資料存成可用的格式。根據資料量和用途選擇:少量存 CSV 或 JSON;大量或需要查詢就用 SQLite。

import requests, time, csv
from bs4 import BeautifulSoup
from pathlib import Path

def scrape_products(base_url: str, pages: int = 3) -> list:
    """爬取商品清單,回傳 list of dict。"""
    results = []
    headers = {"User-Agent": "Mozilla/5.0 學術研究"}

    for page in range(1, pages + 1):
        url = f"{base_url}?page={page}"
        try:
            r = requests.get(url, headers=headers, timeout=10)
            r.raise_for_status()
            soup = BeautifulSoup(r.text, "html.parser")
            for item in soup.select(".product-card"):
                name  = item.select_one(".name")
                price = item.select_one(".price")
                results.append({
                    "name":  name.get_text(strip=True) if name  else "",
                    "price": price.get_text(strip=True) if price else "",
                    "page":  page
                })
        except requests.exceptions.RequestException as e:
            print(f"第 {page} 頁失敗:{e}")
        time.sleep(2)   # 禮貌間隔
    return results

def save_csv(data: list, path: str) -> None:
    """存成 UTF-8 CSV。"""
    if not data:
        return
    with open(path, "w", newline="", encoding="utf-8-sig") as f:
        writer = csv.DictWriter(f, fieldnames=data[0].keys())
        writer.writeheader()
        writer.writerows(data)

三道遞進題:讀懂、改寫、抓錯

三道題各練一種能力:讀懂是追蹤 BeautifulSoup 怎麼從 HTML 抓出資料;改寫是把不安全的爬蟲升級到生產標準;抓錯是找出合規、效能、可靠性上的問題。動手前先自己想過一輪,再對答案。

題目一:讀懂(基礎)

說明以下爬蟲程式每個步驟的作用:

import requests
from bs4 import BeautifulSoup

r = requests.get("https://news.example.com", timeout=8)
r.raise_for_status()
soup = BeautifulSoup(r.text, "html.parser")

headlines = soup.select("article.news-item h2 a")
results = [
    {"title": h.get_text(strip=True), "url": h.get("href", "")}
    for h in headlines
]
print(f"共取得 {len(results)} 篇新聞")

任務:說明 "html.parser" 的意思、.select() 選取的元素結構,以及 h.get("href","")h["href"] 的差異。

答案拆解

"html.parser" 是 Python 內建的 HTML 解析器,不需要額外安裝;"lxml" 速度更快但需要另外安裝。

select("article.news-item h2 a") 選取所有 article.news-item 裡面 h2 裡面的 a 標籤,是三層後代選擇。

h.get("href","") 在屬性不存在時回傳空字串不報錯;h["href"] 屬性不存在時會拋出 KeyError,爬蟲要用 .get() 比較安全。

題目二:改寫(進階)

從「能爬」到「能用」的差距

AI 給的初版爬蟲通常能跑一次,但不穩定:沒有錯誤處理、沒有間隔、不儲存資料。真正可用的爬蟲需要禮貌頻率、例外處理、結構化儲存,以及遇到失敗還能繼續的能力。

請 AI 把以下爬蟲升級到生產等級:

import requests
from bs4 import BeautifulSoup

for page in range(1, 11):
    r = requests.get(f"https://shop.example.com/p{page}")
    soup = BeautifulSoup(r.text, "html.parser")
    items = soup.select(".item")
    print(items)
給 AI 的提示詞

「請升級這段爬蟲:① 加 timeout=10 和 raise_for_status;② 每頁之間加 time.sleep(2);③ 加 try/except 捕捉 RequestException,失敗時記錄並繼續;④ 把結果存成 UTF-8 CSV;⑤ 加 User-Agent header 說明用途;⑥ 加 docstring 和中文註解。」

答案拆解

① timeout 加 raise_for_status 能防止程式卡住,或默默吞掉錯誤回應。

time.sleep(2) 加在每次請求後,對伺服器表示禮貌,也降低被封鎖的機率。

③ try/except 搭配 continue,讓單頁失敗不中斷整個爬取,失敗的頁碼記錄下來事後補爬。

題目三:抓錯(高階)

爬蟲的問題分三層:合規問題(爬得對不對)、可靠性問題(爬不爬得到)、資料品質問題(爬到的對不對)。找出以下爬蟲的所有問題:

import requests
from bs4 import BeautifulSoup

# 問題一:完全沒有檢查 robots.txt
urls = [f"https://target.com/user/{i}" for i in range(1, 10001)]

for url in urls:          # 問題二
    r = requests.get(url) # 問題三、四
    soup = BeautifulSoup(r.text, "html.parser")
    name  = soup.find("span", class_="username").text
    email = soup.find("span", class_="email").text  # 問題五
    print(name, email)    # 問題六
六個問題逐一拆解

問題一:沒有檢查 robots.txt,可能違反網站使用規範,爬用戶資料也涉及個資法。

問題二:一萬筆不間斷請求,可能對伺服器造成類似 DDoS 等級的負載,觸法或被永久封鎖。

問題三:沒有 timeout,任一請求卡住就讓整個程式停住。

問題四:沒有 raise_for_status 和 try/except,任一頁失敗程式就崩潰。

問題五:.text 在元素不存在時(find 回傳 None)會拋出 AttributeError,改用 x.get_text(strip=True)if x else "" 比較安全。

問題六:爬取並印出用戶 email 屬於個人資料,蒐集前需符合個資法的目的告知義務。

重點整理與完成清單

步驟做什麼工具
合規確認讀 robots.txt、服務條款urllib.robotparser
取得頁面發送 HTTP 請求requests
解析 HTML找到目標元素BeautifulSoup
提取資料取出文字和屬性.get_text()、.get()
禮貌間隔避免過度請求time.sleep(2 到 5 秒)
存儲結果CSV 或 SQLitecsv、sqlite3
動手前的爬蟲三問

可以爬嗎(robots.txt 和條款)?有沒有 timeout 和例外處理?有沒有加 sleep 和 User-Agent?三個問題都想清楚了再按下執行。

  • 爬之前先讀 robots.txt,確認合規
  • 能用 requests 搭配 BeautifulSoup 取得並解析 HTML
  • 能用 CSS 選擇器精準定位元素,配合瀏覽器複製 Selector
  • 知道 .get_text(strip=True)和 .get(attr,"")的安全取值方式
  • 每次請求加 timeout、raise_for_status、try/except、time.sleep
  • 完成題目一:說明 html.parser、select、.get()和 [] 的差異
  • 完成題目二:請 AI 把初版爬蟲升級到生產等級
  • 完成題目三:找出合規、可靠性、個資三層問題

延伸學習

把這篇文章分享給需要的人FacebookLINEThreadsX

常見問答

爬蟲一定要先看 robots.txt 嗎?
是的。robots.txt 是網站對爬蟲的使用說明書,說明哪些路徑可以爬、哪些不行,這是爬蟲的倫理底線也是法律風險的第一道防護。即使 robots.txt 允許,還要確認服務條款、個資法,以及請求頻率會不會讓伺服器過載。
requests 和 BeautifulSoup 差在哪?
requests 負責發送 HTTP 請求、把網頁抓下來;BeautifulSoup 負責把抓到的 HTML 字串解析成可以用 Python 操作的樹狀結構,兩者合起來才是完整的爬蟲工具鏈。
.find()和 .select()差在哪?
.find()是 BeautifulSoup 原生語法,靠標籤名稱和屬性找元素;.select()用的是 CSS 選擇器語法,可以直接跟瀏覽器開發者工具「複製 selector」的結果對接,通常是最快定位元素的方式。
遇到 JavaScript 動態載入的網站怎麼辦?
先用瀏覽器開發者工具的 Network 分頁找 XHR 請求,看網站是不是直接呼叫某個 API 取資料,找到的話用 requests 呼叫那個 API 最簡單。真的找不到再考慮 Playwright 或 Selenium 這類會控制瀏覽器的工具。
爬到的資料要存在哪裡?
看資料量和用途:少量資料存成 CSV 或 JSON 就夠用;資料量大或之後需要查詢、篩選,建議存進 SQLite 這類資料庫。