Dart实现Genius歌词爬虫每次运行返回不同结果问题求助
问题根源
- Genius网站存在反爬机制、多版本页面AB测试逻辑,不同请求返回的HTML结构存在差异:
- 小概率返回旧版页面,歌词存储在
class="lyrics"的元素中 - 大部分场景返回新版页面,歌词存储在带动态后缀的
Lyrics__Container类名元素中 - 高频请求触发反爬规则时,会返回验证页/错误页,完全不存在歌词相关元素,此时调用
getElementsByClassName('lyrics')[0]就会抛出空数组索引越界的RangeError
- 小概率返回旧版页面,歌词存储在
- 多次运行输出结果不一致、偶现报错,就是上述页面动态变化直接导致的。
修复后的实现
修改code.dart代码,增加请求头伪装浏览器、兼容多版本歌词选择器、增加空安全判断即可:
import "dart:io"; import 'package:http/http.dart' as http; import 'package:html/parser.dart'; import 'package:html/dom.dart'; Future<String> scrapLyrics(String lyricsUrl) async { // 伪装浏览器请求头,降低反爬触发概率 final headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36" }; final response = await http.get(Uri.parse(lyricsUrl), headers: headers); var document = parse(response.body); // 先匹配旧版页面的lyrics类元素 List<Element> lyricsElements = document.getElementsByClassName('lyrics'); if (lyricsElements.isNotEmpty) { return lyricsElements.first.text.trim(); } // 旧版匹配不到就匹配新版页面前缀为Lyrics__Container的元素 lyricsElements = document.querySelectorAll('[class*="Lyrics__Container"]'); if (lyricsElements.isNotEmpty) { return lyricsElements.map((e) => e.text.trim()).join("\n"); } // 都匹配不到说明触发反爬,返回友好提示 return "未获取到歌词,可能触发网站反爬机制,请降低请求频率后重试"; } main() async { String ly; for (int i = 1; i <=10; i++) { print("Request number $i"); try{ ly= await scrapLyrics("https://www.genius.com/The-chainsmokers-closer-lyrics"); // 每次请求间隔2秒,避免高频触发反爬 await Future.delayed(const Duration(seconds: 2)); } catch(e){ ly=e.toString(); } File file = File("file$i.txt"); await file.writeAsString(ly); ly=""; } }
pubspec.yaml无需修改,沿用原有配置即可。
额外优化建议
如果后续还是频繁出现获取不到歌词的情况,可以:
- 进一步拉长请求间隔时间,建议调整为3-5秒每次
- 增加代理IP轮换逻辑,避免单IP高频请求被封禁
内容的提问来源于stack exchange,提问作者SHUVAM JASWAL
相关产品推荐
相关产品推荐

