这两年我见过不少以“基于 Python 的美妆产品网络评价的数据采集与分析”为标题的项目,往往后面还会带一个日期和编号,比如 2025_a0h0967b 这种。一开始我也觉得命名平平无奇,真正把整条链路跑通后发现,这种课题覆盖的刚好是数据工作里最值钱的一段:从零散的用户评论里采出结构化的数据,再通过 Python 数据采集与数据分析的技术把“好用”“搓泥”“回购”这类口语词变成能支撑选品和产品迭代的结论。这篇我就按实际做项目的顺序来拆:先定需求和数据源,再讲采集代码的坑,重点讲数据清洗和情感分析,最后放一个可以直接参照的排查清单。
如果你正准备做类似的课程设计、毕业课题,或者要给某个美妆店铺做消费者口碑洞察,这篇文章的路径基本可以直接搬。我尽量把“为什么这么做”也讲清楚,而不是只给一段能跑的代码。
1. 拆项目:这类标题背后到底要交付什么
1.1 先判断你是“交付代码”还是“交付结论”
拿到这种项目标题,第一件事不是写爬虫,而是搞清楚项目的验收方到底要什么。
如果是课程设计或结课作业,验收方通常关注你有没有完整走通“采集-存储-清洗-分析-可视化”的链路,代码结构、注释、文档占分不低。如果你是给品牌方或店铺做分析,他们只关心你能不能说清楚某个精华液为什么口碑两极分化、油皮用户抱怨最多的是什么。这两类目标的“够用”标准完全不一样。
我自己的建议是,不管哪种场景,都按“采集 + 分析 + 结论”完整闭环去搭,代码封装成模块,最后产出一份带可视化结论的分析报告。这样哪怕验收入想挑毛病,也没什么可挑的。
1.2 美妆评论和其他评论数据有什么不一样
美妆产品网络评价是一个很适合拿来练手的场景,但它跟数码产品、图书评论有明显差异。
第一,评论量集中且词汇极度口语化。“绝绝子”“yyds”和“闷痘”“拔干”这几种词会混在一起,直接按分数去统计会丢掉大量信息。第二,评论里隐藏着多维属性:肤质(油皮/干皮/敏感肌)、场景(通勤/约会)、质地(滋润/清爽)、成分(烟酰胺/水杨酸)、色号适配等。第三,评价还呈现明显的“种草-吐槽”共存文字,比如“味道好闻但是持久度不行”,单纯做好坏分类远远不够。
因此,这个项目的核心难点不在“能爬到多少条”,而在于你能否把杂乱评价拆成几个可量化维度。理解了这一点,技术选型才会有方向。
1.3 全流程最少需要哪几个环节
用一张逻辑图来理解整个项目的话,它应该是这样的:
- 确定研究对象:选定一个或几个美妆产品/品牌,明确要比什么。
- 数据源评估:选择公开网页、评论接口或经过授权的数据服务。
- 采集入库:Python 脚本定时/批量抓取评论,规范字段后存库。
- 数据清洗:去重复、去广告、规整文本、剔除低质量评论。
- 分词与特征构造:用 jieba 等工具做分词,构造肤质、质地、褒贬等特征。
- 情感分析与洞察:对每个商品/维度做情感极性判断和话题归纳。
- 可视化输出:用 wordcloud、pyecharts 或 matplotlib 把结果落到图表。
在这里面,Python 从头到尾只会用到一条技术栈,不用频繁切换语言,这也是我推荐用 Python 做这类项目最直接的原因。
1.4 年度后缀和编号有什么用
标题里的 2025_a0h0967b 这种命名,常见于实训平台或归档系统,本质是为了标记项目版本。但反过来提醒我们:数据处理项目,版本管理很重要。评论数据一旦重采,结果可能就变了,脚本和数据文件最好都打上日期和版本。这不是形式感,而是避免你一个月后回来看不清楚“这份词云是用哪批数据跑出来的”。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 工具清单与环境准备
2.1 给新手的 Python 环境建议
如果你是从零开始,我建议直接安装 Python 3.10 以上版本。不要一上来就折腾虚拟环境和多种解释器,先保证一条主链路能跑通。
安装完成后,建议用以下命令安装依赖:
bash复制pip install requests pandas beautifulsoup4 parsel lxml jieba snownlp wordcloud pyecharts openpyxl
我解释一下为什么是这些,而不是更多花哨的库:
- requests:发起 HTTP 请求,处理绝大多数静态接口。
- parsel / beautifulsoup4:解析 HTML 和 JSON 中的结构化数据。
- pandas:清洗和表格化处理,做过去重和筛选后你会发现它太顺手了。
- jieba:中文分词,必须要配自定义词典才适合美妆场景。
- snownlp:轻量情感分析库,适合做基线版本,但不建议直接上生产结论。
- wordcloud、pyecharts:词云和交互图表的可视化层。
很多资料会让你直接装 scrapy、selenium 这些重量级工具,如果目标只是几千条评论,实际上用不上。requests + pandas 已经能覆盖项目的主体部分,scrapy 适合超大规模定向爬取,selenium/playwright 只在你非抓不可、且页面完全依赖 JavaScript 渲染时才需要引入。
2.2 数据源选择:不是所有平台都适合当爬取对象
我在实际做项目时,会把数据源分成三类:
| 类型 | 特征 | 适合程度 |
|---|---|---|
| 公开静态页面/开放数据 | 无需登录即可浏览,接口返回完整 JSON | 最适合练习,合规压力小 |
| 登录后可见的电商评论 | 需要账号 cookie,接口有签名和加密参数 | 不建议个人批量爬,风险高 |
| 社媒种草内容 | 图文笔记、短视频评论,多以动态渲染为主 | 可以做,但要关注作者版权和平台规则 |
对于美妆产品网络评价,真正的评价富矿集中在电商购买评论和内容平台的种草笔记。我的经验是,课程设计和学习目的优先选择那些公开、无需登录的页面;如果是真实商业诉求,推荐使用平台开放接口、数据服务商或授权数据库,把精力省下来去做更有价值的分析部分。不要在合规层面给自己挖坑。
2.3 反爬机制决定了你要不要上浏览器渲染
2025 年这个时间点,还能用 requests 直接拿到的评论接口已经越来越少,很多平台都在评论数据上做了签名、风控和字体反爬。遇到这种情况,我的建议是分级处理:
- 如果接口返回是干净 JSON,直接用 requests 处理,效率最高。
- 如果页面数据由 JavaScript 动态加载,但网络请求里有固定的 JSON 接口,用抓包工具找到接口地址,补上必要的请求头,照常请求即可。
- 如果请求参数每次都在变,且有签名算法,优先看是否有企业级授权的数据服务。
- 如果非要抓取动态页面不可,用 DrissionPage 或 Playwright 这类浏览器自动化工具。
我个人很少一上来就开浏览器模拟,因为浏览器自动化的维护代价比纯 HTTP 请求高很多。先抓包分析网络请求,永远是性价比最高的方案。
3. 采集层:把几十万条评论变成结构化数据
3.1 先建字段模型,再写采集代码
许多初学者一上来就“先爬了再说”,结果拿到的是几十个字段混在一起的 HTML 文本,清洗时想死的心都有。我建议在写第一行代码前,先确定你最终要保留哪些字段。
对于美妆评论分析,一个相对合理的存储结构大致是:
| 字段名 | 说明 | 是否必留 |
|---|---|---|
| comment_id | 评论唯一标识 | 建议保留,用于去重 |
| product_id / product_name | 所属商品或产品线 | 必须 |
| sku_info | 具体色号/规格 | 强烈建议 |
| rating | 用户给出的星级或评分 | 必须 |
| content | 评论正文 | 必须 |
| tag_list | 商品标签、购买属性等信息 | 尽量保留 |
| created_at | 发布时间 | 必须 |
| like_num | 点赞/热度 | 可选 |
| user_hash | 脱敏后的用户标识,不要存原始账号 | 可选 |
需要注意,user_hash 是对用户信息脱敏后得到的值,比如对原始 ID 做 MD5。原始账号、手机号、收货地址这类字段如果出现在返回结果里,直接丢弃。批量处理公开评论时,我们只对文本内容做分析,不需要也不应该触碰个人隐私信息。
3.2 一个可复用的采集框架
直接看代码框架会更直观。这里我以一套简化的 JSON 评论接口为例,帮你理解请求、解析、入库的思路。
python复制import requests
import pandas as pd
import time
import hashlib
def fetch_comments(page):
url = "https://example.com/api/comments"
params = {
"productId": "p9527",
"page": page,
"pageSize": 20,
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
"Referer": "https://example.com/product/9527",
}
resp = requests.get(url, params=params, headers=headers, timeout=10)
resp.raise_for_status()
return resp.json()
def parse_comments(data):
rows = []
for item in data["data"]["comments"]:
rows.append({
"comment_id": item["id"],
"product_name": item["productName"],
"sku_info": item.get("skuInfo", ""),
"rating": item.get("rating", 0),
"content": item.get("content", "").strip(),
"created_at": item.get("createdAt", ""),
})
return rows
def collect(max_pages=20):
all_rows = []
for page in range(1, max_pages + 1):
try:
data = fetch_comments(page)
rows = parse_comments(data)
if not rows:
break
all_rows.extend(rows)
print(f"page {page} fetched, total {len(all_rows)}")
time.sleep(1.5) # 控制请求频率
except Exception as e:
print(f"page {page} error: {e}")
break
return pd.DataFrame(all_rows)
if __name__ == "__main__":
df = collect(max_pages=10)
df.to_csv("comments_raw.csv", index=False, encoding="utf-8-sig")
这段代码的关键设计有三处。
一是把请求和解析拆开,万一页面结构变了,只需要改 parse_comments 里的字段映射。二是 time.sleep(1.5) 在真实抓取里必须有,这不是为了程序优雅,而是降低对目标服务的压力,避免给双方都制造麻烦。三是导出 CSV 时用 utf-8-sig 而不是 utf-8,因为 utf-8-sig 能让 Excel 直接打开中文不乱码。
需要特别说明,真实平台的评论接口大概率不是这么直白,往往会有时间戳、签名甚至加密参数。这部分我没有办法在文章中给出一份“通杀代码”,因为每个平台的算法都在变,而且批量爬取授权边界非常敏感。我的实际做法是:学习阶段用公开数据集或自建演示接口验证流程,商业阶段走正规接口或数据采购,把时间花在数据怎么分析上。
3.3 采集过程中的常见隐患
页面没变,昨天能爬到今天断掉——这是最常见的情况。大多数时候不是对方封了你,而是你的请求头缺失或频率太快。先把请求头补全,再把请求间隔从 0.5 秒调成 2 秒以上,能解决大部分问题。
另一种情况是爬到后面发现大量重复内容,原因通常是评论列表页的排序不稳定。请求下一页时恰好赶上评论排序更新,会重复抓取。解决办法就是在入库环节用 comment_id 去重。评论 ID 的稳定性远比内容本身稳定,所以它才是我建议保留 comment_id 的真正原因。
4. 评论数据的清洗与情感分析
4.1 先处理掉那批“不是评论”的评论
采集下来的原始数据不能直接分词。美妆评论区里有大量低质量或非真实反馈的内容,比如“此用户未填写评价内容”“好评”“物流很快”这类默认好评模板,还有不少引流广告、抽奖口令、复制粘贴的凑单评价。
我常用的清洗逻辑包含这几步:
python复制import pandas as pd
def clean_text(text):
if not isinstance(text, str):
return ""
# 去掉HTML标签和URL
text = re.sub(r"<.*?>", "", text)
text = re.sub(r"https?://\S+", "", text)
# 去掉表情符号和特殊符号,保留中文、英文、数字
text = re.sub(r"[^\u4e00-\u9fa5a-zA-Z0-9,。!?、;:%#&]", "", text)
# 去除默认好评和纯无意义文本
if len(text) < 4:
return ""
return text.strip()
df["clean_content"] = df["content"].apply(clean_text)
df = df[df["clean_content"] != ""]
df = df.drop_duplicates(subset=["comment_id"], keep="first")
长度过滤非常关键。很多默认好评只有几个字,对分析没有价值。但在过滤前要注意,有些高价值短评是“搓泥”“拔干”这样明确的负面词,我会额外保留这类关键词,通过自定义词表把它们从“过短文本”里捞回来。
4.2 让 jieba 学会说“美妆话”
通用分词器在美妆评论上很容易翻车。比如“卡粉”可能被拆成“卡”和“粉”,“闷痘”可能只识别出“痘”,“敏感肌”可能被拆得七零八落。解决方式是给 jieba 加一份自定义词典。
python复制import jieba
custom_words = [
"闷痘", "卡粉", "搓泥", "拔干", "敏感肌", "油皮", "干皮", "混油皮",
"混干皮", "痘肌", "烟酰胺", "水杨酸", "玻色因", "早C晚A", "显白",
"氧化", "黄气", "不黏腻", "假白", "闷痘", "爆皮", "刺痛感", "泛红",
]
for word in custom_words:
jieba.add_word(word)
不要忽视停用词。评论里大量出现的“可以”“还是”“感觉”“这个”“东西”“真的”等词,如果不剔除会严重干扰词频统计。建议把中文停用词表和电商场景停用词合并,比如“宝贝”“收到”“快递”“包装”这类词,在口碑分析里通常不是你最关心的维度。
一个补充做法是统计分词词频时用词性过滤。比如只保留形容词、名词、动词中的高频词,能更容易看到用户真正在描述什么。这个在 jieba.posseg 里可以做到,但对初学者来说,先做好自定义词典和停用词就够用了。
4.3 情感分析:不能用默认模型直接交差
我见过很多项目直接用 SnowNLP 跑一遍情感分数,然后画一个饼图说“正向评价占 80%”,这种做法在美妆评论里会有明显失真。
SnowNLP 默认训练语料偏向网络购物样本,跟美妆评论有一定相关性,但会遇到两类问题:一是“味道很好闻,可惜不持久”这种转折句,模型很可能给出中性或积极分数,实际用户明显是不满意;二是“这个价格能买到这种使用感已经很惊喜了”这种看似挑剔实为夸奖的长句,模型也容易判断错。
我的建议是搭一套“词典规则 + 模型”的混合方案,这也是我在项目中用得最稳的方式。
python复制def sentiment_score(text):
score = 0
words = jieba.lcut(text)
for i, word in enumerate(words):
if word in pos_words:
weight = 1.0
if i > 0 and words[i - 1] in degree_words:
weight = degree_words[words[i - 1]]
score += weight * pos_words[word]
elif word in neg_words:
weight = 1.0
if i > 0 and words[i - 1] in degree_words:
weight = degree_words[words[i - 1]]
score += weight * neg_words[word]
# 转折词处理:后半句权重放大
for conj in ["但是", "可是", "不过", "然而", "可惜"]:
if conj in text:
pos = text.find(conj)
after_part = text[pos:pos + 30]
after_score = sum(
pos_words.get(w, 0) if w in pos_words else neg_words.get(w, 0)
for w in jieba.lcut(after_part)
)
score = score * 0.3 + after_score * 1.5
break
return score
这里的 pos_words 和 neg_words 是自定义情感词典。词典不一定要很大,把美妆评论区最强的正面词和负面词覆盖到即可。比如“好用”“温和”“显白”“回购”“不刺激”“清爽”放正面;“搓泥”“卡粉”“闷痘”“拔干”“刺痛”“假白”放负面。程度副词的作用是让“非常温和”和“有点温和”拉开差距。
值得留意的是,“不闷痘”“不搓泥”这类“否定词 + 负面词”结构其实是在表扬。所以我不会直接对“不”后面的负面词减分,而是构造一个规则:当负面词前出现“不”“没”“无”时,认为它表达的是“避免了这个负面问题”,情感倾向转正。
情感词典不是越全越好,项目里能解释主要现象就足够了。如果想更精细,也可以在大模型 API 上对每条评论做 LLM 分类,让模型输出“肤质/质地/价格/包装/气味”的多标签正负面。效果很好,但需要成本和隐私权衡。离线方式的优势在于可复现、成本低、不依赖外部接口,用于做课程设计和个人分析已经够了。
4.4 从评论中找出用户真正关心的主题
有了情感分数后,不要停留在“这个产品 70% 好评”这种粗粒度结论上。更有效的操作是“按主题切分情感”。
我的经验做法是:把评论按差评关键词拆成几组,比如“质地组”包含搓泥、粘腻、油腻、清爽等词;“肤感组”包含刺痛、泛红、闷痘等词;“颜色组”包含假白、氧化、显白、暗沉等词。然后分别统计每个组的负面评论占比。
这种方式能让分析结论非常有说服力。举个例子,一款粉底液整体评分 4.6,看起来不错,但把差评按主题拆开后发现,关于“暗沉”“氧化”的评论占了差评的 60%,你就可以得出“用户抱怨焦点主要在持妆后的暗沉问题”这个结论,而不仅仅是“有人不喜欢它”。
对新手来说,判断特征词不一定非得上 LDA 主题模型。在样本量几千条这个尺度,用高频词 + 业务自定义标签的方式反而更稳定、可解释性更强。LDA 看起来高端,但主题数需要调,结果也可能出现与常识不符的混杂主题。
5. 可视化与结论输出
5.1 词云图怎么生成才不出戏
很多项目的词云图做得漂亮但细看没有信息量。问题在于直接把原始评论丢给了 WordCloud。正确的做法是:分词 -> 过滤停用词 -> 统计高频词 -> 再喂给 WordCloud。
python复制from wordcloud import WordCloud
word_freq = {}
for word in all_words:
if len(word) < 2 or word in stop_words:
continue
word_freq[word] = word_freq.get(word, 0) + 1
wc = WordCloud(
font_path="msyh.ttc", # 中文必须指定字体,否则全是方块
width=800,
height=600,
max_words=200,
background_color="white",
)
wc.generate_from_frequencies(word_freq)
wc.to_file("wordcloud.png")
中文字体缺失是词云图最常见的坑。Windows 系统一般可以指定路径 C:/Windows/Fonts/msyh.ttc,如果是在 Linux 服务器上运行,需要先安装中文字体,否则词云里全是豆腐块。另外生成词云前最好用词频字典而不是直接传字符串,这样能保证每个词出现的权重和真实语料一致。
5.2 用图表讲清楚“谁在夸、谁在骂、骂什么”
我在项目里一般配置三类图表:
- 横向柱状图:不同产品/色号的平均评分和好评率对比。
- 折线图:评论数量或负面情感随发布时间的变化,能看出产品有没有出现集中质量波动。
- 分组柱状图:“肤质 + 差评主题”的交叉统计,比如油皮用户吐槽控油,干皮用户吐槽拔干。
这些用 pyecharts 可以快速实现,交互式 HTML 在汇报时很加分。如果只是自用记录,matplotlib 就够。
有一个细节值得强调:可视化不是要把所有维度都画出来,而是只画能支撑你核心结论的图。比如你做的是 A 品牌和 B 品牌的竞品口碑对比,那就要把“好评率对比”“差评主题对比”“肤质情感对比”在一页报告里串成一条叙事线。图表是为了让人更容易接受结论,不是堆砌工作量。
5.3 分析报告的结构建议
报告可以按“结论先行”的方式来组织:
- 一句话结论:这个产品/品牌在哪些方面口碑好,哪些方面是用户吐槽点。
- 证据一:样本构成和整体情感分布。
- 证据二:各维度的好评率/差评主题分解。
- 证据三:代表性评论引用,让观点落到具体用户声音。
- 建议:如果我是产品经理,下一步该优化什么。
这种结构无论你是在做答辩还是给业务方汇报,都远比“数据量很大、经过了复杂清洗”更有说服力。
6. 高频问题与实操心得
6.1 高频故障速查表
| 问题 | 现象 | 排查方向 | 解决建议 |
|---|---|---|---|
| CSV 用 Excel 打开乱码 | 中文变成乱码 | 保存编码 | 使用 encoding=“utf-8-sig” |
| 请求几分钟后返回异常 | 连续出现超时/验证码 | 请求频率过高 | 降低频率,增加随机延时 |
| 页数抓不全 | 后面几页数据重复或为空 | 分页参数或排序机制 | 检查是否有游标分页,落库时按 ID 去重 |
| 评论里混进大量默认好评 | 分析结果一片“正向好评” | 清洗规则不够 | 加入模板评论过滤关键词列表 |
| 情感分析结果与主观感受不符 | 部分明显差评被判为好评 | 模型通用性不足 | 增加自定义词典与转折规则 |
| 词云中文变方块 | 图里全是方框 | 缺少中文字体 | 指定 font_path |
| 接口参数每次请求都不同 | 直接请求无法获取数据 | JS 生成签名 | 评估合规风险,改用授权数据服务 |
表里的每一条我都实际遇到过,尤其是 CSV 编码和请求过频的问题,几乎每次都会发生。把这两条处理规则记进你的脚手架代码里,能省不少返工时间。
6.2 避坑心得:别把“数据量”当成项目成果
很多项目汇报会写“采集了 10 万条评论”,听起来很猛,但如果分析只能停留在“好评多、差评少”这种层面,价值其实有限。
我的体会是,做这类数据分析项目的真正成果是你能不能用 100 条高质量差评讲清楚问题,而不是你拥有多少 TB 原始数据。我宁可选择 2000 条经过仔细清洗、标注了肤质维度的评论做深挖,也不想面对 10 万条相互重复、含义不明的模板文本。
另外,保存数据时我强烈建议你保留每个阶段的产物。raw 目录放原始 JSON/CSV,clean 目录放清洗后的结构化表格,result 目录放图表和报告。这样可以回溯问题,也能防止你某次清洗规则写错以后,被迫重新爬取数据。
6.3 项目还能往哪些方向扩展
这个项目做完以后,扩展价值很大。比如:
- 把单一产品扩展成竞品对比,做“A / B/C 品牌同价位口碑差异”;
- 引入追评时间线,分析用户使用两周后的持久度、氧化等长周期评价;
- 把差评关键词做成监控报表,帮助店铺及时响应质量危机;
- 结合商品价格和成分表,探索不同成分与肤质评价之间的关系。
如果以后你想往数据工程或数据科学方向发展,也可以把处理脚本重构成自动化管道,用定时任务调度。这样这个项目就不只是期末作业,它会成为你个人作品集里一个能讲出完整业务逻辑的案例,也让我们看到,真正的技术不是模仿接口测试,而是通过自己的思考把无序信息提炼成对业务有真实价值的结论。
