博客來爬蟲:以中文書暢銷榜為例

import re

import requests
import pandas as pd
from bs4 import BeautifulSoup
from urllib.parse import urljoin


### 設定網址
base_url = "https://www.books.com.tw"

url = (
    "https://www.books.com.tw/web/sys_saletopb/books/"
    "?loc=P_menu_th_1_002"
)

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=15
)

response.raise_for_status()

print("狀態碼:", response.status_code)
print("實際網址:", response.url)
print("HTML 長度:", len(response.text))


### 建立 BeautifulSoup
soup = BeautifulSoup(
    response.text,
    "lxml"
)


### 顯示網頁標題
page_title = soup.select_one("title")

if page_title:
    print(
        "網頁標題:",
        page_title.get_text(" ", strip=True)
    )


### 找到所有商品
book_items = soup.select(
    ".item"
) # 每個 .item 代表一項商品

print("找到的商品區塊數量:", len(book_items))


### 建立書籍資料串列
book_list = []


### 逐筆抓取書籍
for item in book_items:
    item_text = item.get_text(
        " ",
        strip=True
    ) # 取得整個商品區塊的文字


    ### 從商品區塊文字找出排名
    rank_match = re.search(
        r"TOP\s*(\d+)",
        item_text
    )

    if not rank_match:
        continue # 不是排行榜商品就跳過

    rank = int(rank_match.group(1))


    ### 抓取書名與商品網址
    title_element = item.select_one(
        "h4 a"
    )

    if not title_element:
        continue

    title = title_element.get_text(
        " ",
        strip=True
    )

    relative_url = title_element.get(
        "href",
        ""
    )

    book_url = urljoin(
        base_url,
        relative_url
    )


    ### 抓取作者
    author = ""

    author_text_node = item.find(
        string=re.compile(r"作者[::]")
    ) # 尋找包含「作者:」的文字節點

    if author_text_node:
        author_box = author_text_node.parent
        author_links = author_box.select("a")

        if author_links:
            author_list = []

            for author_link in author_links:
                author_name = author_link.get_text(
                    " ",
                    strip=True
                )

                if author_name:
                    author_list.append(author_name)

            author = "、".join(author_list)


    ### 抓取價格原文
    price_text = ""

    price_text_node = item.find(
        string=re.compile(r"優惠價")
    ) # 找到包含「優惠價」的文字節點

    if price_text_node:
        price_box = price_text_node.parent

        price_text = price_box.get_text(
            " ",
            strip=True
        )


    ### 從價格文字取出金額
    price_match = re.search(
        r"(\d[\d,]*)\s*元",
        price_text
    )

    if price_match:
        price = price_match.group(1).replace(
            ",",
            ""
        )

        price = int(price)
    else:
        price = None


    ### 從價格文字取出折扣
    discount_match = re.search(
        r"(\d+)\s*折",
        price_text
    )

    if discount_match:
        discount = discount_match.group(1) + "折"
    else:
        discount = ""


    ### 抓取封面圖片
    image_element = item.select_one("img")

    if image_element:
        image_url = (
            image_element.get("data-src")
            or image_element.get("src")
            or ""
        )

        image_url = urljoin(
            base_url,
            image_url
        )
    else:
        image_url = ""


    ### 加入書籍資料
    book_list.append({
        "排名": rank,
        "書名": title,
        "作者": author,
        "折扣": discount,
        "優惠價": price,
        "價格原文": price_text,
        "商品網址": book_url,
        "封面網址": image_url
    })



### 依照排名排序
book_list.sort(
    key=lambda book: book["排名"]
)


### 顯示書籍資料
print("\n博客來中文書暢銷榜:")

for book in book_list:
    print()
    print("排名:", book["排名"])
    print("書名:", book["書名"])
    print("作者:", book["作者"])
    print("折扣:", book["折扣"])
    print("優惠價:", book["優惠價"])
    print("商品網址:", book["商品網址"])


### 轉換成 DataFrame
book_df = pd.DataFrame(book_list)

print("\n暢銷榜資料表:")
print(book_df)


### 有資料才儲存 CSV
if not book_df.empty:
    book_df.to_csv(
        "博客來中文書暢銷榜.csv",
        index=False,
        encoding="utf-8-sig"
    )

    print("\n資料抓取完成")
    print("共抓到:", len(book_list), "本書")
    print("已產生:博客來中文書暢銷榜.csv")

else:
    print("\n沒有抓到書籍資料")

    with open(
        "博客來_回應內容.html",
        "w",
        encoding="utf-8"
    ) as file:
        file.write(response.text)

    print("已儲存:博客來_回應內容.html")