1. 项目概述从数据到洞察的实战路径最近在做一个内容分析相关的项目需要了解用户对特定话题的真实反馈。小红书作为国内领先的生活方式分享平台其评论区蕴藏着大量鲜活、真实的用户观点是进行市场研究和用户洞察的绝佳数据源。但直接手动收集不仅效率低下而且难以进行量化分析。于是我决定用Python写一个自动化工具来完成“爬取小红书评论并进行情感分析”这个任务。这听起来像是一个标准的爬虫数据分析组合拳但在实际操作中你会发现从反爬策略应对到中文文本情感分析的准确性每一步都有不少细节需要打磨。这个项目本质上是一个端到端的数据管道从网页中获取原始评论数据经过清洗和预处理最后运用自然语言处理技术判断每条评论的情感倾向正面、负面或中性。它非常适合对Python爬虫和文本分析感兴趣的朋友无论是用于学术研究、品牌舆情监控还是个人兴趣探索都能提供一个完整的、可复现的实战案例。接下来我将拆解整个流程分享从环境搭建、爬虫编写、反爬绕过、数据清洗到模型选型和结果分析的全过程以及我踩过的那些坑和总结出的有效经验。2. 项目核心思路与技术选型在动手写代码之前理清整体架构和每个环节的技术选型至关重要。一个鲁棒的系统不是一堆库的简单堆砌而是基于需求和约束的合理设计。2.1 整体流程设计整个项目可以清晰地划分为四个主要阶段数据获取层模拟浏览器访问小红书笔记页面定位并提取评论数据处理分页加载。数据持久化层将爬取到的原始数据评论内容、用户信息、时间等结构化地存储起来通常选择CSV或数据库。数据预处理层对评论文本进行清洗去噪、分词、标准化为后续分析做准备。这是影响情感分析准确性的关键。情感分析层应用情感分析模型或词典对每条处理后的评论进行情感极性打分和分类。这个流程形成了一个闭环的数据流水线后续如果要监控某个笔记的评论变化只需定期执行数据获取和后续分析即可。2.2 关键技术栈选型与考量爬虫部分Requests 动态渲染处理最初我尝试用经典的requests库直接发送HTTP请求但很快发现小红书对核心数据如评论采用了异步加载直接请求HTML页面只能得到骨架没有内容。因此需要能执行JavaScript的动态爬取方案。备选方案一Selenium。完全模拟浏览器行为能轻松获取渲染后的页面。但其缺点是速度慢、资源占用高适合需要复杂交互或应对高强度反爬的场景对于本项目略显“重”。备选方案二Playwright。新一代浏览器自动化工具比Selenium更快速、API更现代。它同样能完美解决动态渲染问题。备选方案三逆向工程接口。通过浏览器开发者工具F5分析网络请求直接找到加载评论的API接口然后用requests模拟调用。这是效率最高的方式但需要一定的逆向分析能力且接口可能变更。实操心得对于小红书评论我推荐“逆向工程接口为主Playwright为辅”的策略。大部分评论数据可以通过分析XHR/Fetch请求找到JSON格式的接口直接爬取效率极高。但对于一些特别复杂的页面或需要处理登录状态的情况可以备用Playwright。本项目将重点讲解接口逆向的方式因为这是目前最稳定和高效的方法。数据处理与分析部分Pandas Jieba SnowNLP/TransformersPandas数据处理的瑞士军刀用于加载、清洗、筛选和保存结构化的评论数据无可替代。Jieba优秀的中文分词工具虽然小红书评论口语化强但分词是文本向量化的基础步骤。情感分析模型这是核心选择点。SnowNLP一个纯Python的中文自然语言处理库其情感分析功能基于朴素贝叶斯算法训练。优点是简单易用、无需配置环境缺点是模型较旧对网络新词、特定领域如美妆、穿搭的评论泛化能力可能一般。百度AI、腾讯NLP等云API提供高精度的情感分析服务但通常有调用次数限制和费用。基于Transformer的预训练模型如BERT目前最先进的方案例如transformers库中的bert-base-chinese模型可以在下游任务包括情感分析上微调达到极高的准确率。但需要一定的深度学习知识和计算资源。情感词典如知网Hownet、BosonNLP通过匹配情感词和程度副词来计算情感值。规则简单、解释性强但需要维护词典且无法理解上下文和反讽。我的选择与理由为了平衡准确性、开发难度和可复现性我决定采用“SnowNLP进行基线分析辅以自定义词典修正”的策略。SnowNLP能快速给出一个基线结果对于大多数普通评论是有效的。同时我会构建一个针对小红书语境充满“绝了”、“踩雷”、“YYDS”等词汇的补充情感词典对SnowNLP的结果进行后处理修正这在实践中能显著提升准确率。如果追求极致效果可以后续尝试微调BERT模型。3. 爬虫核心逆向工程小红书评论接口这是项目中最具技术挑战性的一环。我们的目标不是下载整个网页而是找到小红书服务器直接提供评论数据的“后门”。3.1 环境准备与工具首先确保你的Python环境已经安装了必要的库。我们将主要使用requests来处理HTTP请求用json来解析数据用pandas来保存数据。pip install requests pandas接下来你需要一个现代浏览器Chrome/Firefox和它的开发者工具。我们将用它来“侦查”小红书是如何加载评论的。3.2 定位评论数据接口打开目标笔记在浏览器中打开一篇小红书笔记例如https://www.xiaohongshu.com/explore/笔记ID。打开开发者工具按F12切换到“网络”(Network)选项卡。筛选请求在筛选栏输入fetch或xhr以过滤出可能的数据请求。触发评论加载滚动到笔记下方评论区或者点击“展开更多评论”。此时网络面板会出现新的请求。寻找目标请求仔细观察新出现的请求寻找包含comment、note、interaction等关键词的请求。请求的“响应”(Response)选项卡里很可能就是结构化的JSON数据里面包含了评论列表、用户信息等。这个请求的“标头”(Headers)里的“请求网址”(Request URL)就是我们需要的接口地址。注意事项小红书的接口地址和参数格式可能会更新。我分析时发现一个典型的评论接口模式是https://edith.xiaohongshu.com/api/sns/web/v2/comment/page?note_idxxx...。关键点在于找到note_id笔记ID和可能的cursor用于分页的游标参数。note_id通常可以从笔记页面的URL或页面源代码中找到。3.3 构建爬虫请求找到接口后我们需要用Python的requests库来模拟这个请求。关键在于复制浏览器发送请求时的所有必要信息否则服务器会拒绝。import requests import json import pandas as pd import time def fetch_comments_by_note_id(note_id, max_pages10): 根据笔记ID爬取评论 :param note_id: 小红书笔记ID :param max_pages: 最大爬取页数 :return: 评论数据列表 comments_list [] # 初始游标第一页通常为空或特定值 cursor headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36, Referer: fhttps://www.xiaohongshu.com/explore/{note_id}, # 以下Cookie和x-sign需要根据实际情况获取这是反爬关键 # Cookie: 你的Cookie, # x-sign: 生成的签名, } for page in range(1, max_pages 1): # 构建请求参数参数名需根据实际接口确定 params { note_id: note_id, cursor: cursor, top_comment_id: , # 可能不需要 image_formats: jpg,webp,avif, num: 10, # 每页数量 } # 接口URL需替换为你找到的实际地址 url https://edith.xiaohongshu.com/api/sns/web/v2/comment/page try: response requests.get(url, headersheaders, paramsparams, timeout10) response.raise_for_status() # 检查请求是否成功 data response.json() # 解析评论数据结构需根据实际响应调整 if data.get(success): comments data.get(data, {}).get(comments, []) for comment in comments: comment_info { comment_id: comment.get(id), content: comment.get(content), user_name: comment.get(user_info, {}).get(nickname), user_id: comment.get(user_info, {}).get(user_id), likes: comment.get(like_count, 0), time: comment.get(create_time), sub_comment_count: comment.get(sub_comment_count, 0) } comments_list.append(comment_info) # 更新游标用于请求下一页 cursor data.get(data, {}).get(cursor) if not cursor: # 如果没有游标说明没有更多数据 print(f第{page}页爬取完毕共{len(comments)}条评论已无更多数据。) break print(f成功爬取第{page}页共{len(comments)}条评论。) time.sleep(1.5) # 礼貌性延迟避免请求过快 else: print(f请求失败: {data.get(msg)}) break except requests.exceptions.RequestException as e: print(f网络请求异常: {e}) break except json.JSONDecodeError as e: print(fJSON解析异常: {e}) break return comments_list # 使用示例 if __name__ __main__: note_id 填入你要爬的笔记ID comments fetch_comments_by_note_id(note_id, max_pages5) print(f总共爬取到 {len(comments)} 条评论)3.4 应对反爬机制与关键难点直接运行上面的代码很可能失败因为小红书有较强的反爬措施。主要难点在于headers里的Cookie和x-sign或其他签名参数。Cookie这是你的身份会话标识。短期内你可以从浏览器开发者工具中找到那个评论接口的请求复制其Request Headers中的整个Cookie字符串粘贴到代码的headers里。但Cookie会过期。x-sign这是一个动态生成的签名用于验证请求的合法性。它是反爬的核心通常由请求URL、参数、时间戳等通过特定算法可能是MD5、SHA256等生成。逆向这个算法是最大的挑战。实操心得与解决方案短期/小规模手动从浏览器复制Cookie和签名如果有到代码中可以用于临时测试或少量数据爬取。注意签名可能有时效性。中长期/自动化需要深入研究JavaScript代码找到生成签名的函数并用Python重写即“扣代码”。这需要较强的JS逆向能力。可以在开发者工具的“源代码”(Sources)中搜索sign、x-sign等关键词定位相关代码。替代方案如果逆向难度太大可以考虑使用Playwright这类工具。它通过控制真实浏览器来获取完全渲染的页面然后直接解析页面HTML中的评论数据。虽然慢但能绕过复杂的签名逻辑。下面是一个Playwright的简化示例from playwright.sync_api import sync_playwright import time def fetch_comments_with_playwright(note_url): with sync_playwright() as p: # 启动浏览器headlessFalse可以看到浏览器界面 browser p.chromium.launch(headlessFalse) context browser.new_context() page context.new_page() page.goto(note_url) time.sleep(3) # 等待页面加载 # 模拟滚动或点击加载更多评论 for _ in range(5): # 尝试滚动5次 page.evaluate(window.scrollTo(0, document.body.scrollHeight)) time.sleep(2) # 现在页面已包含评论直接通过选择器提取 # 需要分析小红书评论在HTML中的结构例如 # comment_elements page.query_selector_all(.comment-item .content) # comments [el.inner_text() for el in comment_elements] # 注意实际选择器需要你通过检查元素来确定 # 这里只是一个示例可能不准确 comment_elements page.locator(div[class*comment]).all() comments [] for el in comment_elements: # 更精确地提取内容 content el.locator(span[class*content]).first if content: comments.append(content.inner_text()) browser.close() return comments4. 数据清洗与预处理为分析打好基础爬取到的原始评论数据是“脏”的包含各种噪声必须经过清洗才能用于情感分析。这一步直接决定了后续分析结果的质量。4.1 原始数据加载与审视假设我们将爬取的数据保存为CSV文件。import pandas as pd # 假设我们已经将comments_list保存为CSV df pd.read_csv(xiaohongshu_comments.csv) print(df.head()) print(f数据形状: {df.shape}) print(df[content].head(10)) # 查看原始评论内容你会看到文本里可能包含无关符号大量的表情符号、URL、用户。冗余信息“回复 某某用户” 这样的前缀。特殊格式HTML实体、换行符\n。无效内容纯表情、纯标点、系统提示如“该评论已删除”。4.2 系统化的清洗流程我设计了一个多步骤的清洗管道import re import jieba import zhconv # 用于简繁转换 def clean_comment_text(text): if not isinstance(text, str): return # 1. 去除换行符、多余空格 text re.sub(r\s, , text) # 2. 去除常见的回复前缀 (例如回复 用户名) text re.sub(r回复\s*[\w\u4e00-\u9fa5]\s*, , text) # 3. 去除URL链接 text re.sub(rhttps?://\S, , text) # 4. 去除提及 text re.sub(r[\w\u4e00-\u9fa5], , text) # 5. 去除大部分表情符号一种常见模式 # 更复杂的表情可能需要更专业的库如emoji text re.sub(r\[.*?\], , text) # 去除方括号表情 # 简单去除一些常见符号实际中表情很复杂 text re.sub(r[^\w\u4e00-\u9fa5。、“”‘’【】《》…\-\.\,!?;:\\\(\)\[\]], , text) # 6. 繁体转简体 (如果数据源有繁体) text zhconv.convert(text, zh-cn) # 7. 去除首尾空格 text text.strip() return text def preprocess_dataframe(df): 对DataFrame进行批量清洗和预处理 df_clean df.copy() # 应用文本清洗 df_clean[content_clean] df_clean[content].apply(clean_comment_text) # 去除清洗后为空或过短的评论可能是无效评论 df_clean df_clean[df_clean[content_clean].str.len() 1] # 分词 (用于后续可能的词云或更复杂的分析) # 注意情感分析不一定需要分词SnowNLP内部会处理 # 这里分词是为了展示和可能的自定义词典匹配 df_clean[content_cut] df_clean[content_clean].apply(lambda x: .join(jieba.lcut(x))) # 重置索引 df_clean.reset_index(dropTrue, inplaceTrue) return df_clean # 应用清洗 df_clean preprocess_dataframe(df) print(f清洗后数据形状: {df_clean.shape}) print(df_clean[[content, content_clean, content_cut]].head())4.3 预处理中的关键细节分词准确性jieba默认词典对网络新词如“绝绝子”、“栓Q”识别不好。可以通过jieba.add_word()添加自定义词或者加载用户词典。停用词处理中文中有很多无实际意义的词的、了、是等。去除停用词可以降低噪声但有时也可能影响情感表达如“不是很好”中的“不”。对于情感分析需要谨慎处理否定词。我建议先不做通用停用词过滤而是构建一个针对情感分析的特殊停用词表。数据去重有时同一用户可能发布重复或高度相似的评论。可以使用df.drop_duplicates(subset[content_clean])进行去重但要注意可能误伤正常重复。踩坑记录最初我激进地过滤了所有非中文字符和标点结果把“YYDS”永远的神这类网络情感强烈的缩写也过滤掉了导致一些明显正面的评论被误判。后来我修改了正则表达式保留了常见的英文情感缩写和必要的标点。5. 情感分析模型实战从SnowNLP到自定义优化数据准备好后就进入核心的情感分析阶段。我将演示如何使用SnowNLP进行基线分析并介绍如何通过自定义词典来优化结果。5.1 使用SnowNLP进行基线情感分析SnowNLP的使用非常简单它直接对整句文本进行情感打分0到1之间越接近1越正面。from snownlp import SnowNLP import pandas as pd def analyze_sentiment_snownlp(text): 使用SnowNLP分析单条文本情感 :return: (sentiment_score, sentiment_label) try: s SnowNLP(text) score s.sentiments # 情感极性得分0-1 # 根据阈值划分情感类别 (阈值可根据情况调整) if score 0.6: label 正面 elif score 0.4: label 负面 else: label 中性 return score, label except Exception as e: print(f分析文本时出错: {text[:50]}... 错误: {e}) return None, 未知 # 对清洗后的评论应用情感分析 print(开始进行SnowNLP情感分析...) df_clean[[sentiment_score, sentiment_label]] df_clean[content_clean].apply( lambda x: pd.Series(analyze_sentiment_snownlp(x)) ) # 查看分析结果分布 sentiment_dist df_clean[sentiment_label].value_counts() print(情感分布:) print(sentiment_dist) print(f\n正面比例: {sentiment_dist.get(\正面\, 0) / len(df_clean):.2%}) print(f负面比例: {sentiment_dist.get(\负面\, 0) / len(df_clean):.2%}) print(f中性比例: {sentiment_dist.get(\中性\, 0) / len(df_clean):.2%}) # 查看一些示例 print(\n--- 正面评论示例 ---) print(df_clean[df_clean[sentiment_label] 正面][[content_clean, sentiment_score]].head(3).to_string(indexFalse)) print(\n--- 负面评论示例 ---) print(df_clean[df_clean[sentiment_label] 负面][[content_clean, sentiment_score]].head(3).to_string(indexFalse))5.2 SnowNLP的局限性分析与案例运行后你可能会发现一些明显的误判。例如“这个东西也太难用了吧踩雷了”明显负面 - SnowNLP可能因为“太”等程度副词或模型训练语料问题给出一个不低的分数。“不是很好不推荐。”双重否定整体负面 - SnowNLP可能无法很好处理复杂否定。“YYDS给我冲”强烈正面 - 网络新词SnowNLP基础模型可能无法识别其强烈情感。这是因为SnowNLP的通用模型是在较早、较通用的语料上训练的对垂直领域如美妆、美食和网络新语的适应性有限。5.3 构建自定义情感词典进行优化为了提升准确率我们可以构建一个领域情感词典来修正SnowNLP的结果。思路是如果评论中出现我们词典中的强情感词就覆盖或调整SnowNLP的分数。# 定义自定义情感词典 (可根据小红书常见词汇扩展) custom_positive_words { 绝绝子: 0.9, YYDS: 0.95, 宝藏: 0.85, 惊艳: 0.88, 回购: 0.8, 冲: 0.75, 安利: 0.8, 好用: 0.7, 好看: 0.7, 美味: 0.75, 神仙: 0.9, 必入: 0.85, 爱了: 0.85, 太可了: 0.88 } custom_negative_words { 踩雷: -0.9, 避坑: -0.85, 拔草: -0.8, 垃圾: -0.95, 太难用: -0.88, 失望: -0.75, 不值: -0.8, 吐槽: -0.7, 黑榜: -0.85, 千万别: -0.9, 翻车: -0.85, 雷品: -0.8 } # 程度副词权重 degree_words { 极其: 1.8, 超: 1.7, 太: 1.6, 非常: 1.5, 特别: 1.5, 有点: 0.7, 稍微: 0.6, 不太: 0.5, 不: -1.0 # “不”用于反转情感 } def adjust_with_custom_dict(text, original_score): 使用自定义词典调整情感得分 words jieba.lcut(text) adjusted_score original_score custom_effect 0 negation 1 # 否定词标志 i 0 while i len(words): word words[i] # 检查程度副词 if word in degree_words: degree_weight degree_words[word] # 查看程度副词后面是否有情感词 if i1 len(words) and (words[i1] in custom_positive_words or words[i1] in custom_negative_words): # 如果是“不”等否定词则反转后续情感 if word 不: negation -1 # 否则程度副词会放大后续情感词的影响 # 这里简化处理将程度权重暂存在遇到情感词时应用 pass # 检查情感词 elif word in custom_positive_words: effect custom_positive_words[word] * negation custom_effect effect elif word in custom_negative_words: effect custom_negative_words[word] * negation custom_effect effect # 遇到情感词后重置否定标志简化逻辑 negation 1 i 1 # 结合原始得分和自定义词典影响这里采用加权平均 if custom_effect ! 0: # 如果自定义词典检测到强情感词则大幅倾向于词典判断 final_score original_score * 0.3 (0.5 custom_effect * 0.5) * 0.7 final_score max(0.0, min(1.0, final_score)) # 限制在0-1 else: final_score original_score return final_score def analyze_sentiment_enhanced(text): 增强版情感分析SnowNLP基线 自定义词典修正 try: s SnowNLP(text) base_score s.sentiments adjusted_score adjust_with_custom_dict(text, base_score) if adjusted_score 0.6: label 正面 elif adjusted_score 0.4: label 负面 else: label 中性 return adjusted_score, label except Exception as e: return None, 未知 # 应用增强版分析 print(\n开始进行增强版情感分析...) df_clean[[sentiment_score_enhanced, sentiment_label_enhanced]] df_clean[content_clean].apply( lambda x: pd.Series(analyze_sentiment_enhanced(x)) ) # 对比优化前后结果 comparison pd.crosstab(df_clean[sentiment_label], df_clean[sentiment_label_enhanced]) print(\n优化前后情感标签对比行原始列增强后:) print(comparison) # 找出被修正的评论 changed df_clean[df_clean[sentiment_label] ! df_clean[sentiment_label_enhanced]] if not changed.empty: print(f\n共有 {len(changed)} 条评论的情感标签被修正。) print(changed[[content_clean, sentiment_label, sentiment_label_enhanced]].head(10).to_string(indexFalse))这个自定义词典方法虽然简单但能有效纠正一部分明显的误判特别是对于包含领域特定情感词汇的评论。你可以通过不断扩充和调整custom_positive_words和custom_negative_words来优化它。6. 结果可视化与深入洞察数据分析的最后一环是将结果直观地呈现出来并从中提炼出有价值的洞察。6.1 基础可视化分布与趋势使用matplotlib或seaborn进行绘图。import matplotlib.pyplot as plt import seaborn as sns # 设置中文字体 (根据你的系统调整) plt.rcParams[font.sans-serif] [SimHei, DejaVu Sans] plt.rcParams[axes.unicode_minus] False # 1. 情感分布饼图 fig, axes plt.subplots(1, 2, figsize(14, 6)) sentiment_counts df_clean[sentiment_label_enhanced].value_counts() axes[0].pie(sentiment_counts.values, labelssentiment_counts.index, autopct%1.1f%%, startangle90) axes[0].set_title(评论情感分布增强版) # 2. 情感得分分布直方图 axes[1].hist(df_clean[sentiment_score_enhanced].dropna(), bins20, edgecolorblack, alpha0.7) axes[1].axvline(x0.4, colorred, linestyle--, alpha0.5, label负面阈值) axes[1].axvline(x0.6, colorgreen, linestyle--, alpha0.5, label正面阈值) axes[1].set_xlabel(情感得分) axes[1].set_ylabel(评论数量) axes[1].set_title(情感得分分布直方图) axes[1].legend() plt.tight_layout() plt.show() # 3. 按时间的情感趋势如果数据有时间戳 if time in df_clean.columns: df_clean[time] pd.to_datetime(df_clean[time], unitms, errorscoerce) # 假设时间戳是毫秒 df_clean[date] df_clean[time].dt.date daily_sentiment df_clean.groupby(date)[sentiment_score_enhanced].mean() plt.figure(figsize(12, 6)) plt.plot(daily_sentiment.index, daily_sentiment.values, markero) plt.axhline(y0.5, colorgrey, linestyle--, alpha0.5) plt.fill_between(daily_sentiment.index, 0.6, 1, alpha0.1, colorgreen, label正面区域) plt.fill_between(daily_sentiment.index, 0, 0.4, alpha0.1, colorred, label负面区域) plt.xlabel(日期) plt.ylabel(平均情感得分) plt.title(评论情感趋势变化) plt.xticks(rotation45) plt.legend() plt.tight_layout() plt.show()6.2 文本洞察高频词与典型观点除了数值分析文本本身也富含信息。我们可以通过词云和关键词提取来了解讨论焦点。from wordcloud import WordCloud from collections import Counter # 生成词云基于分词后的结果 all_words .join(df_clean[content_cut].dropna().tolist()) wordcloud WordCloud( font_pathsimhei.ttf, # 指定中文字体路径 width800, height400, background_colorwhite, max_words100 ).generate(all_words) plt.figure(figsize(12, 6)) plt.imshow(wordcloud, interpolationbilinear) plt.axis(off) plt.title(评论高频词云) plt.show() # 分别查看正面和负面评论的高频词 positive_words .join(df_clean[df_clean[sentiment_label_enhanced]正面][content_cut].dropna().tolist()) negative_words .join(df_clean[df_clean[sentiment_label_enhanced]负面][content_cut].dropna().tolist()) pos_word_freq Counter(positive_words.split()) neg_word_freq Counter(negative_words.split()) print(正面评论Top10高频词:) for word, freq in pos_word_freq.most_common(10): print(f{word}: {freq}) print(\n负面评论Top10高频词:) for word, freq in neg_word_freq.most_common(10): print(f{word}: {freq})6.3 生成分析报告最后可以将关键发现总结成一份简单的文本报告。def generate_report(df): total len(df) pos len(df[df[sentiment_label_enhanced]正面]) neg len(df[df[sentiment_label_enhanced]负面]) neu len(df[df[sentiment_label_enhanced]中性]) avg_score df[sentiment_score_enhanced].mean() report f 小红书评论情感分析报告 分析评论总数{total} 条 情感分布 - 正面评论{pos} 条 ({pos/total:.1%}) - 负面评论{neg} 条 ({neg/total:.1%}) - 中性评论{neu} 条 ({neu/total:.1%}) 整体情感平均分{avg_score:.3f} (范围0-1越高越正面) 关键发现 1. 用户整体情绪偏向{积极 if avg_score 0.5 else 消极 if avg_score 0.5 else 中性}。 2. 正面评论中常见词汇{, .join([w for w, _ in Counter( .join(df[df[sentiment_label_enhanced]正面][content_cut].dropna().tolist()).split()).most_common(5)])} 3. 负面评论中常见词汇{, .join([w for w, _ in Counter( .join(df[df[sentiment_label_enhanced]负面][content_cut].dropna().tolist()).split()).most_common(5)])} return report print(generate_report(df_clean))7. 常见问题与排查技巧实录在实际操作中你几乎一定会遇到下面这些问题。这里是我总结的排查清单和解决方案。问题现象可能原因排查步骤与解决方案爬虫返回空数据或403错误1. 请求头Headers不完整或错误。2. Cookie过期。3. 接口参数如note_id,cursor格式错误。4. IP请求频率过高被限制。1.核对Headers用浏览器开发者工具仔细对比你的Python请求头与浏览器请求头是否一致特别是User-Agent,Referer,Cookie以及x-sign等签名参数。2.更新Cookie重新登录小红书从浏览器复制新的Cookie字符串。3.验证接口直接在浏览器地址栏尝试拼接接口URL和参数看是否能返回JSON数据。4.降低频率在请求间增加随机延迟如time.sleep(random.uniform(1, 3))或使用代理IP池。SnowNLP对所有评论都返回0.5左右的中性分数1. 文本预处理过度清除了所有情感词汇。2. 评论文本过短或无效。3. SnowNLP模型未能加载。1.检查清洗函数确保没有过滤掉关键的情感词如“好”、“坏”、“喜欢”、“讨厌”。可以先不对清洗后的文本进行分析看看原始文本的得分。2.检查输入打印几条评论的清洗前后文本确认内容是有意义的句子。3.验证安装尝试from snownlp import sentiment; sentiment.train(neg.txt, pos.txt)看是否报错但通常不需要重训练。情感分析结果明显错误如负面评论判为正面1. SnowNLP通用模型不适用于垂直领域或网络用语。2. 文本中存在反讽、双重否定等复杂句式。3. 阈值设置不合理。1.使用自定义词典如本文所述构建领域情感词库进行修正。2.尝试其他模型对于重要项目可以考虑使用基于BERT的微调模型准确率更高。3.调整分类阈值根据你对样本的人工标注调整正面/负面的阈值0.6和0.4。4.人工审核对于关键决策建议对模型结果进行一定比例的抽样人工复核。分词结果不理想网络新词被拆散Jieba默认词典未收录新词。1.添加自定义词典创建一个文本文件每行一个词格式如绝绝子 3 n然后使用jieba.load_userdict(my_dict.txt)加载。2.使用调整模式对于短文本可以尝试jieba.cut_for_search(text)。3.考虑其他分词器如pkuseg、THULAC等可能在某些领域表现更好。运行Playwright爬虫时浏览器无法启动或闪退1. 未安装浏览器驱动。2. 系统环境问题如缺少依赖。3. 端口冲突或浏览器实例未正确关闭。1.安装驱动运行playwright install chromium来安装所需的浏览器。2.以非无头模式启动设置headlessFalse查看具体错误信息。3.确保正确关闭使用with sync_playwright() as p:上下文管理器确保浏览器在异常后也能被关闭。检查是否有残留的浏览器进程。数据量较大时程序运行缓慢或内存不足1. 所有数据一次性加载到内存。2. 未使用批量处理或流式处理。3. 情感分析是CPU密集型任务。1.分块处理使用Pandas的chunksize参数读取大型CSV文件。2.考虑数据库对于海量数据将评论存入SQLite或MySQL分批查询分析。3.优化情感分析对于SnowNLP可以尝试将文本批量送入模型但提升有限。对于BERT等模型考虑使用GPU加速。4.异步爬虫对于爬虫部分可以考虑使用aiohttp进行异步请求大幅提升IO密集型任务的效率。最后再分享一个小技巧在启动一个长期运行的爬虫任务前最好先写一个小的“侦察脚本”只爬取1-2页数据并完整打印出请求的URL、Headers和返回的响应状态码、前几百个字符。这能帮你快速验证整个请求链路是否通畅参数是否正确避免浪费大量时间在无效的爬取上。数据处理和分析部分也一样先在小样本比如100条评论上跑通整个流程确保每个环节的输出都符合预期然后再扩展到全量数据。这种“小步快跑验证先行”的方法能帮你节省大量调试时间。