行政院本院新聞爬蟲:進入詳細頁抓完整內文
import time
import requests
import pandas as pd
from bs4 import BeautifulSoup
from urllib.parse import urljoin
### 設定網址
base_url = "https://www.ey.gov.tw" # 行政院網站的主網址
list_url = "https://www.ey.gov.tw/Page/6485009ABEC1CB9C" # 行政院新聞列表頁網址
headers = {
"User-Agent": "Mozilla/5.0"
} # 模擬一般瀏覽器發送請求,避免部分網站拒絕程式存取
### 建立 Session
session = requests.Session() # 建立 Session,讓後續多次 HTTP 請求可以共用連線
session.headers.update(headers) # 將 User-Agent 設定到 Session,之後每次請求都會自動帶入
### 抓取新聞詳細頁內文
def get_news_content(news_url):
try:
response = session.get(
news_url,
timeout=10
) # 進入新聞詳細頁,最多等待 10 秒
response.raise_for_status() # 如果 HTTP 狀態碼為 4xx 或 5xx,直接拋出例外
soup = BeautifulSoup(
response.text,
"lxml"
) # 將新聞詳細頁的 HTML 原始碼交給 BeautifulSoup 解析
content_element = soup.select_one(
".words_content .data_left"
) # 使用 CSS Selector 找到新聞主要內文區域
if not content_element:
return "" # 如果找不到新聞內文區域,回傳空字串
paragraphs = content_element.select("p") # 找到新聞內文區域中的所有 <p> 段落
content_list = []
for paragraph in paragraphs:
text = paragraph.get_text(
" ",
strip=True
) # 取得段落文字,並移除前後及多餘的空白
if text:
content_list.append(text) # 排除沒有文字的空白段落
content = "\n\n".join(content_list) # 將所有段落合併,每個段落之間空一行
return content # 回傳整理完成的新聞完整內文
except requests.exceptions.RequestException as error:
print("詳細頁抓取失敗:", news_url)
print("錯誤訊息:", error)
return "" # 如果請求發生錯誤,回傳空字串,避免整個程式中斷
### 抓取新聞列表頁
response = session.get(
list_url,
timeout=10
) # 向行政院新聞列表頁發送 GET 請求
response.raise_for_status() # 如果 HTTP 狀態碼為 4xx 或 5xx,直接拋出例外
print("列表頁狀態碼:", response.status_code)
### 建立 BeautifulSoup
soup = BeautifulSoup(
response.text,
"lxml"
) # 將新聞列表頁的 HTML 原始碼交給 BeautifulSoup 解析
### 找到所有新聞項目
news_items = soup.select(
"ul.list-group-item > li"
) # 找到新聞列表中所有第一層的 <li> 新聞項目
print("本頁新聞數量:", len(news_items))
### 抓取新聞資料
news_list = []
for index, item in enumerate(news_items, start=1):
title_element = item.select_one(".title") # 找到新聞標題
date_element = item.select_one(".date") # 找到新聞日期
summary_element = item.select_one("p") # 找到新聞摘要
link_element = item.select_one("a") # 找到新聞詳細頁連結
if title_element and date_element and link_element:
title = title_element.get_text(
" ",
strip=True
) # 取得新聞標題文字
date = date_element.get_text(
strip=True
) # 取得新聞發布日期
if summary_element:
summary = summary_element.get_text(
" ",
strip=True
) # 如果有摘要,就取得摘要文字
else:
summary = "" # 如果沒有摘要,就使用空字串
relative_url = link_element.get("href") # 取得 <a> 標籤中的 href 網址
news_url = urljoin(
base_url,
relative_url
) # 將相對網址與網站主網址組合成完整網址
print(f"\n正在抓取第 {index} 則新聞")
print("標題:", title)
print("網址:", news_url)
content = get_news_content(
news_url
) # 呼叫函式進入新聞詳細頁,抓取完整新聞內文
news_list.append({
"標題": title,
"日期": date,
"摘要": summary,
"完整內文": content,
"網址": news_url
}) # 將這一則新聞整理成字典後加入 news_list
time.sleep(1) # 每抓完一則新聞暫停 1 秒,避免短時間內發送太多請求
### 轉換成 DataFrame
news_df = pd.DataFrame(news_list) # 將新聞資料列表轉換成 pandas DataFrame
### 顯示抓取結果
print("\n新聞資料:")
for index, news in enumerate(news_list, start=1):
print(f"\n第 {index} 則新聞")
print("標題:", news["標題"])
print("日期:", news["日期"])
print("完整內文:")
print(news["完整內文"])
print("網址:", news["網址"])
### 儲存成 CSV
news_df.to_csv(
"行政院新聞完整資料.csv",
index=False,
encoding="utf-8-sig"
) # 儲存成 CSV,使用 utf-8-sig 避免 Excel 開啟中文時出現亂碼
### 執行完成
print("\n資料抓取完成")
print("共抓到:", len(news_list), "則新聞")
print("已產生:行政院新聞完整資料.csv")