news 2026/10/22 3:17:27

Python实战:百度指数数据抓取、清洗与趋势分析全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python实战:百度指数数据抓取、清洗与趋势分析全流程

简介:这份Python完整示例代码面向具备基础语法、希望入门数据分析的开发者,围绕百度指数这一关键词热度工具,演示从数据获取到可视化呈现的完整流程。包内共6个文件,以2个py脚本为核心,配合1个csv数据文件、1个txt城市代码、1个docx关键词说明及1个docx文档,压缩包约24KB,结构轻量便于快速上手。代码示例串联Pandas、NumPy、Matplotlib与requests等常用库,覆盖API请求、DataFrame清洗、日期转换、均值统计及时间序列绘图等环节,并涉及缺失值与异常值处理思路。已有700人学习下载,适合想借真实场景熟悉数据分析链路、对照脚本动手实践的读者参考。

1. 百度指数数据分析python完整示例代码:从数据获取到趋势洞察的落地路径

百度指数反映的是关键词在百度搜索中的日均搜索量趋势,对做市场分析、内容运营、SEO 优化的人来说,它是一份低成本、高频次的需求侧数据。但很多人卡在第一步:指数页面只给图表,不给原始数据。手动抄?几十个关键词、几百天跨度,抄到怀疑人生。用 Python 把这条链路跑通,就能把“看趋势”变成“算趋势”——批量拉取、清洗、对齐、可视化、导出,一套脚本管到底。这篇笔记面向有基础 Python 能力、想用代码替代手工取数的从业者,从零搭出一个可复现的百度指数数据分析流程。需要说明的是,百度指数没有公开的官方数据接口,常见做法是通过浏览器开发者工具观察页面加载时的请求规律,再用脚本模拟这些请求获取数据。下面所有代码都围绕这个思路展开,你可以直接抄作业,也可以按自己的场景改参数。

2. 环境准备与请求链路拆解:先把“黑匣子”打开

2.1 为什么不能直接 requests.get 就完事

百度指数的数据加载不是简单的静态页面。你在浏览器里打开指数页面,看到的曲线是前端 JavaScript 拿到数据后渲染出来的。数据本身通过异步请求返回,请求里带着几个关键参数:关键词、时间范围、以及一个由前端生成的加密签名。这个签名是最大的门槛——它随时间变化,直接复制粘贴只能管几分钟。

常见做法是:用自动化浏览器工具(比如 Playwright 或 Selenium)打开页面,让页面自己完成签名计算,然后拦截网络请求,把返回的 JSON 数据截下来。这样就不需要逆向签名算法,稳定性也更好。我一般会优先选 Playwright,因为它对异步请求的拦截更干净,而且自带等待机制,不容易翻车。

2.2 最小可运行环境与依赖安装

先建一个干净的虚拟环境,避免和系统里其他包的版本打架。Python 版本建议 3.9 以上,3.11 更稳。

python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install playwright pandas matplotlib openpyxl playwright install chromium

这里装了五个东西:playwright 负责驱动浏览器,pandas 做数据清洗和透视,matplotlib 画图,openpyxl 用来导出 Excel。playwright install chromium是下载浏览器内核,只跑一次就行。如果你之前装过 Selenium,也不用卸,两者互不冲突,但建议只保留一套,减少排查成本。

2.3 拦截请求:拿到第一份原始 JSON

下面这段代码打开百度指数首页,输入关键词,等待图表加载,然后监听所有返回 JSON 的响应。关键点是page.on("response", ...)这个回调,它会在每个响应到达时触发。

from playwright.sync_api import sync_playwright import json def fetch_index_data(keyword: str, start_date: str, end_date: str): with sync_playwright() as p: browser = p.chromium.launch(headless=False) # 首次调试建议有头 page = browser.new_page() captured = [] def on_response(response): # 只关心返回 JSON 且 URL 里带 index 的请求 if "application/json" in response.headers.get("content-type", ""): if "index" in response.url or "api" in response.url: try: captured.append(response.json()) except Exception: pass page.on("response", on_response) # 打开指数页面,关键词通过 URL 参数传入 url = f"https://index.baidu.com/v2/main/index.html#/trend/{keyword}?words={keyword}" page.goto(url, wait_until="networkidle") # 设置时间范围(这里用页面上的日期控件,具体选择器需按实际页面调整) page.wait_for_timeout(3000) # 等图表渲染 browser.close() return captured if __name__ == "__main__": data = fetch_index_data("Python", "2024-01-01", "2024-06-30") print(json.dumps(data, ensure_ascii=False, indent=2)[:2000])

