如何优化Web Scraping脚本,提升大批量歌词数据的爬取速度?
歌词爬取效率优化方案
- 替换阻塞式同步请求为并发请求
当前代码的核心耗时瓶颈是urlopen的同步IO机制:每发起一次请求需要等待网络响应完全返回后才会执行后续逻辑,大部分时间都处于空闲等待状态。对于爬虫这类IO密集型任务,使用多线程/异步并发请求可以将整体耗时压缩到原来的1/5~1/10,10000首歌的爬取耗时可以控制在10分钟以内。
推荐直接用concurrent.futures.ThreadPoolExecutor实现多线程请求,改造成本极低,示例逻辑如下:
from concurrent.futures import ThreadPoolExecutor, as_completed import pandas as pd from urllib.request import Request, urlopen from bs4 import BeautifulSoup def url_lyric(music,artist): url_list = ("https://www.letras.mus.br/", str(artist),"/", str(music),"/") url = ''.join(url_list) req = Request(url, headers={'User-Agent': 'Mozilla/5.0'}) try: webpage = urlopen(req, timeout=10).read() # 替换为lxml解析器提升速度 bs = BeautifulSoup(webpage, 'lxml') lines =bs.find('div', {'class':'cnt-letra p402_premium'}) final_lines = lines.find_all('p') return final_lines except: return 0 # 先把所有需要爬取的(歌曲名,歌手)对整理成列表 task_list = [] for year in range(1920, 2021): # 从原DataFrame中提取对应年份的歌曲、歌手字段 year_songs = years_1920_2020[years_1920_2020['year']==year][['music','artist']].values.tolist() task_list.extend(year_songs) # 调整max_workers参数,建议设置为10~30,不要太高避免触发网站反爬 lyric_result = [] with ThreadPoolExecutor(max_workers=20) as executor: future_map = {executor.submit(url_lyric, music, artist): (music, artist) for music, artist in task_list} for future in as_completed(future_map): res = future.result() lyric_result.append(res) # 最后一次性转换为Series即可 final_lyric_series = pd.Series(lyric_result, name="lyrics")
- 优化解析与数据处理效率
- 把BeautifulSoup的解析器从
html.parser替换为lxml,解析速度可以提升3~5倍,只需提前执行pip install lxml安装依赖即可生效。 - 不要在循环中反复执行
pd.concat,每次拼接都会生成新的Series对象,存在大量冗余的内存拷贝开销。把所有结果存到普通列表中,所有爬取任务完成后再一次性转换为Series,能减少至少90%的拼接开销。
- 把BeautifulSoup的解析器从
- 补充缓存与重试机制
- 提前对DataFrame中的「歌手+歌曲」组合做去重处理,避免重复请求相同页面浪费时间。
- 给请求逻辑增加3次以内的重试,避免临时网络波动、网站响应超时导致的爬取失败,减少后续补爬的工作量。
内容的提问来源于stack exchange,提问作者Felipe Mezzarana
相关产品推荐
相关产品推荐

