Python爬虫实战:QQ音乐巅峰新歌榜的时间序列与趋势追踪!
㊗️本期内容已收录至专栏《Python爬虫实战》持续完善知识体系与项目实战建议先订阅收藏后续查阅更方便㊙️本期爬虫难度指数⭐⭐福利一次订阅后专栏内的所有文章可永久免费看持续更新中保底1000(篇)硬核实战内容。全文目录 开篇语0️⃣ 前言Preface1️⃣ 摘要Abstract2️⃣ 背景与需求Why3️⃣ 合规与注意事项必写4️⃣ 技术选型与整体流程What/How5️⃣ 环境准备与依赖安装可复现6️⃣ 核心实现请求层Fetcher7️⃣ 核心实现解析层Parser8️⃣ 数据存储与导出Storage9️⃣ 运行方式与结果展示必写 常见问题与排错强烈建议写1️⃣1️⃣ 进阶优化可选但加分1️⃣2️⃣ 总结与延伸阅读 文末✅ 专栏持续更新中建议收藏 订阅✅ 互动征集✅ 免责声明 开篇语哈喽各位小伙伴们你们好呀我是【喵手】。运营社区 C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO欢迎大家常来逛逛一起学习一起进步我长期专注Python 爬虫工程化实战主理专栏 《Python爬虫实战》从采集策略到反爬对抗从数据清洗到分布式调度持续输出可复用的方法论与可落地案例。内容主打一个“能跑、能用、能扩展”让数据价值真正做到——抓得到、洗得净、用得上。专栏食用指南建议收藏✅ 入门基础环境搭建 / 请求与解析 / 数据落库✅ 进阶提升登录鉴权 / 动态渲染 / 反爬对抗✅ 工程实战异步并发 / 分布式调度 / 监控与容错✅ 项目落地数据治理 / 可视化分析 / 场景化应用专栏推广时间如果你想系统学爬虫而不是碎片化东拼西凑欢迎订阅专栏《Python爬虫实战》一次订阅后专栏内的所有文章可永久免费阅读持续更新中。订阅后更新会优先推送按目录学习更高效0️⃣ 前言Preface哈喽各位数据玩家今天咱们要挑战的是国内流媒体巨头——QQ音乐。我们要利用requests模拟接口请求穿透它的动态加载机制把不同历史时期的新歌榜单扒下来。最终不仅会产出一份带时间戳的标准数据集qqmusic_new_songs_timeseries.csv还会用全英文的图表画出爆款歌曲的热度跃迁曲线。读完这篇硬核实战你将解锁突破大厂复杂 JSON 结构精准提取嵌套数据的实战经验。掌握“动态传参遍历”技巧一口气拉取多个历史榜单。利用 Pandas 将离散的爬虫数据无缝转化为高价值的“时间序列Time Series”。1️⃣ 摘要Abstract本文针对QQ音乐巅峰榜新歌榜的动态 API 特性摒弃了传统的 HTML 渲染解析直接通过网络抓包定位底层数据接口。利用 Python 按周期如按周/按天迭代请求解析返回的 JSON 载荷清洗并补齐时间维度字段后落地为 CSV最后结合 Matplotlib 实现了单曲排名的动态走势可视化。读者将获得应对现代前端架构的纯 API 抓取思路。数据清洗与时间序列拼接的通用代码模板。2️⃣ 背景与需求Why为什么要抓榜单的历史数据因为静态的榜单只能代表“现在”而时间序列能预示“未来”。一首歌是从第50名稳步爬升到第1名还是空降第1名然后迅速跌出榜单背后的商业逻辑和营销水分完全不同。我们的目标站点/接口QQ音乐巅峰榜新歌榜的底层 API。我们需要提取的核心字段清单period: 榜单期数非常关键这是构建时间序列的锚点比如 2023_W41rank: 当前排名song_name: 歌曲名称singer: 歌手名称publish_time: 发行时间heat_trend: 热度趋势如上升、下降、持平或具体的变动名次3️⃣ 合规与注意事项必写音乐平台的法务部可不是吃素的所以咱们做技术探讨必须守规矩robots.txt 约束作为国内顶级平台QQ音乐对爬虫有严格的防御。我们只获取“公开的榜单排行元数据”绝对不可去破解音频文件的下载链接那是盗版侵权行为的红线碰都不能碰。频率控制API 请求务必加上随机休眠。大厂的风控系统极度敏感一秒钟发10个请求下一秒你的IP就进小黑屋了。个人学习声明本案例仅做数据分析维度的技术交流切勿将高频抓取的数据用于倒卖或商业变现。4️⃣ 技术选型与整体流程What/How这篇实战属于纯正的动态 API 抓取。现在的音乐网站绝对不会把榜单写在 HTML 源码里全是通过 Ajax通常是带有极强反爬签名的 GET/POST 请求从服务器拿 JSON。整体流程链路构建历史期数列表 → 构造带参 API 请求 (Fetcher) → 拆解深层嵌套 JSON (Parser) → 注入时间锚点并扁平化 → 追加存入 CSV (Storage) → 序列化绘图为什么不用 Selenium/Playwright虽然大厂有鉴权加密比如传说中的sign参数但只要我们能通过 F12 拿到一个短时间内有效的接口 URL直接用requestsjson解析是效率最高、代码最优雅的方案。5️⃣ 环境准备与依赖安装可复现又到了配置武器库的环节这次我们需要画图所以别忘了装好绘图库。Python 版本推荐 3.9 及以上。依赖安装pipinstallrequests pandas matplotlib项目结构推荐qqmusic_trend_spider/ ├── config.py # 存放易变的 API 模板或 Cookie ├── trend_scraper.py # 爬虫核心与时间序列逻辑 ├── plot_trend.py # 可视化脚本 └── data/ # 存放最终数据集和图表6️⃣ 核心实现请求层Fetcher根据我们对 QQ 音乐网页版的 F12 抓包分析榜单数据通常来自一个类似u.y.qq.com/cgi-bin/musicu.fcg的接口。这里面最折磨人的就是它需要传一大串 Payload负载。注由于QQ音乐的sign参数强加密且动态变化以下代码使用结构演示。实战中请把你在 Network 面板里复制到的真实完整 URL 和 Headers 填进去。importrequestsimporttimeimportrandomclassMusicAPIFetcher:def__init__(self):self.sessionrequests.Session()# 伪装头部大厂特别看重 Referer 和 Originself.headers{User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36,Accept:application/json,Referer:https://y.qq.com/,Origin:https://y.qq.com}deffetch_chart_by_period(self,api_url,period_param):try:# 优雅的延时模拟人类操作time.sleep(random.uniform(2.0,4.5))# 假设你的 api_url 是从浏览器抓到的你需要把代表“期数”的参数动态替换进去# 实战中可以通过 requests.get(url, params{...}) 传递responseself.session.get(api_url,headersself.headers,timeout10)response.raise_for_status()returnresponse.json()exceptrequests.exceptions.RequestExceptionase:print(f❌ 请求炸了 [期数:{period_param}]:{e})returnNone7️⃣ 核心实现解析层Parser大厂的 JSON 往往嵌套得非常深。比如json_data[req_1][data][songlist]。这里最考验的就是防空指针报错KeyError。classChartParser:staticmethoddefparse_json(json_data,period):解析 JSON并强行注入 period 作为时间序列的锚点ifnotjson_data:return[]parsed_songs[]try:# 这里的层级根据你实际抓包的结构为准这里为通用模拟结构song_listjson_data.get(req_1,{}).get(data,{}).get(songList,[])forindex,iteminenumerate(song_list):# 容错提取song_infoitem.get(songInfo,{})rankindex1song_namesong_info.get(title,Unknown)# 歌手可能有多个拼接起来singers .join([s.get(name)forsinsong_info.get(singer,[])])publish_timesong_info.get(time_public,N/A)# 热度趋势看 rankValue 或是 old_rank 的差值# 假设 API 返回了 rankType (1上升, 2下降, 3持平)trend_valitem.get(rankType,3)trend_map{1:Up,2:Down,3:Stable,4:New}heat_trendtrend_map.get(trend_val,Unknown)parsed_songs.append({Period:period,# 构建时间序列的灵魂字段Rank:rank,Song:song_name,Singer:singers,Release_Time:publish_time,Trend:heat_trend})exceptExceptionase:print(f❌ JSON 解析扑街了检查结构是不是变了:{e})returnparsed_songs8️⃣ 数据存储与导出Storage使用pandas简直是处理时间序列的神器我们将所有期数的数据合并后导出一个干净的 CSV这为后续的纵向趋势分析打下了完美基础。importpandasaspdimportosclassTimeSeriesStorage:def__init__(self,filenamedata/qqmusic_new_songs_timeseries.csv):self.filenamefilename os.makedirs(os.path.dirname(self.filename),exist_okTrue)defappend_data(self,data_list):ifnotdata_list:returndfpd.DataFrame(data_list)# 如果文件不存在就带上表头写入如果存在就追加写入并跳过表头ifnotos.path.exists(self.filename):df.to_csv(self.filename,indexFalse,encodingutf-8-sig)else:df.to_csv(self.filename,modea,headerFalse,indexFalse,encodingutf-8-sig)print(f 成功追加{len(data_list)}条记录当前期数{data_list[0][Period]})9️⃣ 运行方式与结果展示必写为了跑通时间序列我们模拟拉取过去 3 个周期例如 2023年的第40周到42周的数据。直接运行这个 Python 脚本奇迹就会发生。defmain():print( 启动QQ音乐巅峰榜时间序列拉取雷达...)fetcherMusicAPIFetcher()storageTimeSeriesStorage()# 模拟我们要回溯的历史期数history_periods[2023_W40,2023_W41,2023_W42]forperiodinhistory_periods:print(f\n 正在穿越时空抓取期数:{period})# ⚠️ 注意实战中这里必须换成你 F12 抓到的真实 API并且处理好 period 参数mock_api_urlfhttps://api.example.com/musicu.fcg?period{period}signxxxxxxxxraw_jsonfetcher.fetch_chart_by_period(mock_api_url,period)# 解析并注入时间锚点period_dataChartParser.parse_json(raw_json,period)# 追加存储storage.append_data(period_data)print(\n 全周期榜单抓取完成时间序列数据集已就绪)if__name____main__:# main() # 实战中取消注释即可运行pass抓取到的qqmusic_new_songs_timeseries.csv示例结果PeriodRankSongSingerRelease_TimeTrend2023_W401悬溺葛东琪2023-09-01New2023_W402那些你很冒险的梦林俊杰2011-12-31Up2023_W411那些你很冒险的梦林俊杰2011-12-31Up2023_W413悬溺葛东琪2023-09-01Down(发现了吗有了Period字段我们就能清晰看到《悬溺》从第一跌到了第三而JJ的歌爬到了第一这就是时间序列的魅力) 常见问题与排错强烈建议写抓大厂的 API遇到坑是日常不遇坑才是见鬼了。致命的sign签名校验QQ音乐的接口大多带有一个动态的sign参数比如signzzbh53...这是用前端 JS 里的 WebAssembly 或高度混淆的算法生成的。如果不用正确的sign请求会返回类似{code: -1, msg: sign error}。解法1小白友好手动在网页操作复制那个短期有效的 URL 来跑脚本过期了再手动换。解法2进阶降维放弃requests直接上Playwright驱动真实无头浏览器利用page.route拦截响应数据绕过一切加密算法返回空壳或{code: 1000}可能 Cookie 失效或 IP 被风控了马上停止运行换个 IP 并且补全你在浏览器里的完整Cookie字符串到 headers 里。JSON 结构突然变更有时碰到特殊的“推广曲”它的节点结构可能不一样一定要用字典的.get()方法防止抛出KeyError让程序暴毙。1️⃣1️⃣ 进阶优化可选但加分最激动人心的时刻来了既然我们有了一份按期数排列的 CSV我们完全可以写一段牛逼的脚本画出某一首特定歌曲在这几周内的排名走势图。(严格遵循要求图表内所有文本元素全部为英文)importpandasaspdimportmatplotlib.pyplotaspltimportosdefplot_english_time_series():csv_filedata/qqmusic_new_songs_timeseries.csvifnotos.path.exists(csv_file):returndfpd.read_csv(csv_file)# 我们假设要追踪 林俊杰 这首歌的趋势target_song那些你很冒险的梦song_dfdf[df[Song]target_song].sort_values(byPeriod)ifsong_df.empty:print(没有找到目标歌曲的数据哦)returnplt.figure(figsize(9,5))# 画折线图设置标记点plt.plot(song_df[Period],song_df[Rank],markero,linestyle-,color#FF5722,linewidth2)# 强制全部英文描述plt.title(Ranking Trend of Target Song Over Time,fontsize15,fontweightbold,pad15)plt.xlabel(Chart Period (Time Series),fontsize12)plt.ylabel(Chart Rank (Top is 1),fontsize12)# 音乐榜单通常是数值越小越厉害第1名在最上面所以要反转 Y 轴plt.gca().invert_yaxis()# 添加网格线方便查看plt.grid(True,linestyle--,alpha0.6)# 添加数据标签forx,yinzip(song_df[Period],song_df[Rank]):plt.text(x,y-0.2,fRank{int(y)},hacenter,vabottom,fontsize10)plt.tight_layout()plt.savefig(data/song_heat_trend_chart.png,dpi300)print( 太帅了全英文的时间序列走势图已生成data/song_heat_trend_chart.png)# plot_english_time_series()1️⃣2️⃣ 总结与延伸阅读呼~ 这一顿操作下来是不是觉得酣畅淋漓今天咱们不仅拆解了大型流媒体网站底层的 API 结构更关键的是掌握了**“通过循环期数构建 Time Series时间序列”**的顶级数据思维。这套代码逻辑不仅适用于QQ音乐你拿去抓微博热搜走势、B站排行榜演变都是直接套用下一步的进阶方向如果想彻底实现全自动化不想手动复制 URL你可以去研究一下JS 逆向工程尝试用PyExecJS去本地执行大厂的加密算法来动态生成sign或者直接拥抱自动化测试工具Playwright用“魔法打败魔法”。 文末好啦以上就是本期的全部内容啦如果你在实践过程中遇到任何疑问欢迎在评论区留言交流我看到都会尽量回复咱们下期见小伙伴们在批阅的过程中如果觉得文章不错欢迎点赞、收藏、关注哦三连就是对我写作道路上最好的鼓励与支持❤️✅ 专栏持续更新中建议收藏 订阅墙裂推荐订阅专栏 《Python爬虫实战》本专栏秉承着以“入门 → 进阶 → 工程化 → 项目落地”的路线持续更新争取让每一期内容都做到✅ 讲得清楚原理✅ 跑得起来代码✅ 用得上场景✅ 扛得住工程化想系统提升的小伙伴强烈建议先订阅专栏 《Python爬虫实战》再按目录大纲顺序学习效率十倍上升✅ 互动征集想让我把【某站点/某反爬/某验证码/某分布式方案】等写成某期实战评论区留言告诉我你的需求我会优先安排实现(更新)哒~⭐️ 若喜欢我就请关注我叭更新不迷路⭐️ 若对你有用就请点赞支持一下叭给我一点点动力⭐️ 若有疑问就请评论留言告诉我叭我会补坑 更新迭代✅ 免责声明本文爬虫思路、相关技术和代码仅用于学习参考对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。使用或者参考本项目即表示您已阅读并同意以下条款合法使用 不得将本项目用于任何违法、违规或侵犯他人权益的行为包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。风险自负 任何因使用本项目而产生的法律责任、技术风险或经济损失由使用者自行承担项目作者不承担任何形式的责任。禁止滥用 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。使用或者参考本项目即视为同意上述条款,即 “谁使用谁负责” 。如不同意请立即停止使用并删除本项目。