逻辑说明:headless=False是为了第一次跑的时候能看到浏览器在干什么,方便判断是否被拦截。wait_until="networkidle"表示等网络请求基本停了再继续,但百度指数有轮询,可能永远不 idle,所以后面加了固定等待。captured列表会收集所有符合条件的 JSON,实际返回的可能有多个,需要根据结构判断哪个是趋势数据。

参数说明:keyword就是你要查的词,中文也行,但 URL 里要做编码。start_date和end_date目前没直接用到,因为页面上的时间选择需要模拟点击,更稳的做法是直接在拦截到的请求里改参数重放。这个后面会讲。

提示:第一次跑建议把headless设为False,观察页面是否正常加载。如果出现验证码,说明请求频率过高,需要降低速度或换时间段。

3. 数据清洗与结构化:把 JSON 变成 DataFrame

3.1 识别有效数据体:别把广告 JSON 也收进来

拦截到的 JSON 可能包含页面配置、用户信息、广告位数据。真正的指数数据通常有几个特征:包含data字段,里面是userIndexes或index数组,数组元素有date和value。我一般会写一个过滤函数,只保留符合这个结构的响应。

def extract_trend_json(captured_list): """从拦截到的多个 JSON 中找出趋势数据""" for item in captured_list: if not isinstance(item, dict): continue # 常见结构:{"data": {"userIndexes": [{"date": "...", "value": ...}]}} data = item.get("data") if isinstance(data, dict): for key in ["userIndexes", "index", "trend"]: if key in data and isinstance(data[key], list): return data[key] # 有些版本直接返回数组 if isinstance(data, list) and len(data) > 0: if "date" in data[0] and "value" in data[0]: return data return None

这段代码不依赖固定字段名,而是按“有没有 date 和 value”来判断,兼容性更好。如果你拿到的数据里 value 是加密字符串,那说明签名没对上,需要回到上一步检查请求是否完整。

3.2 构建 DataFrame 并处理缺失值

拿到列表后,直接转成 DataFrame,然后做三件事:日期解析、数值转换、缺失值填充。

import pandas as pd def build_dataframe(trend_list): df = pd.DataFrame(trend_list) df["date"] = pd.to_datetime(df["date"]) df["value"] = pd.to_numeric(df["value"], errors="coerce") df = df.sort_values("date").reset_index(drop=True) # 指数数据常见缺失:周末或节假日可能为空,用前值填充 df["value"] = df["value"].fillna(method="ffill").fillna(0) # 加上移动平均,方便看趋势 df["ma7"] = df["value"].rolling(window=7, min_periods=1).mean() df["ma30"] = df["value"].rolling(window=30, min_periods=1).mean() return df

errors="coerce"会把无法转换的值变成 NaN,避免整列报错。ffill是前向填充,适合指数这种连续但偶尔缺失的场景。移动平均的窗口 7 和 30 是经验值,7 天对应一周周期,30 天对应月度趋势。如果你分析的是短期活动,可以把 7 改成 3。

3.3 多关键词对齐:时间轴不一致怎么办

同时分析多个词时,每个词返回的日期范围可能略有差异。用pd.merge按日期做外连接,再统一填充。

def merge_keywords(df_dict): """df_dict: {"Python": df1, "Java": df2}""" merged = None for kw, df in df_dict.items(): temp = df[["date", "value"]].rename(columns={"value": kw}) if merged is None: merged = temp else: merged = pd.merge(merged, temp, on="date", how="outer") merged = merged.sort_values("date").reset_index(drop=True) merged = merged.fillna(method="ffill").fillna(0) return merged

这样得到的表,每一列是一个关键词,每一行是一个日期。后续画图、算相关性都方便。注意how="outer"会保留所有日期,如果某个词缺某天数据,填充后可能引入偏差,所以最好在填充前先检查缺失比例,超过 20% 的词建议单独处理。

4. 可视化与趋势解读:让图表说人话

4.1 单关键词趋势图:把移动平均叠上去

import matplotlib.pyplot as plt import matplotlib.dates as mdates plt.rcParams["font.sans-serif"] = ["SimHei"] # 中文显示 plt.rcParams["axes.unicode_minus"] = False def plot_single(df, keyword): fig, ax = plt.subplots(figsize=(12, 5)) ax.plot(df["date"], df["value"], alpha=0.3, label="日指数", color="gray") ax.plot(df["date"], df["ma7"], label="7日均线", color="orange", linewidth=2) ax.plot(df["date"], df["ma30"], label="30日均线", color="red", linewidth=2) ax.xaxis.set_major_formatter(mdates.DateFormatter("%m-%d")) ax.xaxis.set_major_locator(mdates.WeekdayLocator(interval=2)) plt.xticks(rotation=45) ax.set_title(f"{keyword} 百度指数趋势") ax.legend() plt.tight_layout() plt.savefig(f"{keyword}_trend.png", dpi=150) plt.show()

