網頁爬蟲程式
## 一、網頁爬蟲的基本概念
網頁爬蟲(Web Crawler / Web Scraper)是一種自動化程式,通常依照以下流程取得網頁資料:
1. **發送 HTTP 請求**:向指定網址要求網頁內容。
2. **接收 HTML**:伺服器回傳網頁原始碼。
3. **解析 HTML**:找出需要的元素,例如標題、作者、日期或超連結。
4. **整理資料**:將資料顯示、儲存成文字、CSV 或資料庫。
5. **控制請求頻率**:避免過度頻繁存取網站,造成伺服器負擔。
本例中:
- `urllib.request`:Python 內建模組,用來發送 HTTP 請求與取得網頁內容。
- `BeautifulSoup`:第三方 HTML 解析工具,用來從 HTML 中搜尋指定標籤與資料。
---
## 二、安裝 BeautifulSoup
`urllib.request` 已經包含在 Python 標準函式庫中,不需另外安裝;BeautifulSoup 則需安裝:
```bash
pip install beautifulsoup4
```
程式中通常使用以下方式匯入:
```python
from urllib.request import Request, urlopen
from bs4 import BeautifulSoup
```
---
## 三、PTT 棒球版網頁結構
PTT 棒球版網址為:
```text
https://www.ptt.cc/bbs/baseball/index.html
```
文章列表大致使用以下 HTML 結構:
```html
<div class="r-ent">
<div class="title">
<a href="/bbs/baseball/M.1234567890.A.123.html">
文章標題
</a>
</div>
</div>
```
因此可以:
- 先選取所有 `div.r-ent`
- 再從每個項目中找出 `div.title a`
- 取得超連結中的文字作為文章標題
---
## 四、使用 `urllib.request` 與 BeautifulSoup 爬取標題
```python
from urllib.request import Request, urlopen
from bs4 import BeautifulSoup
url = "https://www.ptt.cc/bbs/baseball/index.html"
# 建立 HTTP 請求
request = Request(
url,
headers={
"User-Agent": "Mozilla/5.0",
# PTT 部分頁面需要此 Cookie 表示已同意瀏覽十八禁內容
"Cookie": "over18=1"
}
)
try:
# 取得網頁內容
with urlopen(request, timeout=10) as response:
html = response.read()
# PTT 使用 UTF-8 編碼
soup = BeautifulSoup(html, "html.parser")
# 找出所有文章項目
articles = soup.select("div.r-ent")
for article in articles:
title_tag = article.select_one("div.title a")
# 被刪除的文章可能沒有 a 標籤,因此要先判斷
if title_tag:
title = title_tag.get_text(strip=True)
link = title_tag.get("href")
print("標題:", title)
print("網址:", "https://www.ptt.cc" + link)
print("-" * 50)
except Exception as error:
print("讀取網頁時發生錯誤:", error)
```
### 程式說明
#### 1. 建立請求
```python
request = Request(
url,
headers={
"User-Agent": "Mozilla/5.0",
"Cookie": "over18=1"
}
)
```
`User-Agent` 用來告訴伺服器目前的用戶端類型。有些網站若沒有設定 `User-Agent`,可能會拒絕請求。
PTT 某些版面會要求使用者確認十八歲以上,透過:
```text
Cookie: over18=1
```
可以模擬已完成確認的瀏覽器。
#### 2. 取得 HTML
```python
with urlopen(request, timeout=10) as response:
html = response.read()
```
`urlopen()` 會向網址發送請求,`read()` 則讀取伺服器回傳的 HTML 原始資料。
#### 3. 建立 BeautifulSoup 物件
```python
soup = BeautifulSoup(html, "html.parser")
```
這會將 HTML 轉換成可搜尋的結構,之後可以使用 CSS Selector 或其他方法尋找元素。
#### 4. 找出文章標題
```python
articles = soup.select("div.r-ent")
```
表示找出所有類別為 `r-ent` 的 `div` 元素。
接著:
```python
title_tag = article.select_one("div.title a")
```
在每一篇文章中尋找標題超連結。
```python
title = title_tag.get_text(strip=True)
```
取得標籤中的文字,並移除前後空白。
---
## 五、只列印標題的簡化版本
如果只需要標題,可以寫得更簡潔:
```python
from urllib.request import Request, urlopen
from bs4 import BeautifulSoup
url = "https://www.ptt.cc/bbs/baseball/index.html"
request = Request(
url,
headers={
"User-Agent": "Mozilla/5.0",
"Cookie": "over18=1"
}
)
with urlopen(request) as response:
soup = BeautifulSoup(response.read(), "html.parser")
for title_tag in soup.select("div.r-ent div.title a"):
print(title_tag.get_text(strip=True))
```
這裡的:
```python
soup.select("div.r-ent div.title a")
```
表示尋找位於:
```text
div.r-ent
└── div.title
└── a
```
中的所有超連結。
---
## 六、處理編碼問題
PTT 頁面通常使用 UTF-8。如果要明確指定編碼,可以使用:
```python
html = response.read().decode("utf-8", errors="ignore")
soup = BeautifulSoup(html, "html.parser")
```
完整範例如下:
```python
with urlopen(request, timeout=10) as response:
html = response.read().decode("utf-8", errors="ignore")
soup = BeautifulSoup(html, "html.parser")
```
`errors="ignore"` 可以避免少數無法解碼的字元使程式中斷,但也可能忽略部分異常字元。
---
## 七、取得下一頁資料
PTT 頁面通常會有「‹ 上頁」連結,可以找出上一頁網址:
```python
prev_link = soup.select_one("div.btn-group-paging a.btn.wide")
if prev_link:
previous_url = "https://www.ptt.cc" + prev_link["href"]
print(previous_url)
```
例如,將解析功能包裝成函式:
```python
from urllib.request import Request, urlopen
from bs4 import BeautifulSoup
BASE_URL = "https://www.ptt.cc"
def get_page(url):
request = Request(
url,
headers={
"User-Agent": "Mozilla/5.0",
"Cookie": "over18=1"
}
)
with urlopen(request, timeout=10) as response:
html = response.read()
return BeautifulSoup(html, "html.parser")
url = "https://www.ptt.cc/bbs/baseball/index.html"
soup = get_page(url)
for article in soup.select("div.r-ent"):
title_tag = article.select_one("div.title a")
if title_tag:
print(title_tag.get_text(strip=True))
# 取得上一頁連結
previous = soup.select_one("div.btn-group-paging a.btn.wide")
if previous:
print("上一頁:", BASE_URL + previous["href"])
```
---
## 八、使用爬蟲時的注意事項
1. **不要過於頻繁發送請求**,可以在請求之間加入延遲:
```python
import time
time.sleep(2)
```
2. **遵守網站規範與服務條款**,不要大量下載或造成網站負擔。
3. **妥善處理錯誤**,例如網路中斷、網址不存在或伺服器拒絕請求。
4. **尊重個人資料與著作權**,爬取的資料不應任意散布或用於不當用途。
5. **網站 HTML 結構可能改變**,因此 CSS Selector 可能需要隨網站結構調整。
總結來說,`urllib.request` 負責「取得網頁」,而 BeautifulSoup 負責「解析網頁」。兩者搭配後,就能從 PTT 棒球版的 HTML 中找出文章標題及其連結。
相關學習地圖、教學課程
Python 資料工程
Python 後端工程、資料庫