news 2026/10/1 12:27:45

Python爬虫实战:从豆瓣短评到中文词云生成保姆级教程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python爬虫实战:从豆瓣短评到中文词云生成保姆级教程

前两天帮朋友处理了一个小需求:把一部电影在豆瓣上的最新短评爬下来,生成一张词云图看看观众都在聊什么。当时顺手写了个Python脚本,从requests爬评论,到jieba分词,再到wordcloud生成词云,前后加起来不到两百行。朋友看完直呼“就这么简单?”,所以今天把这套流程整理成一份完整教程放出来。标题里三个关键词——Python、网络爬虫、词云wordcloud——正好覆盖了从数据采集到可视化输出的整个链路,这也是很多入门者最想一次性打通的一条线。

这篇文章会带着你从零开始,先搭好Python环境和爬虫依赖,再实际爬取豆瓣最新评论,然后把评论内容清洗、分词,最后生成矩形、圆形、心形、白底黑字等各式词云。无论你是刚装好Python还没摸清语法的纯新手,还是写了几个小爬虫想再学点可视化技巧的老手,这篇文章都能直接照着抄作业。

1. 整体设计与思路拆解

1.1 项目目标与完整数据链路

这个项目说白了就是一条数据流水线:从豆瓣短评页面把观众写的最新评论抓下来,切成一个个有意义的词,再按照词频高低把高频词放大、低频词缩小,拼出一张有观赏性的图形。

整条链路分四步。第一步是数据采集,用requests向豆瓣电影短评页面发送HTTP请求,拿回HTML响应;第二步是解析提取,用BeautifulSoup定位页面里的评论节点,把评论文本全部抠出来;第三步是文本加工,调用jieba做中文分词,顺便把“我们”“这个”“真的”这类对主题表达毫无帮助的停用词过滤掉;第四步是可视化,把处理后的词列表交给wordcloud,设置字体、背景、mask蒙版,输出成PNG图片。

之所以要把这四个环节拆开讲,是因为后面排查问题基本都是按环节定位的。比如生成的词云带乱码,那大概率是第四步字体问题;词云里冒出一堆“我们”“你们”,那问题出在第三步停用词没过滤干净;压根没爬到数据,那要回到第一步看请求头或Cookie。脑子里有了这条链路,再往下走就清楚自己在干嘛了。

1.2 为什么选豆瓣评论当练手案例

想练爬虫,第一反应都是去爬豆瓣。不光是它的页面结构规整,更因为短评内容是现成的优质中文文本。

具体来说,豆瓣短评有几点很适合拿来入门。第一,页面是静态的。豆瓣短评的HTML由服务端直接渲染,评论内容就在标签结构里,不需要处理动态加载和XHR接口,对新手友好得多。第二,数据结构规整。每一条短评都被包在class="comment-item"的div里,正文放在span.short,用户和评分有各自的标签,选对CSS选择器之后一抓一个准。第三,评论语言质量高。豆瓣用户写短评往往有个人判断,高频词能直观反映大众对电影的讨论焦点,比如“特效”“剧情”“演技”“泪点”,生成的词云一眼就能看出门道。

当然也要说清楚,豆瓣近两年对未登录用户看短评做了限制,这个后面会给出解决方案。只需要把浏览器里的Cookie带进请求头,就能正常抓取,不影响学习流程。

1.3 核心工具选型解析

项目里要装的Python库有requests、beautifulsoup4、lxml、jieba、wordcloud、matplotlib、numpy、Pillow。下面这张表是我选定它们的原因,也列了备选方案供参考。

环节推荐库备选方案选择理由
网络请求requestsurllib语法简洁、自动处理编码、会话复用方便
HTML解析beautifulsoup4 + lxml正则表达式选择器直观,页面改版后调整成本低
中文分词jiebasnownlp、thulac使用简单、支持自定义词典、生态成熟
词云生成wordcloudstylecloud、pyecharts原生支持mask蒙版和自定义字体,可控性强
图片处理Pillow + numpyOpenCV生成和读取mask图片够用,没有额外安装负担