灰色细线是原始日指数,波动大但能看到突发峰值。橙色 7 日均线抹平了周内波动,红色 30 日均线看月度走向。如果 7 日均线突然上穿 30 日均线,通常意味着热度在爬升。保存图片时 dpi 设 150,够清晰又不至于文件太大。

4.2 多关键词对比:归一化后再比

不同词的指数绝对值差异可能很大,直接画在一起,小的那条会被压成直线。常见做法是做归一化,把每个词的值除以自己的最大值,变成 0 到 1 之间的相对趋势。

def plot_compare(merged_df, keywords): fig, ax = plt.subplots(figsize=(12, 5)) for kw in keywords: normalized = merged_df[kw] / merged_df[kw].max() ax.plot(merged_df["date"], normalized, label=kw, linewidth=2) ax.xaxis.set_major_formatter(mdates.DateFormatter("%m-%d")) plt.xticks(rotation=45) ax.set_ylabel("相对热度(归一化)") ax.set_title("关键词热度对比") ax.legend() plt.tight_layout() plt.savefig("compare_trend.png", dpi=150) plt.show()

归一化后,你比较的是“走势形状”而不是“绝对量级”。如果两个词走势高度重合,说明它们可能受同一事件驱动;如果一个词先涨、另一个滞后涨,可能存在传导关系。这个技巧在选品和舆情分析里很实用。

4.3 导出 Excel:给不写代码的同事留条路

分析结果最终要给别人看,Excel 是最通用的格式。pandas 的to_excel可以一次写多个 sheet。

def export_excel(merged_df, single_dfs, filename="baidu_index.xlsx"): with pd.ExcelWriter(filename, engine="openpyxl") as writer: merged_df.to_excel(writer, sheet_name="多词对比", index=False) for kw, df in single_dfs.items(): df.to_excel(writer, sheet_name=kw[:20], index=False) # sheet 名有长度限制 print(f"已导出:{filename}")

sheet 名超过 31 个字符会报错,所以做了截断。如果关键词里有特殊字符,比如*、?,也要替换掉,否则 openpyxl 会抛异常。导出后建议手动打开检查一下日期列格式,有时候 Excel 会自动把日期识别成数字,需要设置单元格格式。

5. 避坑与常见问题:那些让我熬夜的瞬间

5.1 请求返回 403 或验证码

现象:脚本跑几次后,页面跳转到验证页,或者接口返回 403。原因:请求频率过高,或者浏览器指纹被识别。解决:降低速度,每次请求间隔 5 到 10 秒;用headless=False加随机延迟;如果持续被封,换 IP 或等几小时再试。我一般会在脚本里加page.wait_for_timeout(random.randint(3000, 8000)),模拟人工操作节奏。

5.2 拿到的 value 是加密字符串

现象:JSON 里value是一长串字母数字,不是数字。原因:百度指数对部分接口做了加密,需要额外的解密步骤。解决:优先拦截页面渲染后的数据,而不是原始接口。可以在page.on("response")里过滤 URL 包含api且返回内容里value是数字的响应。如果所有响应都加密,那就需要逆向解密逻辑,这超出本文范围,建议改用其他数据源或手动导出。

5.3 日期对不上或缺少某几天

现象:DataFrame 里日期跳跃,或者某几天是 NaN。原因:指数数据本身在周末或节假日可能不更新,或者接口返回的分页没取全。解决:用pd.date_range生成完整日期序列,然后reindex,缺失的用前值填充。如果缺失太多,检查是不是只取了第一页,需要翻页逻辑。

5.4 中文关键词 URL 编码问题

现象:关键词带中文时,页面打开空白或报错。原因:URL 里中文需要 percent-encoding。解决:用urllib.parse.quote处理。

from urllib.parse import quote keyword = "数据分析" encoded = quote(keyword) url = f"https://index.baidu.com/v2/main/index.html#/trend/{encoded}?words={encoded}"

5.5 多关键词时只返回了第一个词的数据

现象:同时传多个词,但拦截到的 JSON 只包含一个词。原因:页面可能分多次请求,每个词一个请求。解决:在on_response里不要提前 return,把所有符合条件的响应都收集起来,最后按关键词分组。或者干脆一个词一个词跑,稳但慢。

6. 进阶技巧:用对比窗口和相关性矩阵挖出隐藏信号

