我的第1個爬蟲程序——豆瓣Top250爬蟲的詳細步驟指南
一、創建隔離開發環境
1. 使用虛擬環境(推薦venv
)
# 在項目目錄打開終端執行
python -m venv douban_env # 創建虛擬環境
source douban_env/bin/activate # Linux/macOS激活
douban_env\Scripts\activate # Windows激活
2. 安裝依賴庫
pip install requests beautifulsoup4 lxml
3. 生成依賴清單
pip freeze > requirements.txt
二、項目架構設計
douban_top250/
├── config/ # 配置文件
│ └── settings.py
├── core/ # 核心邏輯
│ ├── spider.py
│ └── storage.py
├── utils/ # 工具函數
│ └── helper.py
├── output/ # 輸出目錄
├── main.py # 主入口
└── requirements.txt # 依賴清單
三、分步實現
步驟1:創建配置文件 config/settings.py
# 請求配置
HEADERS = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36','Referer': 'https://movie.douban.com/'
}# 目標URL配置
BASE_URL = 'https://movie.douban.com/top250'# 存儲配置
OUTPUT_DIR = './output'
CSV_HEADERS = ['標題', '評分', '年份', '國家', '類型', '鏈接']# 容錯配置
SAFE_MODE = True # 遇到錯誤時跳過條目而不是終止
UNKNOWN_PLACEHOLDER = "未知" # 數據缺失時的占位符
步驟2:編寫工具類 utils/helper.py
import random
import timedef random_delay(min=1, max=3):"""隨機延遲防止被封"""time.sleep(random.uniform(min, max))def make_soup(html):"""創建BeautifulSoup對象"""from bs4 import BeautifulSoupreturn BeautifulSoup(html, 'lxml')
步驟3:核心爬蟲邏輯 core/spider.py
import requests
from config import settings
from utils.helper import random_delay, make_soupclass DoubanSpider:def __init__(self):self.session = requests.Session()self.session.headers.update(settings.HEADERS)def fetch_page(self, url):"""獲取頁面內容"""try:random_delay()response = self.session.get(url)response.raise_for_status() # 自動處理HTTP錯誤return response.textexcept requests.RequestException as e:print(f"請求失敗: {str(e)}")return Nonedef parse_page(self, html):"""改進后的解析方法"""soup = make_soup(html)movies = []for item in soup.find_all('div', class_='item'):try:# 標題與鏈接title = item.find('span', class_='title').text.strip()rating = item.find('span', class_='rating_num').text.strip()link = item.find('a')['href']# 詳細信息解析(穩健版)info_div = item.find('div', class_='bd')info_text = info_div.p.get_text(" ", strip=True) # 用空格替代換行# 使用正則表達式提取年份/國家/類型import repattern = r'(\d{4})[^/]*(.*?)\s+/\s+(.*?)$'match = re.search(pattern, info_text)if match:year = match.group(1).strip()country = match.group(2).strip().replace('/', ' ') # 處理國家中的斜杠genre = match.group(3).strip()else:year = country = genre = "N/A" # 無法解析時填充默認值movies.append({'標題': title,'評分': rating,'年份': year,'國家': country,'類型': genre,'鏈接': link})except Exception as e:print(f"解析條目失敗: {str(e)}")continue # 跳過當前條目return moviesdef get_all_pages(self):"""處理分頁"""all_movies = []start = 0while True:url = f"{settings.BASE_URL}?start={start}"html = self.fetch_page(url)if not html:breakmovies = self.parse_page(html)if not movies:breakall_movies.extend(movies)start += 25# 檢查是否還有下一頁if start >= 250: # Top250最多250條breakreturn all_movies
步驟4:數據存儲模塊 core/storage.py
import csv
import json
import os
from config import settingsclass DataStorage:@staticmethoddef save_csv(data, filename='douban_top250.csv'):os.makedirs(settings.OUTPUT_DIR, exist_ok=True)path = os.path.join(settings.OUTPUT_DIR, filename)with open(path, 'w', newline='', encoding='utf-8') as f:writer = csv.DictWriter(f, fieldnames=settings.CSV_HEADERS)writer.writeheader()writer.writerows(data)print(f"數據已保存至 {path}")@staticmethoddef save_json(data, filename='douban_top250.json'):os.makedirs(settings.OUTPUT_DIR, exist_ok=True)path = os.path.join(settings.OUTPUT_DIR, filename)with open(path, 'w', encoding='utf-8') as f:json.dump(data, f, ensure_ascii=False, indent=2)print(f"數據已保存至 {path}")
步驟5:主程序 main.py
from core.spider import DoubanSpider
from core.storage import DataStoragedef main():# 檢查robots協議print("豆瓣 robots.txt 重要條款:")print("User-agent: *")print("Disallow: /search") # 實際需查看最新內容# 執行爬蟲spider = DoubanSpider()movies_data = spider.get_all_pages()# 存儲數據if movies_data:DataStorage.save_csv(movies_data)DataStorage.save_json(movies_data)else:print("未獲取到有效數據")if __name__ == '__main__':main()
四、運行與驗證
- 在激活的虛擬環境中執行:
python main.py
- 檢查
output/
目錄生成的 CSV 和 JSON 文件
五、高級優化建議
- 異常處理增強:
# 在spider類中添加重試機制
def fetch_page(self, url, retries=3):for attempt in range(retries):try:# ...原有代碼...except requests.RequestException as e:if attempt == retries - 1:raiseprint(f"重試中 ({attempt+1}/{retries})...")time.sleep(2 ** attempt) # 指數退避
- 請求頭輪換:
# 在settings.py中添加多個User-Agent
USER_AGENTS = ['Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36','Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36'
]# 在helper.py中添加選擇函數
def get_random_user_agent():return random.choice(settings.USER_AGENTS)
- 代理設置(如果需要):
# 在spider初始化時添加
def __init__(self, proxy=None):if proxy:self.session.proxies = {'http': proxy, 'https': proxy}
六、法律合規檢查
- 訪問 https://www.douban.com/robots.txt 查看協議
- 重點條款:
User-agent: *
Disallow: /subject_search
Disallow: /amazon_search
Disallow: /search
Disallow: /group/search
Disallow: /event/search
Disallow: /forum/search
Disallow: /game/search
- 合規措施:
- 限制請求頻率(代碼中已實現隨機延遲)
- 不繞過反爬機制
- 僅用于學習用途
- 不存儲敏感信息
通過這個結構化的項目實現,你可以:
- 保持代碼的可維護性
- 方便后續擴展功能(如添加代理支持)
- 符合Python最佳實踐
- 有效管理依賴項
下一步可以嘗試:
- 添加日志記錄模塊
- 實現數據庫存儲(MySQL/MongoDB)
- 使用Scrapy框架重構項目
- 部署到服務器定時運行