很多老教程喜欢用正则从HTML里抠数据,不是说不行,而是页面结构一改你就得跟着改一大堆匹配规则。换成BeautifulSoup用CSS选择器之后,维护成本低得多。词云库这里,wordcloud是社区里积累最久、文档最全的选择,而且支持mask蒙版,这才是后面能生成心形、圆形造型的关键。

2. 环境准备与核心细节解析

2.1 Python环境安装与虚拟环境配置

先把环境捣鼓好。很多入门者不是卡在代码上,而是卡在Python装不上、库装不对。第一步是下载Python安装包。Windows用户去官网下载3.9到3.12之间任意稳定版本,安装时一定记得勾选Add Python to PATH,不然命令行里敲python会没反应。macOS用户用brew install python@3.11就行。

装完Python后我强烈建议建一个虚拟环境,这样这个项目单独装一套库,不会和系统其它项目互相污染。在项目根目录执行:

python -m venv wordcloud_env

进入虚拟环境,Windows是wordcloud_env\Scripts\activate,macOS/Linux是source wordcloud_env/bin/activate。接着一次性安装依赖:

pip install requests beautifulsoup4 lxml jieba wordcloud matplotlib numpy pillow

如果你用的VS Code,装完后按Ctrl+Shift+P打开命令面板,搜索“Python: Select Interpreter”,把解释器指到刚才建的wordcloud_env路径下,运行代码时才会走到虚拟环境里。这一步我见过太多新手漏掉,结果终端里明明是venv,编辑器却还在用全局Python,导致库永远找不到,代码始终报ModuleNotFoundError。

2.2 发送HTTP请求的关键细节

环境准备好之后,真正写爬虫前,得先把HTTP请求的基础打好。所谓爬虫,本质上就是用程序代替人重复发送HTTP请求并解析响应。豆瓣这类网站做基础反爬,第一个检测项就是User-Agent。如果requests请求头里没有UA,服务器很可能直接拒绝访问或者返回异常页面。所以我习惯在headers里放一个完整的浏览器UA,让请求看起来像Chrome发出的。

headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", "Accept-Language": "zh-CN,zh;q=0.9", # "Cookie": "登录豆瓣后复制浏览器里的Cookie到这里" }

Accept-Language告诉服务器希望返回中文页面,可以避免拿到默认语言不对的页面。第二是超时与重试。requests不设timeout的话,一旦服务器不响应,脚本会一直挂在那里。给请求加上timeout=10,超时后再捕获异常重试,整个脚本才不会在某个页面卡死。

第三是分页参数。豆瓣短评列表的URL长这样:https://movie.douban.com/subject/{电影ID}/comments,其中srot=new_score表示按最新排序,start=0表示从第0条开始,每页最多返回20条。爬下一页只需要把start变成20、40,这个逻辑不复杂,但新手经常忘记把start写进params里,结果翻来覆去只爬到第一页。

2.3 HTML解析与评论提取

拿到HTML响应之后不要急着用正则匹配,先用BeautifulSoup解析成DOM结构再操作。

豆瓣短评页面的评论文本在class="comment-item"的div下面,定位选择器是.comment-item .short。用select('.comment-item .short')能一次性拿到当前页全部评论节点,然后逐个取.text并strip掉首尾空白。这样做的好处是页面改版时,往往只需要微调选择器,而不需要重写整个解析逻辑。

清洗这一步别偷懒。我从页面抠出来的文本可能带有换行、多个空格,甚至混进一些无关字符。最简单的方式是:

content = node.text.strip().replace("\n", " ")

处理完的评论先写进一个列表,等所有页抓完再统一落盘。如果某个页面没有正常返回评论,BeautifulSoup会解析出一个空列表,代码里要判断一下:列表为空就停掉翻页,避免继续空转。

