零基础实战:Python爬虫入门之豆瓣影评数据抓取与存储

张开发
2026/4/21 5:57:26 15 分钟阅读

分享文章

零基础实战:Python爬虫入门之豆瓣影评数据抓取与存储
1. 为什么选择豆瓣影评作为爬虫入门项目第一次接触Python爬虫时很多人都会纠结从哪个网站开始练习。我当年也试过新闻网站、电商平台最后发现豆瓣电影是最适合新手的练习场。原因很简单豆瓣的网页结构相对规范反爬机制不算太严格而且影评数据对格式要求不高容错空间大。记得我带的第一个爬虫项目小组有个成员用某电商网站练手结果刚爬了几十条数据就被封IP。后来改爬豆瓣影评当天就完成了数据采集。这里有个小技巧新手最好选择内容类网站而非交易类网站前者对爬虫的容忍度通常更高。豆瓣影评还有个优势是数据价值高。你爬下来的影评可以用来做情感分析、关键词提取甚至训练简单的推荐模型。我去年就用爬取的10万条影评训练了一个电影类型分类器准确率能达到75%左右。这种即时反馈对初学者特别重要能让你保持学习动力。2. 环境准备与基础工具安装工欲善其事必先利其器。在开始写爬虫代码前我们需要准备好Python环境。推荐使用Python 3.8或以上版本太老的版本可能会遇到库兼容问题。我习惯用Anaconda管理环境这样可以避免把系统搞得一团糟。核心工具库就这几个requests比urllib更人性化的HTTP库BeautifulSoupHTML解析神器sqlite3Python内置的轻量级数据库re正则表达式模块安装方法很简单pip install requests beautifulsoup4这里有个新手常踩的坑不要一上来就装scrapy这样的大型框架。我见过太多人花两天时间配置scrapy结果连最简单的页面都爬不下来。先用基础库把流程跑通等需要爬大规模数据时再考虑框架。3. 分析豆瓣影评页面结构打开任意豆瓣电影页面比如《流浪地球》的影评页按F12打开开发者工具。你会发现每个影评条目都在一个class为main-bd的div里这个规律很重要。我常用的分析方法是右键点击影评标题 - 检查在Elements面板找到对应HTML代码观察父级容器的class或id以《流浪地球》为例影评链接的规律是这样的h2a hrefhttps://movie.douban.com/review/1234567/影评标题/a/h2分页参数也很有规律https://movie.douban.com/subject/1234567/reviews?start20这个start20表示从第20条影评开始显示每页固定20条。4. 编写基础爬取代码先写个最简单的页面下载函数import requests def get_html(url): headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) } try: r requests.get(url, headersheaders) r.raise_for_status() return r.text except requests.exceptions.RequestException as e: print(f请求失败: {e}) return None测试一下url https://movie.douban.com/subject/26266893/reviews html get_html(url) print(html[:500]) # 打印前500字符看是否成功接下来用BeautifulSoup解析from bs4 import BeautifulSoup soup BeautifulSoup(html, html.parser) reviews soup.find_all(div, class_main-bd) for review in reviews: title review.h2.a.text.strip() print(title)5. 处理反爬机制豆瓣的反爬策略主要有这些频率检测连续快速请求会被封User-Agent验证不加UA头会被拦截Cookie检查某些页面需要登录状态我的应对方案import time import random def safe_get(url): headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64), Cookie: 你的cookie(可选) } time.sleep(random.uniform(1, 3)) # 随机等待1-3秒 return get_html(url)实测下来控制请求间隔在2秒以上基本不会被封。如果要做长期采集建议使用代理IP池模拟真实用户行为设置异常重试机制6. 数据存储方案最简单的存储方式是CSVimport csv def save_to_csv(data, filename): with open(filename, a, newline, encodingutf-8) as f: writer csv.writer(f) writer.writerow(data)但更推荐用SQLite方便后续查询import sqlite3 def init_db(): conn sqlite3.connect(reviews.db) c conn.cursor() c.execute(CREATE TABLE IF NOT EXISTS reviews (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, content TEXT, movie_id INTEGER)) conn.commit() conn.close() def save_to_db(review): conn sqlite3.connect(reviews.db) c conn.cursor() c.execute(INSERT INTO reviews (title, content, movie_id) VALUES (?, ?, ?), (review[title], review[content], review[movie_id])) conn.commit() conn.close()7. 完整代码示例结合所有知识点这是完整版的爬虫代码import requests from bs4 import BeautifulSoup import sqlite3 import time import random class DoubanSpider: def __init__(self, movie_id): self.movie_id movie_id self.base_url fhttps://movie.douban.com/subject/{movie_id}/reviews self.init_db() def init_db(self): self.conn sqlite3.connect(reviews.db) self.c self.conn.cursor() self.c.execute(CREATE TABLE IF NOT EXISTS reviews (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, content TEXT, movie_id INTEGER)) self.conn.commit() def get_html(self, url): headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) } try: time.sleep(random.uniform(1, 3)) r requests.get(url, headersheaders) r.raise_for_status() return r.text except Exception as e: print(f请求失败: {e}) return None def parse_reviews(self, html): soup BeautifulSoup(html, html.parser) reviews soup.find_all(div, class_main-bd) results [] for review in reviews: title review.h2.a.text.strip() link review.h2.a[href] content_html self.get_html(link) if content_html: content self.parse_content(content_html) results.append({ title: title, content: content, movie_id: self.movie_id }) return results def parse_content(self, html): soup BeautifulSoup(html, html.parser) content_div soup.find(div, idlink-report) if content_div: return content_div.get_text().strip() return def save_review(self, review): self.c.execute(INSERT INTO reviews (title, content, movie_id) VALUES (?, ?, ?), (review[title], review[content], review[movie_id])) self.conn.commit() def crawl(self, pages5): for i in range(pages): url f{self.base_url}?start{i*20} html self.get_html(url) if html: reviews self.parse_reviews(html) for review in reviews: self.save_review(review) print(f已保存: {review[title]}) def close(self): self.conn.close() # 使用示例 if __name__ __main__: spider DoubanSpider(26266893) # 流浪地球的ID spider.crawl(pages3) spider.close()8. 常见问题与解决方案问题1突然获取不到数据了可能原因IP被封或网页改版 解决方案检查返回的状态码更换User-Agent用浏览器手动访问看页面是否正常问题2数据中有乱码解决方法html response.content.decode(utf-8)问题3需要登录才能查看解决方法手动登录后复制cookie添加到请求头中或者使用selenium模拟登录问题4分页失效可能原因豆瓣改动了分页逻辑 解决方法改用滚动加载的API接口或使用selenium模拟滚动9. 项目扩展建议当基础爬虫跑通后可以尝试这些进阶功能自动翻页检测下一页按钮自动爬取多线程爬取使用concurrent.futures提高效率数据可视化用pyecharts做影评词云情感分析使用snownlp分析评论情感倾向比如做词云分析from wordcloud import WordCloud import jieba def generate_wordcloud(text, output_file): wordlist jieba.cut(text) wc WordCloud(font_pathsimhei.ttf, background_colorwhite) wc.generate( .join(wordlist)) wc.to_file(output_file)爬虫最有趣的地方在于你可以根据自己的需求不断扩展功能。我有个学生就用豆瓣影评数据做了个电影推荐系统虽然简单但效果还不错。

更多文章