使用HTMLAgilityPack抓取Genius歌词时SelectSingleNode偶发返回null如何解决
根因分析
- 反爬策略拦截:Genius对未携带正常User-Agent的请求会随机返回拦截页面,不会返回正常歌词页HTML,自然无法找到对应节点,10次请求仅2次成功是反爬随机放行的典型表现
- 选择器匹配规则过严:Genius会动态给歌词节点附加额外类名(比如加载状态相关的类),使用的
@class='lyrics'是完全匹配规则,节点只要多了任意其他类名就会匹配失败 - 结构兼容问题:Genius新版歌词页面存在两套渲染逻辑,部分场景下歌词容器的类名为
Lyrics__Container而非lyrics,且部分内容是客户端异步加载的
优化方案
1. 补全请求头模拟浏览器行为
给请求添加正常的浏览器User-Agent,规避反爬拦截,不要使用HtmlWeb默认的空标识请求头
2. 优化XPath选择器为模糊匹配
用contains规则匹配包含对应类名的节点,不受附加类名影响
3. 兼容多套页面结构
同时匹配新旧版本的歌词容器节点,兜底覆盖所有场景
4. 增加有限次重试机制
不要用死循环检查,设置最多3次、每次间隔1~2秒的重试逻辑,既提升成功率也不会给站点造成过大压力触发封禁
优化后代码
HtmlWeb web = new HtmlWeb(); // 模拟Chrome浏览器的User-Agent web.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"; int retryCount = 0; HtmlDocument doc = null; while (retryCount < 3) { doc = web.Load("https://genius.com/song"); // 同时匹配新旧版本歌词容器 var node = doc.DocumentNode.SelectSingleNode("//div[contains(@class,'lyrics')] | //div[contains(@class,'Lyrics__Container')]"); if (node != null) { string lyrics = node.InnerText.Trim(); Console.WriteLine(lyrics); break; } retryCount++; // 重试间隔1秒 Thread.Sleep(1000); } Console.ReadLine();
内容的提问来源于stack exchange,提问作者Bellissimo
相关产品推荐
相关产品推荐