3. 实操过程与核心环节实现

3.1 爬取豆瓣短评数据

先贴完整代码,后面一行行解释关键位置。这个脚本用《流浪地球2》演示,电影ID是35456179。

import time import random import requests from bs4 import BeautifulSoup MOVIE_ID = "35456179" # 流浪地球2,可换成其它电影ID COMMENT_URL = f"https://movie.douban.com/subject/{MOVIE_ID}/comments" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", "Accept-Language": "zh-CN,zh;q=0.9", # "Cookie": "登录豆瓣后复制浏览器里的Cookie到这里", } def fetch_comments(url, params): try: resp = requests.get(url, headers=headers, params=params, timeout=10) if resp.status_code != 200: print(f"请求失败:{resp.status_code}") return [] soup = BeautifulSoup(resp.text, "lxml") nodes = soup.select(".comment-item .short") return [node.text.strip().replace("\n", " ") for node in nodes] except Exception as e: print(f"请求异常:{e}") return [] all_comments = [] for page in range(5): params = { "sort": "new_score", "status": "P", "start": page * 20, "limit": 20, } comments = fetch_comments(COMMENT_URL, params) if not comments: print("没有拿到评论,停止翻页") break all_comments.extend(comments) print(f"第{page + 1}页抓到{len(comments)}条,累计{len(all_comments)}条") time.sleep(random.uniform(1, 3)) with open("comments.txt", "w", encoding="utf-8") as f: f.write("\n".join(all_comments)) print("爬取结束,评论已保存到 comments.txt")

这里有几个关键点。一是params不要手拼到URL里,用params参数传给requests,它会自己处理编码。二是每次翻页start递增20,循环5页能攒下100条评论,对词云来说够了,想多爬就多循环几页。三是最后用sleep做随机延时,不要每秒发一次请求,既是为了自己的IP不被封,也是给豆瓣服务器少添点麻烦。

如果运行后发现页面提示需要登录,或者只返回一页,就在headers里把Cookie那一行补上。打开浏览器登录豆瓣,按F12切到Network面板,随便找一条请求,从请求头里复制Cookie值,粘贴到代码里即可。注意复制的时候别把换行带进去,Cookie就是一行字符串。

提示:Cookie相当于你的登录凭证,千万别随手传到GitHub或者公共代码仓库里,泄露后别人可以直接拿你的账号身份操作。

3.2 分词与停用词过滤

评论爬下来之后是一个个完整的句子,直接扔给wordcloud效果会很差,因为wordcloud默认按空格切分词,中文句子没有空格,它会把一整句当成一个词块。所以要先分词。

import jieba import string with open("comments.txt", "r", encoding="utf-8") as f: text = f.read() stopwords = set("的 了 是 我 你 他 她 它 我们 你们 他们 这个 那个 一个 真的 就是 觉得 但是 电影 很 太 有 在".split()) punctuation = set(string.punctuation + ",。!?、;:“”‘’()《》【】…—") words = jieba.lcut(text) filtered = [ w.strip() for w in words if w.strip() and len(w.strip()) > 1 and w.strip() not in stopwords and w.strip() not in punctuation ]

这里我把常见的停用词直接写死在代码里,你也可以从网上下载一份中文停用词表存成stopwords.txt,然后逐行读取。实际效果更推荐把“电影”“觉得”这类评论里的高频废话也放进去,因为它们对分析观众讨论焦点没有贡献。还有一句比较重要的话:过滤条件里len(w.strip()) > 1的意思是去掉单个字,很多时候单个字的信息量不足,但如果你想保留“燃”“爽”这类关键词,可以把这个条件去掉。

分词完成后,可以用Counter看一眼词频分布,验证数据质量:

from collections import Counter counter = Counter(filtered) for word, count in counter.most_common(10): print(word, count)

