Scrapy多级请求实战:5sing伴奏网爬取踩坑与优化全记录(JSON提取+Xpath解析)

张开发
2026/4/12 21:19:31 15 分钟阅读

分享文章

Scrapy多级请求实战:5sing伴奏网爬取踩坑与优化全记录(JSON提取+Xpath解析)
Scrapy多级请求实战5sing伴奏网爬取踩坑与优化全记录JSON提取Xpath解析前言本次实战围绕5sing伴奏网热榜歌曲爬取展开核心需求是获取首页热榜歌曲基础信息并深入详情页提取歌曲分类、格式、大小、下载量等完整数据。开发过程中核心突破点在于发现网站数据存储的差异化的特点——首页热榜数据以JSON字段形式嵌入页面源码详情页则为标准HTML结构由此完成了从Xpath解析到正则提取的切换同时实现Scrapy多级请求即大家常说的“二次爬取”专业表述为Scrapy多级请求/二级页面爬取全程踩坑不断最终完成优化落地特此整理成实战笔记供各位爬虫爱好者参考避坑。一、核心需求与技术架构1.1 爬取目标目标网站5sing.kugou.com5sing伴奏网爬取范围首页热榜hot歌曲需获取两类数据一级页面首页歌曲ID、歌曲名称、歌手、上传用户ID、上传用户昵称二级页面详情页歌曲分类、文件格式、文件大小、下载次数1.2 专业技术架构Scrapy多级请求二级页面爬取发起一级请求请求首页start_urls获取页面源码解析一级页面提取热榜歌曲的基础信息歌曲ID等构造详情页URL发起二级请求通过Scrapy.Request()携带基础信息请求每首歌的详情页解析二级页面提取详情页扩展信息与一级页面数据合并统一输出。核心核心依赖Scrapy框架、正则表达式re、Xpath解析、JSON数据解析。二、开发全过程踩坑→排查→优化本次开发最核心的难点的是首页与详情页的数据存储方式完全不同初期因惯性思维使用Xpath解析首页导致爬取失败后续通过排查源码发现JSON字段存储的规律切换为正则提取逐步完成优化。以下是完整踩坑与优化记录全程还原真实开发场景。2.1 初始开发惯性思维踩坑——Xpath解析首页失败2.1.1 初始思路基于过往爬取静态网页的经验默认首页热榜歌曲信息会以HTML标签如div、li、table形式渲染因此初始开发时直接使用Xpath定位热榜歌曲的各个字段核心代码如下defparse(self,response):# 错误思路用Xpath定位热榜歌曲song_listresponse.xpath(//div[classhot-song]/li)forsonginsong_list:singersong.xpath(./div[classsinger]/text()).get()song_namesong.xpath(./div[classsong-name]/text()).get()yield{singer:singer,song_name:song_name}2.1.2 踩坑现象运行命令scrapy crawl 5sing后控制台无任何数据输出也无报错信息排查后发现两个关键问题Xpath路径无误但无法提取到任何文本返回None查看页面源码F12在Elements面板能看到热榜歌曲列表但在Page Source页面原始源码中找不到对应的HTML标签。2.1.3 排查过程排除反爬问题已配置请求头User-Agent、Cookie且能正常获取首页源码排除反爬拦截分析页面渲染方式通过对比Elements和Page Source发现热榜歌曲数据并非静态HTML渲染而是通过JavaScript动态加载数据藏在页面源码的JSON字符串中定位数据位置全局搜索页面源码CtrlF关键词“hot”最终发现热榜数据被包裹在bz_songs {download: [...], hot: [...]}这个JSON结构中所有热榜歌曲的基础信息都在“hot”对应的列表里。2.2 优化第一步切换解析方式——从Xpath到正则提取JSON2.2.1 核心突破点首页数据存储方式JSON字段嵌入页面源码无法用Xpath解析需通过正则表达式re截取JSON片段再解析为Python列表提取所需字段。2.2.2 优化代码核心片段importscrapyimportreimportjsondefparse(self,response):# 1. 获取首页原始源码html_textresponse.text# 2. 正则提取 hot: [...] 对应的JSON片段精准匹配避免干扰patternrhot:(\[.*?\])# 捕获hot对应的列表内容hot_jsonre.findall(pattern,html_text,re.S)[0]# re.S让.匹配换行符# 3. JSON转Python列表提取基础信息hot_listjson.loads(hot_json)print(f成功提取到{len(hot_list)}首热榜歌曲开始构造详情页请求...)# 4. 循环构造详情页URL发起二级请求forsonginhot_list:# 用歌曲ID构造详情页URL踩坑后发现songId是核心标识无songUrl字段detail_urlfhttps://5sing.kugou.com/bz/{song[songId]}.html# 用meta传递一级页面数据供详情页解析时合并yieldscrapy.Request(urldetail_url,callbackself.parse_detail,# 详情页解析函数meta{singer:song[singerName],song_name:song[songName],user_id:song[userId],user_nickname:song[userNickname]})2.2.3 踩坑补充构造详情页URL时的小失误初期构造详情页URL时误写为fhttps://5sing.kugou.com/bz/{song[songUrl]}.html运行后报错“KeyError: ‘songUrl’”排查后发现JSON字段中只有“songId”歌曲唯一标识无“songUrl”字段修正为用songId构造URL后成功发起二级请求。2.3 优化第二步详情页解析——Xpath回归使用适配HTML结构2.3.1 关键发现详情页与首页数据存储方式差异页面类型数据存储方式解析方式首页一级页面JSON字符串嵌入源码动态加载正则表达式re提取JSON再解析详情页二级页面标准HTML标签静态渲染Xpath解析直接定位字段2.3.2 详情页解析代码defparse_detail(self,response):# 1. 接收一级页面传递的基础数据base_dataresponse.meta# 2. Xpath定位详情页扩展信息分类、格式、大小、下载次数categoryresponse.xpath(/html/body/div[5]/div[1]/div[1]/div[3]/div[1]/ul/li[2]/text()).get()file_formatresponse.xpath(/html/body/div[5]/div[1]/div[1]/div[3]/div[1]/ul/li[3]/text()).get()file_sizeresponse.xpath(/html/body/div[5]/div[1]/div[1]/div[3]/div[1]/ul/li[4]/text()).get()download_countresponse.xpath(/html/body/div[5]/div[1]/div[1]/div[3]/div[1]/ul/li[5]/text()).get()# 3. 合并一级、二级页面数据统一输出yield{song_id:response.url.split(/)[-1].replace(.html,),# 从URL中提取歌曲IDsinger:base_data[singer],song_name:base_data[song_name],user_id:base_data[user_id],user_nickname:base_data[user_nickname],category:category,format:file_format,size:file_size,download_count:download_count}print(f✅ 完成歌曲《{base_data[song_name]}》详情页爬取数据已输出)2.4 优化第三步添加日志打印便于调试与监控优化前运行爬虫后无法直观看到爬取进度若出现失败也难以定位问题因此添加简单清晰的日志打印监控每一步爬取状态核心优化如下defparse(self,response):print(*50)print(✅ 首页请求成功开始提取热榜JSON数据...)html_textresponse.text patternrhot:(\[.*?\])hot_jsonre.findall(pattern,html_text,re.S)[0]hot_listjson.loads(hot_json)print(f✅ 成功提取{len(hot_list)}首热榜歌曲开始发起详情页请求)print(*50)# 后续循环构造请求...defparse_detail(self,response):base_dataresponse.meta song_namebase_data[song_name]print(f 正在爬取详情页{song_name}| URL{response.url})# 后续Xpath解析、数据合并...print(f✅{song_name}详情页爬取完成数据已输出)2.5 最终优化规范代码结构提升可维护性结合前面的踩坑与优化整理出完整可运行的爬虫代码同时规范请求头配置、字段命名全部改为英文符合开发规范避免冗余代码确保代码可直接复制运行。三、完整可运行代码3.1 爬虫文件spiders/5sing_spider.pyimportscrapyimportreimportjsonclassA5singSpider(scrapy.Spider):name5singallowed_domains[5sing.kugou.com]start_urls[https://5sing.kugou.com/index.html]# 配置请求头避免反爬custom_settings{DEFAULT_REQUEST_HEADERS:{accept:text/html,application/xhtmlxml,application/xml;q0.9,image/avif,image/webp,image/apng,*/*;q0.8,accept-language:zh-CN,zh;q0.9,upgrade-insecure-requests:1,user-agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36,cookie:kg_mid94ae5cb0ff9fda8bd1e89ea8c46853a4; kg_dfid3MqGF81g2Tq64cy0SV3kT5pr; sl-sessionZ1wSDz6f3GmaGwefEC9jzA; kg_dfid_collectd41d8cd98f00b204e9800998ecf8427e; ACK_SERVER_10015%7B%22list%22%3A%5B%5B%22bjlogin-user.kugou.com%22%5D%5D%7D; cct01259e80; wsp_volume0.8,}}defparse(self,response):print(*50)print(✅ 首页请求成功开始提取热榜JSON数据...)html_textresponse.text# 正则精准提取hot对应的JSON列表patternrhot:(\[.*?\])hot_jsonre.findall(pattern,html_text,re.S)[0]hot_listjson.loads(hot_json)print(f✅ 成功提取{len(hot_list)}首热榜歌曲开始发起详情页请求)print(*50)forsonginhot_list:# 用songId构造详情页URL避免KeyErrordetail_urlfhttps://5sing.kugou.com/bz/{song[songId]}.html# 传递基础数据到详情页yieldscrapy.Request(urldetail_url,callbackself.parse_detail,meta{singer:song[singerName],song_name:song[songName],user_id:song[userId],user_nickname:song[userNickname]})defparse_detail(self,response):base_dataresponse.meta song_namebase_data[song_name]print(f 正在爬取详情页{song_name}| URL{response.url})# Xpath解析详情页扩展信息categoryresponse.xpath(/html/body/div[5]/div[1]/div[1]/div[3]/div[1]/ul/li[2]/text()).get()file_formatresponse.xpath(/html/body/div[5]/div[1]/div[1]/div[3]/div[1]/ul/li[3]/text()).get()file_sizeresponse.xpath(/html/body/div[5]/div[1]/div[1]/div[3]/div[1]/ul/li[4]/text()).get()download_countresponse.xpath(/html/body/div[5]/div[1]/div[1]/div[3]/div[1]/ul/li[5]/text()).get()# 合并数据并输出yield{song_id:response.url.split(/)[-1].replace(.html,),singer:base_data[singer],song_name:base_data[song_name],user_id:base_data[user_id],user_nickname:base_data[user_nickname],category:category,format:file_format,size:file_size,download_count:download_count}print(f✅{song_name}详情页爬取完成数据已输出)3.2 运行命令# 进入Scrapy项目根目录执行以下命令scrapy crawl 5sing四、核心踩坑总结与实战经验4.1 核心踩坑点惯性思维误区默认所有页面都能用Xpath解析忽略了动态加载的JSON数据导致首页数据提取失败字段错误构造详情页URL时误使用不存在的“songUrl”字段引发KeyError调试困难初期未添加日志打印爬取失败后无法定位问题所在效率低下数据存储差异未提前分析首页与详情页的数据存储方式导致解析方式不匹配浪费开发时间。4.2 实战优化经验爬取前先分析页面源码通过Page Source查看数据存储方式是HTML静态渲染还是JSON动态加载再选择对应解析方式正则提取JSON需精准匹配使用rhot:(\[.*?\])精准捕获目标JSON片段避免匹配到无关内容同时添加re.S适配换行符多级请求数据传递使用Scrapy的meta参数将一级页面的基础数据传递到二级页面实现数据合并添加日志打印关键节点请求成功、数据提取、详情页爬取添加日志便于调试和监控爬取进度规范字段命名全部使用英文字段符合Python开发规范避免中文字段引发的编码问题。4.3 延伸思考本次爬取的5sing伴奏网首页用JSON存储热榜数据、详情页用HTML存储详情数据这种混合存储方式在很多网站中都很常见首页追求加载速度用JSON动态渲染详情页追求稳定性用静态HTML渲染。后续可进一步优化方向添加数据持久化将爬取的数据保存到CSV、JSON文件或数据库如SQLite完善反爬策略添加请求延迟、随机User-Agent避免频繁请求被封IP异常处理添加try-except捕获解析失败、请求失败等异常提升爬虫稳定性。五、总结本次Scrapy多级请求实战核心突破了“首页JSON动态加载、详情页HTML静态渲染”的混合数据存储解析难题从最初的Xpath解析失败到通过排查源码发现JSON字段再到切换为正则提取、完善二级请求和日志监控全程踩坑但收获颇丰。对于爬虫新手而言最容易陷入“惯性解析”的误区忽略页面数据存储的差异本次实战也再次验证爬取前的页面分析远比盲目编写代码更重要。掌握Scrapy多级请求、正则提取JSON、Xpath解析HTML的核心技巧能应对大多数网站的爬取需求后续可结合实际场景进一步优化爬虫的稳定性和效率。如果本文对你有帮助欢迎点赞、收藏、评论关注我持续分享Scrapy实战干货与爬取技巧

更多文章