一、認識網頁分頁的運作方式
在很多網站中,當資料量太大時,通常不會一次顯示所有內容,而是將資料拆分成多個頁面。
例如:
第 1 頁:書籍 1~20
第 2 頁:書籍 21~40
第 3 頁:書籍 41~60
如果想要取得所有資料,就必須讓程式依序訪問這些頁面。
Books to Scrape 的分頁網址有一個特點:每一頁的網址都會包含頁碼。
例如:
第一頁:
第二頁:
https://books.toscrape.com/catalogue/page-2.html
第三頁:
https://books.toscrape.com/catalogue/page-3.html
從網址可以發現,第一頁沒有直接使用 page-1.html,而是使用網站首頁;第二頁開始才使用 page-2.html、page-3.html 這樣的格式。
因此,在設計爬蟲時,必須先了解網站的分頁規則,才能正確組合每一頁的網址。
二、實作一:先確認第二頁的網址是否能正常取得
在開始撰寫多頁面爬蟲之前,先確認第二頁的網址可以正常存取。
import requests
url = "https://books.toscrape.com/catalogue/page-2.html"
response = requests.get(url, timeout=10)
print("狀態碼:", response.status_code)
print(response.url)
這裡使用 requests.get() 發送 GET 請求,並透過 status_code 確認伺服器是否成功回傳資料。
如果狀態碼是 200,代表請求成功。
接著可以使用 BeautifulSoup 解析第二頁的 HTML。
from bs4 import BeautifulSoup
soup = BeautifulSoup(response.text, "html.parser")
books = soup.select("article.product_pod")
print("第二頁書籍數量:", len(books))
如果能成功取得書籍區塊,就代表第二頁也可以使用昨天學過的方法進行解析。
這個步驟很重要,因為在正式進行多頁面爬蟲之前,先確認網址規則和 HTML 結構,可以避免後續出現大量錯誤。
三、實作二:使用 for 迴圈自動切換頁面
前面已經確認第二頁的網址規則,接下來就可以利用 for 迴圈自動產生不同頁面的網址。
這次先設定抓取前 5 頁。
for page in range(1, 6):
if page == 1:
url = "https://books.toscrape.com/"
else:
url = f"https://books.toscrape.com/catalogue/page-{page}.html"
print("目前抓取頁面:", url)
這段程式碼會依序產生第一頁到第五頁的網址。
這裡使用 range(1, 6),是因為 Python 的 range() 不包含結束值,因此實際產生的數字是 1、2、3、4、5。
另外,第一頁和其他頁面的網址格式不同,所以使用 if...else 進行判斷。
這樣就能自動產生不同頁面的網址,不需要手動修改每一次的 URL。
四、實作三:建立多頁面爬蟲,抓取所有書籍資料
接下來要將昨天的書籍資料擷取程式與今天的分頁邏輯整合起來。
這次會抓取前 5 頁的書籍資料,並將所有資料存放在同一個 List 中。
import requests
from bs4 import BeautifulSoup
books_data = []
rating_map = {
"One": 1,
"Two": 2,
"Three": 3,
"Four": 4,
"Five": 5
}
for page in range(1, 6):
if page == 1:
url = "https://books.toscrape.com/"
else:
url = f"https://books.toscrape.com/catalogue/page-{page}.html"
print("正在抓取第", page, "頁")
response = requests.get(url, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
books = soup.select("article.product_pod")
for book in books:
title = book.select_one("h3 a")["title"]
price_text = book.select_one(".price_color").text
price = float(price_text.replace("£", ""))
availability = book.select_one(".availability").text.strip()
rating_text = book.select_one(".star-rating")["class"][1]
rating = rating_map[rating_text]
book_info = {
"title": title,
"price": price,
"availability": availability,
"rating": rating,
"page": page
}
books_data.append(book_info)
print("全部抓取完成!")
print("總共取得", len(books_data), "筆書籍資料")
程式碼解說
這段程式碼可以分成三個部分。
第一部分:切換頁面
透過 for page in range(1, 6) 依序取得第一頁到第五頁的網址。
第二部分:解析 HTML
使用 Requests 取得網頁內容,再透過 BeautifulSoup 找出每一頁的書籍區塊。
第三部分:整理資料
使用內層的 for 迴圈逐本處理書籍,將名稱、價格、庫存和評分整理成 Dictionary,再加入 books_data。
這裡額外增加了一個 page 欄位,用來記錄每本書來自哪一頁。
這樣做的好處是,後續如果發現某筆資料有問題,就能知道它是從哪個頁面抓取的。
另外,response.raise_for_status() 可以在 HTTP 請求失敗時拋出例外,避免程式在取得錯誤頁面後繼續執行。
五、實作四:確認多頁面資料是否正確
完成多頁面爬蟲後,接下來要確認資料是否真的成功整合。
首先,查看總筆數。
print("總資料筆數:", len(books_data))
如果網站每頁有 20 本書,前 5 頁正常情況下應該會取得 100 筆資料。
接著,查看前 5 筆資料。
for book in books_data[:5]:
print(book)
也可以查看最後 5 筆資料。
for book in books_data[-5:]:
print(book)
這樣可以確認資料不只來自第一頁,而是已經成功累積多個頁面的內容。
最後,統計每一頁抓取到的資料筆數。
for page in range(1, 6):
page_books = [
book for book in books_data
if book["page"] == page
]
print("第", page, "頁:", len(page_books), "本書")
透過這個方式,可以確認每一頁是否都有成功取得資料。
六、實作五:分析多頁面書籍資料
完成多頁面資料擷取後,就可以利用昨天學過的分析方式,對更多資料進行整理。
average_price = total_price / len(books_data)
print("平均價格:", round(average_price, 2))
這段程式碼會加總所有書籍的價格,再除以總筆數,計算出平均價格。
print("價格最高的書籍:", most_expensive["title"])
print("價格:", most_expensive["price"])
這裡使用 max() 找出價格最高的書籍,與昨天使用的 min() 概念相同,只是比較方向不同。
print("五星書籍數量:", len(five_star_books))
for book in five_star_books[:10]:
print(book["title"], book["price"])
這段程式碼會找出所有評分為 5 的書籍,並顯示前 10 筆結果。
透過多頁面爬蟲,我們就能取得比單頁更多的資料,進一步進行篩選和分析。
七、實作六:將多頁面資料儲存成 CSV
當資料量增加後,將資料儲存成 CSV 就更加重要。
這次會將前 5 頁的書籍資料全部寫入同一個 CSV 檔案。
import csv
with open("books_all_pages.csv", "w", newline="", encoding="utf-8-sig") as file:
fieldnames = [
"title",
"price",
"availability",
"rating",
"page"
]
writer = csv.DictWriter(file, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(books_data)
print("多頁面資料已經儲存完成")
這裡使用 csv.DictWriter() 將 List 中的 Dictionary 寫入 CSV。
由於資料已經整合在同一個 List 裡,因此不需要為每一頁建立不同的檔案,只要在最後統一寫入即可。
在 Google Colab 中,可以透過以下程式碼下載檔案:
from google.colab import files
files.download("books_all_pages.csv")
這樣就能將多頁面爬取的資料下載到自己的電腦,之後也可以使用 Excel 或 Pandas 進行分析。
八、實作七:加入錯誤處理,避免爬蟲中斷
實際執行爬蟲時,可能會遇到網路連線失敗、伺服器回傳錯誤狀態碼,或是網頁結構發生變化等情況。
如果沒有處理這些問題,程式可能會在某一頁發生錯誤後直接停止。
因此,可以使用 try...except 來處理請求錯誤。
import requests
from bs4 import BeautifulSoup
url = "https://books.toscrape.com/catalogue/page-2.html"
try:
response = requests.get(url, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
books = soup.select("article.product_pod")
print("成功取得", len(books), "本書籍")
except requests.exceptions.RequestException as e:
print("網頁請求失敗:", e)
這裡使用 try 執行可能發生錯誤的程式碼。
如果請求失敗,或伺服器回傳錯誤狀態碼,程式就會進入 except 區塊,顯示錯誤訊息,而不是直接中斷整個 Notebook。
不過,錯誤處理並不代表所有問題都能自動解決。如果網站的 HTML 結構改變,仍然需要重新檢查 CSS Selector 是否正確。