如果排前10的还是“我们”“真的”这类词,说明停用词表不够,把没用的词加进去重新跑一遍就行。

3.3 生成基础词云

词云的核心是wordcloud库,但第一次用中文最容易踩坑的是字体。wordcloud默认字体不支持中文,不指定字体就会导出一片方框或乱码。所以要给font_path传入一个中文字体路径。Windows可以直接用微软雅黑msyh.ttc,macOS用系统里的PingFang.ttc,Linux可以用思源黑体。更省事的做法是到网上下载思源黑体放到项目目录,路径写成相对路径,换机器不踩坑。

from wordcloud import WordCloud import matplotlib.pyplot as plt text_for_cloud = " ".join(filtered) wc = WordCloud( font_path="msyh.ttc", width=800, height=600, background_color="white", max_words=200, max_font_size=150, random_state=42, collocations=False, ).generate(text_for_cloud) wc.to_file("basic_wordcloud.png") plt.figure(figsize=(10, 8)) plt.imshow(wc, interpolation="bilinear") plt.axis("off") plt.show()

说几个参数的含义。width和height是画布尺寸,越大图片越清晰但生成越慢。background_color控制底色,white就是白底,改成black就是黑底。max_words限制最多展示200个词,词太多了画面会挤。max_font_size是最大字号,防止特别高频的词撑爆画布。random_state是随机数种子,wordcloud布局本身有随机性,固定之后每次生成结果一致,方便反复调参对比。collocations=False是我强烈建议加的,wordcloud默认会去识别两个词的组合,中文语境下容易把“流浪地球”这种词组拼成一个整体,你想按单个词展示就把它关掉。

3.4 生成各式词云:圆形、心形与白底效果

基础版出来之后,就能玩点花样了。wordcloud的mask参数允许传一张模板图,它会只在模板的非白色区域绘制词语,其它区域留白,这样就实现了圆形、心形等各式形状。

先说圆形词云。找一张白底带黑色或彩色圆形的图片,或者自己用Pillow画一张:

from PIL import Image, ImageDraw import numpy as np size = (600, 600) img = Image.new("RGB", size, "white") draw = ImageDraw.Draw(img) draw.ellipse((60, 60, 540, 540), fill="black") # 圆形区域填黑 img.save("circle_mask.png") mask = np.array(img.convert("L"))

然后生成词云时,把mask传进去:

wc = WordCloud( font_path="msyh.ttc", mask=mask, background_color="white", max_words=200, max_font_size=120, random_state=42, colormap="Blues", ).generate(text_for_cloud) wc.to_file("circle_wordcloud.png")

注意,mask一旦设定,width和height会失效,实际尺寸由图片大小决定。

想做心形词云,同理,关键在于准备一张“白底+心形填充”的mask图。我习惯用参数方程现场画一个,不发散找素材:

import numpy as np from PIL import Image, ImageDraw size = (600, 600) img = Image.new("RGB", size, "white") draw = ImageDraw.Draw(img) points = [] scale = 12 for t in range(0, 360): rad = np.radians(t) x = 300 + scale * 16 * np.sin(rad) ** 3 y = 300 - scale * ( 13 * np.cos(rad) - 5 * np.cos(2 * rad) - 2 * np.cos(3 * rad) - np.cos(4 * rad) ) points.append((x, y)) draw.polygon(points, fill="black") img.save("heart_mask.png")

如果你不想自己画,也能直接下载一张白底红色心形PNG,只要背景是纯白、心形区域不是白色就行。这里有个容易踩的坑:很多素材背景不是纯白,带渐变或半透明,直接转数组后背景区域也会被填词。判断标准很简单,用PIL打开后缩略图看看四个角是否都是纯白像素。

生成经典“白底心形红字”词云的完整代码:

from wordcloud import WordCloud import matplotlib.pyplot as plt import numpy as np from PIL import Image mask = np.array(Image.open("heart_mask.png").convert("L")) wc = WordCloud( font_path="msyh.ttc", mask=mask, background_color="white", max_words=200, max_font_size=120, random_state=42, collocations=False, contour_width=2, contour_color="crimson", colormap="Reds", ).generate(text_for_cloud) wc.to_file("heart_wordcloud.png") plt.figure(figsize=(8, 8)) plt.imshow(wc, interpolation="bilinear") plt.axis("off") plt.show()

这里再讲两个参数。contour_width和contour_color是给形状描边,数值越大边越粗,颜色用crimson这种和Reds配色接近的红色会显得协调。colormap控制文字配色,想做出白底红字的心形词云效果就用Reds。想换风格的话,背景改成black、colormap改成plasma,又是另一番感觉。

其他任意形状也完全是同一套思路。比如想生成猫形、五角星、地图形状,只需要找到对应的白底剪影图,传给mask就行,代码都不用变。

4. 常见问题与排查技巧实录

4.1 高频问题速查表

把这段时间实操时遇到的问题整理成了一张速查表,方便你将来对号入座:

问题现象可能原因解决办法
词云图全是方框/乱码没设置font_path给WordCloud传入中文字体路径
词云只有一两个超大词,其它糊成一片max_words太小,或者停用词没过滤干净加大max_words,完善停用词表
输出图片边缘有白色块mask图片背景不纯白换纯白底素材,或用PIL重新生成
爬到几页后返回403/429请求频率太高或UA被识别加随机延时、换UA,必要时补Cookie
评论总是抓不到、解析结果为空Cookie过期,或页面结构改版登录后重新复制Cookie,检查选择器
命令行打印中文报UnicodeEncodeErrorWindows控制台默认编码问题先把数据写入文件,不要直接print大量中文
词云显示“流浪地球”而不是“流浪”“地球”wordcloud默认合并了bigram加上collocations=False
mask无效或者报图片读取错误图片路径不对,或mask带透明通道检查路径,确保图片是RGB/灰度模式

4.2 反爬与稳定性实战心得

爬虫这一块,最容易被新手忽略的是稳定性。我第一次爬豆瓣时,傻乎乎地每秒发一次请求,结果爬到第三页就被拦了,后面半小时都在等解封。后来学乖了,有几个习惯真的建议大家养成。

第一,请求间隔做随机化。time.sleep(random.uniform(1, 3))和固定sleep差别很大,随机间隔能让请求节奏更接近真实用户操作。第二,失败重试。requests请求偶尔会因为网络波动抛异常,我的fetch_comments函数里用try/except兜底了,但这只是最低保障。更稳妥的做法是重试三次,每次间隔递增:

for attempt in range(3): try: resp = requests.get(url, headers=headers, params=params, timeout=10) break except Exception as e: print(f"第{attempt + 1}次请求失败:{e}") time.sleep(attempt * 3)

第三,把中间数据落盘。每次跑完脚本,评论、清洗后的词都要保存成文件。我之前就是没存清洗后的词列表,调整参数重跑时发现原始评论又变了,前后对比搞得一团乱。第四,别硬扛登录限制。豆瓣短评没登录能看的页数很有限,与其频繁换代理硬扛,不如登录后带Cookie跑,稳定得多。

4.3 词云美化的进阶建议

基础能跑通,接下来就能琢磨怎么让词云更好看了。分享几个实际经验。

字体是出效果的第一要素。同一个心形图,用默认黑体做的效果偏硬,换成年轮体、圆体,气质立刻不一样。推荐思源黑体、站酷小薇LOGO体这类开源字体,注意商用前确认授权。配色别用单一纯色,colormap换个参数效果差异巨大,viridis、magma、plasma、Reds、Oranges这几个我轮流试过,都挺稳定。想自定义配色,还可以传一个函数,根据词频返回不同颜色,做出深浅变化。

