Python+MySQL构建热搜评论数据分析系统:从清洗到可视化完整实践

发布时间:2026/9/4 5:27:38
Python+MySQL构建热搜评论数据分析系统:从清洗到可视化完整实践 在实际的 Python 数据分析项目中直接从公开页面拿到“热搜词汇 用户评论”只是第一步。真正决定项目能不能通过面试、能不能被写进简历的是数据流设计采集源怎样约束、表结构怎样设计、文本噪声怎样清洗、情感判断用哪个口径、结论怎样用 MySQL 和可视化一层层算出来。这里以“泡泡玛特热搜评论数据分析”为例子整理一条适合学习的完整路径。它不要求你一次性写出高并发爬虫而是先跑通一条可扩展的数据分析链路。这个项目要解决的核心问题是某潮玩品牌的一条热搜关键词下面用户都在讨论什么、情绪如何、哪些词反复出现以及评论互动量与关键词排名之间是否存在可观察的关系。实现思路是 Python 负责采集、清洗和分析计算MySQL 负责稳定存储和可重复的聚合查询。数据以 Python 列表、JSON 文件或公开页面返回的数组为入口最终在 MySQL 中形成“热搜快照表”和“评论明细表”再用 SQL 完成分组统计最后用 pyecharts 输出可视化结论。1. 项目分析与简历价值定位1.1 先想清楚项目边界很多简历项目把注意力放在爬虫规模上例如“爬了 XX 万条评论”但面试官更关心的是原始数据长什么样哪些字段是有用的。重复数据和广告评论如何处理。情感判断是没有人工标注的还是拍脑袋做的。入库后为什么还要建索引。分析结论能不能用数据支撑。所以本项目把边界拆成四个环节数据采集获取指定关键词的公开热搜信息和公开评论内容。原始材料只保留需要分析的字段例如关键词、评论正文、昵称、点赞数、发布时间。数据清洗去重、补全空值、统一时间格式、过滤超短评论和无意义内容。数据存储在 MySQL 中按“一表一职责”设计数据表将结构化的评论数据和搜索热度数据合并到同一主题。数据分析和可视化使用 SQL 做聚合统计用 Python 生成词频、情感分布、互动趋势等图表。1.2 为什么要用 Python MySQLPython 的优势是文本处理。处理评论内容时要用到字符串清洗、中文分词、简单情感打分这些用 pandas 和 jieba 可以很快完成。MySQL 的优势则是查询稳定和可复现。项目做完后如果面试官问你“昨天统计的正面评论占比是多少”一条 SQL 就能重算不需要重新跑一遍 Python。这里要区分“学习环境”和“生产环境”。学习阶段完全可以先用本地 JSON 文件作为数据入口跑完整条分析链路后再考虑真实采集生产环境则需要加入请求频率控制、异常重试、数据库索引、日志和告警。1.3 简历项目描述建议简历上的项目名称建议写成“基于 Python MySQL 的潮玩品牌热搜评论数据分析系统”项目描述可以按三行展示数据层设计热搜快照表和评论明细表通过 Python 清洗评论中的重复数据、缺失值和异常时间格式实现结构化的入库。分析层使用 SQL 完成关键词维度、情感维度和互动维度的聚合统计使用 jieba 自定义词典提升品牌词分词准确率。表达层基于 pyecharts 输出词频图、情感分布图和互动趋势图形成可沉淀的榜单分析报告。不要写“七天精通”或者“一周拿 offer”这种无法验证的描述。项目面试时能解释清楚自己为什么这样设计表、为什么情感词典要人工补充远比标题好看更有价值。2. 环境准备与 MySQL 部署2.1 开发环境版本约定本项目适合在 Python 3.8 及以上版本运行。如果没有限定环境要求建议先固定一个自己电脑上能跑通的版本避免在版本问题上浪费时间。推荐使用 Python 3.10 或 3.11 这样的较新稳定版本依赖库的兼容性较好。组件推荐版本或方案用途Python3.10编写采集、清洗、分析代码MySQL8.0存储评论数据和热搜快照pymysql1.1.xPython 连接 MySQL 的驱动SQLAlchemy2.0.x配合 pandas 批量写入pandas2.0.x数据清洗和聚合jieba0.42.1中文分词pyecharts2.0.x图表可视化requests2.31.x公开数据源请求beautifulsoup44.12.x页面解析可选2.2 使用 Docker 启动 MySQL 开发库如果在 Windows 或 Mac 本地不想安装完整 MySQL 服务最省事的方案是使用 Docker。先确认本机已经安装 Docker然后执行下面的命令启动一个 MySQL 8.0 容器docker run -d \ --name popmart-mysql \ -e MYSQL_ROOT_PASSWORDroot123456 \ -e MYSQL_DATABASEpopmart_analysis \ -p 3306:3306 \ mysql:8.0 \ --default-authentication-pluginmysql_native_password参数说明--name指定容器名称方便重启和查看日志。MYSQL_ROOT_PASSWORD设置 root 密码。学习环境可以用简单密码生产环境必须使用强密码并限制访问来源。MYSQL_DATABASE创建容器时自动创建数据库。-p 3306:3306把宿主机的 3306 端口映射到容器的 3306 端口。--default-authentication-plugin为了兼容旧客户端MySQL 8.0 默认使用 caching_sha2_password有些 Python 驱动版本需要额外处理。使用这个参数可以避免认证插件问题但要注意该参数在 MySQL 8.4 之后可能不再推荐。启动后检查容器状态docker ps docker logs popmart-mysql如果看到ready for connections说明 MySQL 已启动。然后在本机用命令行验证连接mysql -h 127.0.0.1 -P 3306 -u root -proot1234562.3 安装 Python 依赖建议先创建独立虚拟环境避免污染系统环境python -m venv venv source venv/bin/activateWindows 环境下激活命令是venv\Scripts\activate在项目目录创建requirements.txt写入依赖pandas2.0.3 pymysql1.1.0 SQLAlchemy2.0.25 jieba0.42.1 pyecharts2.0.6 requests2.31.0 beautifulsoup44.12.3 lxml4.9.3然后执行pip install -r requirements.txt安装完成后可以先在 Python 中验证关键依赖是否可用import pandas as pd import pymysql import jieba import sqlalchemy print(pd.__version__) print(sqlalchemy.__version__)3. 数据库与表结构设计3.1 根据分析问题倒推数据表不要急着建表。先列出这个项目要回答的问题每个关键词下有多少条有效评论平均互动量是多少。同一时间窗口不同热搜词之间的讨论量差异。用户评论中出现了哪些核心词汇。正面、负面、中性评论的占比是多少。评论内容集中在哪些时间段。根据这些问题设计两张核心表热搜快照表hot_search_snapshot和评论明细表comment_detail。再设计一张可选的评论词表comment_word_term用于存放分词后产生的词汇及其频次。创建表的 SQL 如下CREATE DATABASE IF NOT EXISTS popmart_analysis DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; USE popmart_analysis; CREATE TABLE hot_search_snapshot ( id BIGINT PRIMARY KEY AUTO_INCREMENT, keyword VARCHAR(100) NOT NULL COMMENT 热搜关键词, platform VARCHAR(50) NOT NULL DEFAULT weibo COMMENT 数据来源平台, rank_no INT DEFAULT NULL COMMENT 当时排名没有则为空, heat_score DECIMAL(12, 4) DEFAULT NULL COMMENT 热度分数, captured_at DATETIME NOT NULL COMMENT 抓取时间 ) ENGINE InnoDB DEFAULT CHARSET utf8mb4; CREATE TABLE comment_detail ( id BIGINT PRIMARY KEY AUTO_INCREMENT, keyword VARCHAR(100) NOT NULL COMMENT 关联的热搜关键词, content TEXT NOT NULL COMMENT 评论原文, content_hash CHAR(32) NOT NULL COMMENT 评论内容的 md5用于去重, user_name VARCHAR(100) DEFAULT NULL COMMENT 发布用户昵称, like_count INT NOT NULL DEFAULT 0 COMMENT 点赞数, reply_count INT NOT NULL DEFAULT 0 COMMENT 回复数, comment_time DATETIME NULL COMMENT 评论发布时间, captured_at DATETIME NOT NULL COMMENT 采集入库时间, sentiment_label VARCHAR(20) DEFAULT NULL COMMENT 正面/中性/负面, sentiment_score DECIMAL(4, 2) DEFAULT NULL COMMENT 情感得分范围可约定为-1到1, UNIQUE KEY uk_content_hash (content_hash) ) ENGINE InnoDB DEFAULT CHARSET utf8mb4;这里有两个值得说明的点。第一content_hash字段是评论正文的唯一性判断凭据。直接给整条评论建立普通索引会导致索引体积偏大用 MD5 固定长度反而更可控。虽然 MD5 在安全场景中不够强但用于数据去重足够。第二DECIMAL用于热度分数和情感得分。评论点赞数、回复数这类计数使用INT但热度分数如果涉及浮点数比值应该用DECIMAL避免多次计算产生浮点误差。3.2 本地数据样例的组织方式为了先把链路跑通推荐准备一份 JSON 文件作为脱机数据源。文件内容模拟某个关键词的热搜快照和评论明细不依赖真实接口也能执行后续所有步骤。{ snapshot: [ { keyword: 泡泡玛特, platform: weibo, rank_no: 1, heat_score: 823456.0, captured_at: 2025-02-10 12:00:00 } ], comments: [ { keyword: 泡泡玛特, content: 这个新品配色好好看已经下单了。, user_name: 示例用户A, like_count: 52, reply_count: 3, comment_time: 2025-02-10 11:42:00 }, { keyword: 泡泡玛特, content: 盲盒价格越来越贵感觉热度是营销出来的。, user_name: 示例用户B, like_count: 18, reply_count: 1, comment_time: 2025-02-10 11:30:00 } ] }把样例 JSON 保存到data/sample_comments.json。后续读取时通过json.load得到一个 dict再用 pandas 转成 DataFrame。这样即使某一天采集代码因为页面改版不可用已经存在的 JSON 数据也不会影响分析流程。4. 数据获取与清洗实现4.1 先读取本地 JSON跑通主流程项目根目录建议这样组织popmart-analysis/ ├── data/ │ └── sample_comments.json ├── sql/ │ └── create_tables.sql ├── src/ │ ├── clean_data.py │ ├── load_to_mysql.py │ ├── analyze_sql.py │ └── visualize.py ├── requirements.txt └── README.md读取 JSON 并转成 DataFrame 的代码import json import pandas as pd with open(data/sample_comments.json, r, encodingutf-8) as f: raw_data json.load(f) comment_df pd.DataFrame(raw_data[comments]) snapshot_df pd.DataFrame(raw_data[snapshot]) print(comment_df.head()) print(snapshot_df.head())到这一步还不需要数据库。先把流程拆开确保每个环节只用 pandas 就能看到中间结果。4.2 评论清洗的关键操作从公开数据源拿到的评论通常会有以下噪声纯表情、纯标点或长度极短的评论。因网络原因重复插入的相同内容。评论时间为空或格式混乱。转发的格式带有“//用户名”。营销广告内容反复出现。清洗代码可以按阶段写import re import hashlib def clean_dedup(df: pd.DataFrame) - pd.DataFrame: 基础清洗去空、去重、时间标准化 df df.copy() df[content] df[content].astype(str).str.strip() # 去掉空字符串和长度小于 2 的评论 df df[df[content].str.len() 2] # 去掉明显的转发头 df[content] df[content].str.replace( r^//.*?:, , regexTrue ) # 生成 content_hash df[content_hash] df[content].apply( lambda x: hashlib.md5(x.encode(utf-8)).hexdigest() ) # 基于 hash 去重 df df.drop_duplicates(subset[content_hash], keepfirst) # 时间字段统一为 datetime df[comment_time] pd.to_datetime( df[comment_time], errorscoerce ) # 没有时间的评论可以用抓取时间兜底 df.loc[:, comment_time] df[comment_time].fillna( pd.to_datetime(2025-02-10 12:00:00) ) return df.reset_index(dropTrue) cleaned_df clean_dedup(comment_df) print(cleaned_df.shape)不要直接删除所有没有时间的评论。要考虑业务口径评论时间缺失时如果用采集时间兜底时间趋势统计仍能保留这条记录如果选择直接丢弃数据量会下降。实际项目中需要结合数据量和分析目的做取舍。4.3 中文分词与情感标签情感分析不一定要引入大模型。在初版项目中推荐使用词典法因为词典法逻辑透明面试时可以解释每一个词为什么出现在正负面词典里。不需要 GPU不会因为模型体积导致项目难以移植。后续可以替换成深度学习模型不影响已经入库的数据结构。基础情感词典可以准备两个词集合positive_words {喜欢, 好看, 可爱, 期待, 推荐, 种草, 满意, 划算} negative_words {贵, 丑, 失望, 套路, 营销, 割韭菜, 溢价, 退坑}然后结合 jieba 分词做评论内容切分import jieba def sentiment_score(text: str) - float: words jieba.lcut(text) pos_count sum(1 for w in words if w in positive_words) neg_count sum(1 for w in words if w in negative_words) if pos_count 0 and neg_count 0: return 0.0 return round((pos_count - neg_count) / (pos_count neg_count), 2) cleaned_df[sentiment_score] cleaned_df[content].apply(sentiment_score) def label_score(score: float) - str: if score 0: return 正面 if score 0: return 负面 return 中性 cleaned_df[sentiment_label] cleaned_df[sentiment_score].apply(label_score)词典法有明显的上限问题一句“这个价格也太离谱了吧”没有出现负向词大概率会被判成中性。培训环境可以接受但简历上要加一句免责说明“使用词典法进行初筛并通过人工抽检评估准确率”。如果要追求准确性可以引入情感分析模型或使用人工标注。4.4 从公开页面获取数据的扩展方案在稳定跑通 JSON 流程后如果想要接入真实公开数据必须在采集频率和数据范围上做约束。推荐的代码结构是先请求一个公开搜索接口或页面然后解析其中的关键字段再调用统一清洗函数。下面是一个最小化请求示例只用于说明思路不能直接照搬到所有项目中因为公开页面结构会变化import time import requests def fetch_public_data(keyword: str): url https://example.com/api/search params {q: keyword, page: 1} headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120 Safari/537.36 } resp requests.get(url, paramsparams, headersheaders, timeout10) resp.raise_for_status() time.sleep(3) # 控制请求频率 return resp.json()注意三点一是只采集公开数据和授权范围内的数据二是设置合理的请求间隔和超时避免对目标服务造成压力三是不要把采集功能做成无人值守的无限循环。学习项目跑通一次即可不必追求全量评论。5. 写入 MySQL 与 SQL 聚合分析5.1 使用 pandas 批量写入清洗后的 DataFrame 可以用 pandas 的to_sql快速入库。先建立 SQLAlchemy 引擎from sqlalchemy import create_engine engine create_engine( mysqlpymysql://root:root123456127.0.0.1:3306/popmart_analysis?charsetutf8mb4 )然后写入两张表cleaned_df.to_sql( namecomment_detail, conengine, if_existsappend, indexFalse, methodmulti, chunksize500, ) snapshot_df.to_sql( namehot_search_snapshot, conengine, if_existsappend, indexFalse, methodmulti, chunksize100, )to_sql默认有一条数据失败就整体回滚所以正式导入前要先确认 DataFrame 的字段名和类型能与表结构对应。如果使用methodmulti表示批量插入可以减少网络往返次数。这里要提醒一个坑如果沿用表中已有的id主键会造成主键冲突。正确的做法是构造 DataFrame 时不要给id赋值让 MySQL 自增主键自动生成。5.2 用 SQL 验证入库结果写入后用 SQL 确认数据条数SELECT COUNT(*) FROM comment_detail; SELECT COUNT(*) FROM hot_search_snapshot;接下来是几个核心分析 SQL。统计每个关键词的评论数、点赞总数、平均点赞SELECT keyword, COUNT(*) AS comment_cnt, SUM(like_count) AS total_likes, AVG(like_count) AS avg_likes FROM comment_detail GROUP BY keyword ORDER BY comment_cnt DESC;统计情感占比SELECT keyword, sentiment_label, COUNT(*) AS cnt, ROUND(COUNT(*) / SUM(COUNT(*)) OVER (PARTITION BY keyword) * 100, 2) AS pct FROM comment_detail GROUP BY keyword, sentiment_label ORDER BY keyword, cnt DESC;这里使用了窗口函数是 MySQL 8.0 支持的特性。如果使用 MySQL 5.7需要改写为子查询或 Python 二次计算。统计各关键词下的高互动评论SELECT keyword, content, like_count, reply_count FROM comment_detail WHERE like_count reply_count 0 ORDER BY (like_count reply_count) DESC LIMIT 20;统计时间段的评论热度SELECT DATE_FORMAT(comment_time, %Y-%m-%d %H:00:00) AS hour_slot, COUNT(*) AS comment_cnt, SUM(like_count) AS total_likes FROM comment_detail GROUP BY hour_slot ORDER BY hour_slot;5.3 评论词频表的设计如果希望直接使用 SQL 展示词频可以在 Python 里分词后生成一张临时词频表。from collections import Counter stop_words {我们, 你们, 这个, 那个, 什么, 就是, 还是, 真的, 不会} word_counter Counter() for content in cleaned_df[content].tolist(): words jieba.cut(content) filtered [ w.strip() for w in words if len(w.strip()) 2 and w.strip() not in stop_words and not w.strip().isdigit() ] word_counter.update(filtered) word_df pd.DataFrame(word_counter.most_common(50), columns[word, cnt]) word_df.to_sql( namecomment_word_term, conengine, if_existsreplace, indexFalse, )comment_word_term表结构可以很轻量只需要word和cnt。这张表不是必须的但有了它以后可以直接在 SQL 里查询 TOP 词频也可以供词云图使用。需要注意的是jieba 默认会把“泡泡玛特”切成单个字或错误片断。解决办法是在项目里加载自定义词典custom_words [泡泡玛特, 盲盒, 限定款, 联名款, 隐藏款] for w in custom_words: jieba.add_word(w)自定义词典要放在加载评论之前调用否则已经产生的分词结果不会更新。6. 可视化分析与数据结论输出6.1 从 MySQL 读取聚合结果可视化脚本不是从 JSON 重新算一遍而是从 MySQL 读取已经聚合好的数据。这样可以保证图表和 SQL 统计结果一致。import pymysql import pandas as pd conn pymysql.connect( host127.0.0.1, userroot, passwordroot123456, databasepopmart_analysis, charsetutf8mb4, ) emotion_df pd.read_sql( SELECT keyword, sentiment_label, COUNT(*) AS cnt FROM comment_detail GROUP BY keyword, sentiment_label , conn, ) print(emotion_df.head()) conn.close()6.2 使用 pyecharts 生成图表词频柱状图可以这样生成from pyecharts.charts import Bar from pyecharts import options as opts bar ( Bar() .add_xaxis(word_df[word].head(20).tolist()) .add_yaxis(出现次数, word_df[cnt].head(20).tolist()) .set_global_opts( title_optsopts.TitleOpts(title泡泡玛特热搜评论高频词 TOP20), xaxis_optsopts.AxisOpts(axislabel_optsopts.LabelOpts(rotate30)), ) ) bar.render(output/high_freq_word.html)情感分布饼图from pyecharts.charts import Pie pie ( Pie() .add( 情感占比, [ (row.sentiment_label, row.cnt) for row in emotion_df.itertuples() ], radius[35%, 60%], ) .set_global_opts(title_optsopts.TitleOpts(title评论情感分布)) ) pie.render(output/sentiment_pie.html)图表出现乱码时优先检查render对应的 HTML 文件中是否包含meta charsetutf-8。pyecharts 新版本一般会自动处理但如果在自定义页面中嵌入图表需要手动确认字符集。6.3 怎么讲数据结论不要只放图还要在项目汇报里把数据串成一个故事。例如可以这样写结论模板该关键词下高频词中的“新品”“盲盒”“隐藏款”体现了用户对新品和收藏玩法的关注。情感占比中“中性”比例偏高说明许多评论是信息询问或客观表达“负面”的关键词多与价格、物流等体验相关。热搜排名与评论点赞总量不一定同步。某一天关键词排名靠前但评论互动量下降可能是广告曝光带来的一次性流量。这些结论必须在项目中保留为文字分析文件。它的价值不亚于代码因为面试官会问“你从数据里看到了什么”。7. 常见问题与排查路径7.1 MySQL 连接失败2003 或 1045现象pymysql.err.OperationalError: (2003, Cant connect to MySQL server on 127.0.0.1)或pymysql.err.OperationalError: (1045, Access denied for user rootlocalhost)排查顺序检查 MySQL 是否启动。Docker 环境执行docker ps查看容器状态。检查端口映射是否冲突。如果宿主机 3306 已被本机 MySQL 占用Docker 端口会映射失败。检查连接地址。容器内部连接使用127.0.0.1不一定有效宿主机连接应使用127.0.0.1和映射端口。检查密码和用户名。处理建议先使用命令行客户端连接排除 Python 驱动问题后再调试 Python 代码。问题常见原因检查方式处理建议2003 错误MySQL 未启动或端口不对docker pstelnet 127.0.0.1 3306启动容器或换端口1045 错误用户名或密码不对命令行 mysql 连接在 Docker 里重置密码或新建用户认证插件报错MySQL 8.0 caching_sha2_password查看创建容器时的认证参数使用 mysql_native_password 或升级驱动7.2 中文写入 MySQL 后乱码乱码的根本原因是字符集不一致。写入前需要保证三层字符集都是 utf8mb4数据库创建时使用utf8mb4。SQLAlchemy 连接串包含?charsetutf8mb4。DataFrame 中原文为正常字符串不要提前使用错误的 encoding 解码。如果已经出现乱码可以删除表体重新导入但生产环境不能直接删除数据。正确做法是先把脏数据导出到临时表清洗后再回写。7.3 jieba 分词结果不理想现象“泡泡玛特”被切碎成“泡泡”和“玛特”导致词频统计失去意义。原因jieba 默认词典没有覆盖品牌词和网络新词。解决在分词前调用jieba.add_word或维护自定义词典文件。import jieba jieba.load_userdict(data/userdict.txt)userdict.txt内容格式泡泡玛特 10 nz 盲盒 10 n 限定款 10 n 隐藏款 10 n这里的频率和词性字段可以简化但保留有助于分词模块参考。7.4 to_sql 重复导入导致数据翻倍if_existsappend表示追加写入不会自动处理重复。如果脚本执行了两次数据就会翻倍。处理方式有两种入库前在 Python 中先读取已有content_hash过滤后再写入。在 MySQL 中对content_hash字段建立唯一索引第二次写入时数据库会报唯一键冲突程序捕获后跳过。ALTER TABLE comment_detail ADD UNIQUE INDEX uk_content_hash (content_hash);然后 Python 使用INSERT ... ON DUPLICATE KEY UPDATE或自行忽略try: cleaned_df.to_sql( namecomment_detail, conengine, if_existsappend, indexFalse, methodmulti, chunksize500, ) except Exception as exc: print(写入失败可能存在重复数据, exc)最稳妥的方案是入库前查询一次已有 hash。因为唯一索引冲突会导致整个 chunk 失败而按行忽略冲突需要写更底层逻辑。7.5 热搜排名数据变化后如何更新热搜快照是随时间变化的。同样的关键词在不同日期可能排在第 1 或第 20 位。快照表要记录captured_at每次采集都追加一条不更新历史记录。分析时需要“某一时刻的排名”就按时间条件过滤需要“排名变化趋势”就按关键词和时间分组。错误做法是在同一行记录里反复更新排名导致历史信息丢失。因为热搜分析要研究的是变化趋势不是今天的静止状态。8. 最佳实践与扩展方向8.1 把项目写进简历前要完成的检查清单下面这个清单可以直接用于最终验收[ ] 程序能从本地 JSON 或公开页面源读取数据不依赖手动复制数据库。[ ] MySQL 中至少有三张业务表热搜快照表、评论明细表、词频表。[ ] 评论明细表包含content_hash去重字段。[ ] 导入脚本可以重复执行不会造成数据成倍增长。[ ] 清洗脚本能够处理空值、转发格式、超短评论和重复内容。[ ] 情感分析部分明确标注使用词典法并记录人工抽检比例。[ ] 至少有三条 SQL 能回答不同维度的问题总览、情感、高互动评论。[ ] 输出目录保存了可视化 HTML 文件和结论文档。[ ] 环境依赖清单和启动步骤写入 README。[ ] 准备一段 3 分钟以内的项目讲解重点讲表结构和情感分析口径。8.2 项目的三种扩展方向第一个方向是引入真实评论数据集。在遵守数据来源平台规则的前提下把原始 JSON 替换为真实公开数据扩大时间范围会增加数据清洗压力也会让结论更有说服力。第二个方向是把情感分析换成预训练中文模型。替换后不要改表结构只需增加一个sentiment_model_version字段记录每批数据是用哪个模型生成的。这样做能保留词典法历史结果也方便对比模型效果。第三个方向是加入定时调度。用 cron 或 APScheduler 每天抓一次热搜快照这样就能画出“关键词排名 评论量”的时间序列曲线。此时 MySQL 表设计中的captured_at字段会变成核心分析维度。8.3 最容易拉高项目评分的一个习惯整个项目里最值得学习的不是某段爬虫代码而是“先设计可解释的数据结构再写处理逻辑”这个习惯。如果一开始没有content_hash就没有稳定的去重依据如果没有captured_at就无法做时间序列分析如果没有把情感词典的版本记录下来后续换模型时所有历史数据都没法对齐。所以建议在项目根目录增加一个docs/analysis_note.md记录数据来源时间、清洗规则、情感词典版本、人工抽检结果和最终结论。这个文件会成为简历面试中最有力的证据因为它证明你不仅会调用 API还会对数据和分析口径负责。