豆瓣影评数据分析入门:用Python+SQLite3构建你的第一个电影评论数据库
豆瓣影评数据分析实战:从爬取到SQLite存储的全流程解析
电影评论数据蕴含着丰富的观众情感和市场反馈信息,对于影视从业者、数据分析爱好者而言都是极具价值的原始素材。本文将手把手带你构建一个完整的豆瓣影评数据处理流程,涵盖网页爬取、数据清洗到结构化存储的全套技术方案。
1. 环境准备与基础工具链搭建
在开始爬取数据之前,需要配置好Python开发环境并安装必要的工具库。推荐使用Python 3.8+版本,它能提供更好的异步支持和类型提示功能。
核心依赖库安装:
pip install beautifulsoup4 requests html5lib sqlite3
这些库各司其职:
beautifulsoup4:HTML文档解析神器requests:人性化的HTTP请求库html5lib:容错性强的HTML解析器sqlite3:Python内置的轻量级数据库引擎
建议使用虚拟环境隔离项目依赖,避免不同项目间的库版本冲突。创建虚拟环境的命令:
python -m venv douban_venv
source douban_venv/bin/activate # Linux/Mac
douban_venv\Scripts\activate # Windows
2. 豆瓣影评页面结构分析与爬取策略
豆瓣电影评论页面采用分页加载机制,每页默认展示20条评论。通过分析URL规律可以发现,分页参数通过start查询字符串控制:
https://movie.douban.com/subject/30128916/reviews?start=20
关键爬取策略:
- 模拟浏览器请求头,降低被封禁风险
- 实现自动翻页逻辑,遍历所有评论页面
- 控制请求频率,建议间隔2-3秒
- 处理反爬机制,实现异常重试
以下是一个健壮的请求函数实现:
import requests
import time
from fake_useragent import UserAgent
def fetch_html(url, retry=3):
headers = {
'User-Agent': UserAgent().random,
'Accept-Language': 'zh-CN,zh;q=0.9',
}
for attempt in range(retry):
try:
resp = requests.get(url, headers=headers, timeout=10)
resp.raise_for_status()
return resp.text
except Exception as e:
print(f"请求失败({attempt+1}/{retry}): {str(e)}")
time.sleep(2 ** attempt) # 指数退避
return None
3. 数据解析与清洗技术详解
获取HTML源码后,需要从中提取结构化数据。豆瓣影评页面包含多种信息元素:
- 评论标题
- 评论正文
- 评分信息
- 有用数统计
- 发布时间
使用BeautifulSoup解析的关键代码:
from bs4 import BeautifulSoup
def parse_reviews(html):
soup = BeautifulSoup(html, 'html5lib')
reviews = []
for item in soup.select('.review-item'):
try:
title = item.select_one('h3 > a').get_text(strip=True)
content = item.select_one('.review-content').get_text(' ', strip=True)
rating = item.select_one('.main-title-rating')['title'] if item.select_one('.main-title-rating') else None
useful_count = int(item.select_one('.action-count').get_text(strip=True).split()[0])
pub_date = item.select_one('.main-meta').get_text(strip=True)
reviews.append({
'title': title,
'content': content,
'rating': rating,
'useful_count': useful_count,
'pub_date': pub_date
})
except Exception as e:
print(f"解析异常: {str(e)}")
continue
return reviews
数据清洗注意事项:
- 处理HTML实体编码(如 )
- 统一日期时间格式
- 过滤广告和无效评论
- 处理emoji等特殊字符
4. SQLite数据库设计与高效存储方案
SQLite作为轻量级关系型数据库,非常适合个人项目和小型数据存储。我们需要设计合理的表结构来存储影评数据。
数据库表结构设计:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | INTEGER | 主键,自增 |
| movie_id | TEXT | 电影唯一标识 |
| title | TEXT | 评论标题 |
| content | TEXT | 评论正文 |
| rating | TEXT | 星级评价 |
| useful_count | INTEGER | 有用数 |
| pub_date | TEXT | 发布时间 |
| crawl_time | TEXT | 爬取时间 |
数据库操作封装类:
import sqlite3
from datetime import datetime
class DoubanDB:
def __init__(self, db_path='douban_reviews.db'):
self.conn = sqlite3.connect(db_path)
self._create_table()
def _create_table(self):
sql = """
CREATE TABLE IF NOT EXISTS reviews (
id INTEGER PRIMARY KEY AUTOINCREMENT,
movie_id TEXT NOT NULL,
title TEXT NOT NULL,
content TEXT NOT NULL,
rating TEXT,
useful_count INTEGER DEFAULT 0,
pub_date TEXT,
crawl_time TEXT,
UNIQUE(movie_id, title) ON CONFLICT IGNORE
)
"""
self.conn.execute(sql)
self.conn.commit()
def insert_review(self, movie_id, review):
sql = """
INSERT INTO reviews
(movie_id, title, content, rating, useful_count, pub_date, crawl_time)
VALUES (?, ?, ?, ?, ?, ?, ?)
"""
crawl_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
params = (
movie_id,
review['title'],
review['content'],
review['rating'],
review['useful_count'],
review['pub_date'],
crawl_time
)
self.conn.execute(sql, params)
self.conn.commit()
def close(self):
self.conn.close()
性能优化技巧:
- 使用事务批量插入
- 建立合适索引
- 定期执行VACUUM优化
5. 完整爬虫系统实现与异常处理
将各个模块组合起来,构建完整的爬虫系统。以下是主控程序的实现框架:
import time
from urllib.parse import urljoin
class DoubanSpider:
def __init__(self, movie_id):
self.movie_id = movie_id
self.base_url = f"https://movie.douban.com/subject/{movie_id}/reviews"
self.db = DoubanDB()
def crawl(self, max_pages=50):
page = 0
while page < max_pages:
url = f"{self.base_url}?start={page*20}"
print(f"正在爬取: {url}")
html = fetch_html(url)
if not html:
print(f"获取页面失败: {url}")
break
reviews = parse_reviews(html)
if not reviews:
print("未解析到评论数据,可能已达末页")
break
for review in reviews:
self.db.insert_review(self.movie_id, review)
page += 1
time.sleep(3) # 遵守爬虫礼仪
def close(self):
self.db.close()
# 使用示例
if __name__ == '__main__':
spider = DoubanSpider('30128916') # 电影《八佰》ID
try:
spider.crawl(max_pages=10)
finally:
spider.close()
关键异常处理点:
- 网络请求超时重试
- 页面解析容错
- 数据库写入冲突处理
- 反爬识别与规避
6. 数据质量检查与后续分析准备
存储到数据库后,需要进行数据质量检查,确保后续分析的可靠性。
常见数据问题检查清单:
- 重复评论检测
- 缺失值处理
- 异常值识别
- 文本编码统一
使用SQL查询进行基础检查:
-- 检查数据完整性
SELECT COUNT(*) AS total,
COUNT(DISTINCT title) AS unique_titles,
COUNT(rating) AS rated_count
FROM reviews;
-- 查看评分分布
SELECT rating, COUNT(*) AS count
FROM reviews
WHERE rating IS NOT NULL
GROUP BY rating
ORDER BY count DESC;
对于文本数据,建议进行以下预处理:
- 去除HTML残留标签
- 统一全角/半角字符
- 标准化日期格式
- 分词处理(中文评论)
7. 扩展思路:从数据存储到价值挖掘
构建好影评数据库只是第一步,真正的价值在于后续的数据分析和可视化呈现。几个值得尝试的方向:
情感分析: 使用NLP技术分析评论情感倾向,了解观众对电影的整体评价。
主题建模: 通过LDA等算法发现评论中的主要讨论话题。
时间序列分析: 观察评论数量和情感随时间的变化趋势。
关联分析: 研究评分、有用数和评论长度之间的关系。
实现基础情感分析的代码示例:
from snownlp import SnowNLP
def analyze_sentiment(content):
s = SnowNLP(content)
return s.sentiments # 返回0-1之间的情感分值
# 为数据库添加情感分析字段
def add_sentiment_analysis(db_path):
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
# 添加新字段
try:
cursor.execute("ALTER TABLE reviews ADD COLUMN sentiment REAL")
except sqlite3.OperationalError:
pass # 字段已存在
# 更新已有数据
cursor.execute("SELECT id, content FROM reviews WHERE sentiment IS NULL")
for row in cursor.fetchall():
doc_id, content = row
sentiment = analyze_sentiment(content)
cursor.execute("UPDATE reviews SET sentiment=? WHERE id=?", (sentiment, doc_id))
conn.commit()
conn.close()
在实际项目中,建议将爬虫任务分解为多个阶段执行,并做好日志记录。可以尝试以下优化:
- 实现增量爬取,只获取新增评论
- 添加代理IP支持,提高爬取稳定性
- 使用任务队列管理爬取流程
- 添加监控告警机制
更多推荐


所有评论(0)