前面跑通了单次抓取和基础可视化,但真正让百度指数分析产生价值的,是把多个词放在同一时间窗口里做交叉验证。我常用的一个技巧是“滑动对比窗口”:不只看全量趋势,而是把时间轴切成 7 天一段,计算每个词在每段里的环比变化率,然后看哪些词的涨跌同步。

def rolling_correlation(merged_df, window=7): """计算多关键词在滑动窗口内的相关性""" cols = [c for c in merged_df.columns if c != "date"] result = [] for i in range(window, len(merged_df)): chunk = merged_df.iloc[i-window:i][cols] corr = chunk.corr().iloc[0, 1] # 取前两个词的相关系数 result.append({ "date": merged_df.iloc[i]["date"], "corr": corr }) return pd.DataFrame(result)

这段代码输出的是两个词在每 7 天窗口内的皮尔逊相关系数。如果相关系数长期在 0.7 以上,说明它们的热度联动性强;如果某段时间突然跌到 0 以下,可能发生了事件导致走势分化。这个信号比看静态对比图更敏感。

另一个实用技巧是“峰值对齐”。把每个词的峰值日期找出来,算它们之间的天数差。

def peak_lag(merged_df, keywords): peaks = {} for kw in keywords: idx = merged_df[kw].idxmax() peaks[kw] = merged_df.loc[idx, "date"] base = min(peaks.values()) for kw, d in peaks.items(): print(f"{kw} 峰值日期:{d.date()},滞后基准 {(d - base).days} 天")

如果 A 词峰值比 B 词早 3 天,可能意味着 A 是先行指标,B 是跟随指标。做内容排期时,可以按这个滞后天数提前布局。

最后说一个我踩过的坑:不要用百度指数的绝对值去推算真实搜索量。指数是经过归一化和对数处理的相对值,不同词的指数不可直接比大小,只能比趋势。我曾经拿两个词的指数直接算比例,结论完全跑偏。后来养成习惯,所有分析都基于归一化后的相对变化,结论反而更稳。

希望帮到你。

本文还有配套的精品资源,点击获取

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

240个线框图标素材包:从AI源文件到代码集成的完整指南

简介:这份资源面向UI/UX设计师、前端开发者及产品原型人员,提供240个开发场景中高频使用的线框图标,帮助快速搭建界面布局、梳理交互逻辑,解决从草图到视觉稿的图标素材需求。压缩包共1447个文件,约5.67MB,…

作者头像 李华
网站建设 2026/10/22 3:17:06

SpringBoot+Vue小区物业管理系统:毕业设计选题、架构与部署实战

每年到毕设选题的季节,我总会遇到一类提问:“想做一个管理系统,但不知道选什么题目合适”、“前后端分离到底怎么落地”、“拿到源码却启动不起来”。如果你也是这个状态,那这个 SpringBoot Vue 的小区物业管理系统,值…

作者头像 李华
网站建设 2026/10/22 3:16:24

机械臂轨迹跟踪模糊滑模控制:MATLAB仿真与抖振抑制

简介:这份资源面向机器人控制方向的研究生、科研人员与工程师,聚焦两连杆机械臂的轨迹跟踪问题,提供一套基于模糊规则优化的滑模控制器实现方案。滑模控制具备强鲁棒性,但易产生抖振,模糊逻辑的引入可自适应调整切换函…

作者头像 李华
网站建设 2026/10/22 3:16:23

MongoShake分片同步原理与单实例/灾备集群实战指南

1. 项目概述:为什么MongoShake成了分片集群数据同步的“刚需工具”最近在某高校实验室做数据库架构优化时,团队遇到一个典型但棘手的问题:线上运行着一个三节点分片集群(shard0、shard1、shard2),每个shard…

作者头像 李华
网站建设 2026/10/22 3:16:05

Thread Dump分析工具:线上CPU飙高排查与死锁检测实战

简介:Thread_Dump_Analyzing_Tool 是一款面向 Java 开发与运维人员的线程转储分析工具,以 Web 界面呈现,用于诊断应用响应缓慢、无响应等并发问题,帮助定位死锁、线程阻塞与资源竞争。项目采用前后端分离结构,前端负责…

作者头像 李华
网站建设 2026/10/22 3:15:42

EKF四元数姿态解算:从理论到MCU代码落地与避坑指南

简介:这份资源面向飞行器、机器人姿态解算方向的学习者与工程师,聚焦扩展卡尔曼滤波(EKF)与四元数结合的姿态估计方案,帮助解决陀螺仪、加速度计、磁力计多传感器数据融合中的姿态解算与稳定性问题。压缩包共10个文件&…

作者头像 李华