布局参数也值得调。prefer_horizontal默认是0.9,表示90%的词横向排布,改成0.5就会横竖各半,适合窄长图。relative_scaling决定词频对字号的影响程度,默认0.5,想突出高频词就调到0.8。最后是小图和大图的区别,想发公众号或者做PPT,建议把width、height设到1600x1200,配合scale=2再放大一圈,导出图片细节更清楚,放大也不会糊。生成速度会慢一些,但效果完全值得。

最后说一个我个人的体会:爬虫和词云单独拿出来都不难,难的是把它们串成一条完整链路,并且在每个环节都留好缓存、兜底和错误提示。只要一次跑通了,剩下的玩法就多了。比如把评论按时间分成前后两组,分别生成两张词云对比,就能直观看到观众关注点的变化;或者把词频数据导出成Excel,结合评分做进一步分析。写这份教程的时候,我顺手把最后生成的心形词云打印出来贴在了工位旁边,别的不说,视觉上确实挺有成就感。你完全可以照这个流程动手跑一次,改一两个参数,就会发现自己也能做出挺专业的词云图。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 12:27:24

Mistral 7B微调实战:从数据集构建到LoRA训练与部署全指南

作为一个已经把前面五篇都跟下来的读者,你大概率已经完成了 Mistral 系列的基础认知搭建——跑通了 API 调用、试过 Prompt 工程、折腾过 RAG 检索增强,甚至可能在本地环境里部署过量化版的模型。到了第六篇,如果还停留在“调用别人的接口”这…

作者头像 李华
网站建设 2026/10/1 12:26:59

多智能体系统落地架构实战:从单Agent崩溃到四层协作编排

1. 多智能体系统落地架构的核心命题1.1 为什么单Agent撑不起复杂业务过去一年我参与过三个多智能体系统的落地项目,从客服工单自动分派到工业质检报告生成,踩过的坑比写过的代码还多。先说一个最直观的感受:单Agent架构在Demo阶段看起来很美好…

作者头像 李华
网站建设 2026/10/1 12:26:28

摩尔线程社区版驱动v240.50.0.1开放HDR支持:完整设置与问题排查

1. 这次社区版驱动更新的核心:HDR 支持开放的含金量摩尔线程把社区版驱动推到了 v240.50.0.1,这几天显卡群和评测圈里讨论最多的就是这一版正式开放了 HDR 支持。对用 MTT S80、S70 这类桌面卡的朋友来说,这算是一个盼了挺久的功能&#xff1…

作者头像 李华
网站建设 2026/10/1 12:26:25

AI预测驱动的高负载处理:从JMeter压测到K8s弹性扩容实践

去年做一次大促系统的压测,凌晨两点线上突然涌进一波流量,我们提前配置的线程池和集群节点直接被冲垮,接口超时率飙到80%。当时第一反应是加机器,但扩容脚本生效时流量已经回落了,机器加了个寂寞。那以后我一直在想&am…

作者头像 李华
网站建设 2026/10/1 12:25:14

MS-Swift + VSCode 调试:大模型微调全流程实战指南

最近把一批模型微调任务从训练脚本硬啃模式,彻底迁到了 MS-Swift 框架 VSCode 远程调试的流程里,顺手把数据集注册、动态数据增强、词表扩展、模型结构修改、自定义 loss 这些硬骨头全啃了一遍。这套组合拳打下来,训练效率提升不是一点半点&…

作者头像 李华
网站建设 2026/10/1 12:24:55

PSCAD Co-Simulation API技术文档翻译实战:从术语到代码全解析

1. 翻译之前,先把 Co-Simulation API 这几个字拆明白 1.1 Co-Simulation 到底协同了什么 仿真圈里提到联合仿真,第一反应往往是机电联合仿真、电磁暂态与机电暂态混合仿真,甚至还有人想到 PLC 与虚拟 PLC 那一类东西。但 PSCAD 里这份 Co-Si…

作者头像 李华