如何加速基于BeautifulSoup的网页爬虫项目?
如何加速多URL网页爬虫的执行速度?
你的问题核心是串行请求导致的耗时过长——单个URL请求时,大部分时间都在等待网络响应(IO操作),串行循环会让每个请求都得等前一个完成才能开始,完全浪费了等待的时间。下面是几个针对性的优化方案:
1. 改用并发请求(最有效)
用多线程处理IO密集型任务,让多个URL请求同时进行,把等待时间重叠起来。Python的concurrent.futures.ThreadPoolExecutor可以轻松实现这一点,不需要复杂的线程管理:
import ssl import bs4 from urllib.request import Request, urlopen import json from concurrent.futures import ThreadPoolExecutor # 跳过SSL验证(注意:生产环境不建议这么做) ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE urls = ['https://www.tiffany.co.uk/jewelry/necklaces-pendants/tiffany-hardwear-graduated-link-necklace-63008966/', 'https://www.tiffany.co.uk/jewelry/necklaces-pendants/tiffany-t-smile-pendant-35189459/'] def get_price(url): req = Request(url, headers={'User-Agent': 'Mozilla/5.0'}) webpage = urlopen(req, context=ctx).read() # 换成更快的lxml解析器(需要先pip install lxml) soup = bs4.BeautifulSoup(webpage, "lxml") data = json.loads(soup.find_all('script', {'type': 'application/ld+json'})[-1].get_text()) return int(data['offers']['price']) # 设置线程数,建议根据你的机器配置和网站反爬限制调整,比如4-8个 with ThreadPoolExecutor(max_workers=4) as executor: # 批量提交任务并获取结果 results = executor.map(get_price, urls) for price in results: print(price)
2. 更换更快的HTML解析器
你当前用的html.parser是Python内置的,速度较慢。换成lxml解析器可以显著提升HTML解析的速度,只需要把bs4.BeautifulSoup(webpage, "html.parser")改成bs4.BeautifulSoup(webpage, "lxml"),记得先安装依赖:pip install lxml。
3. 复用HTTP连接(推荐改用requests库)
urllib的urlopen每次请求都会新建TCP连接,握手过程会额外耗时。改用requests库的Session对象可以复用连接,减少重复握手的开销,同时代码更简洁易读:
import bs4 import json import requests from concurrent.futures import ThreadPoolExecutor # 创建会话,复用连接 session = requests.Session() session.headers.update({'User-Agent': 'Mozilla/5.0'}) # 跳过SSL验证(生产环境谨慎使用) session.verify = False urls = ['https://www.tiffany.co.uk/jewelry/necklaces-pendants/tiffany-hardwear-graduated-link-necklace-63008966/', 'https://www.tiffany.co.uk/jewelry/necklaces-pendants/tiffany-t-smile-pendant-35189459/'] def get_price(url): response = session.get(url) soup = bs4.BeautifulSoup(response.text, "lxml") data = json.loads(soup.find_all('script', {'type': 'application/ld+json'})[-1].get_text()) return int(data['offers']['price']) with ThreadPoolExecutor(max_workers=4) as executor: results = executor.map(get_price, urls) for price in results: print(price)
4. 其他小优化
- 设置超时时间:给请求加上超时,避免某个请求卡住整个程序,比如
urlopen(req, context=ctx, timeout=10)或者session.get(url, timeout=10)。 - 避免重复解析不必要的内容:如果只需要提取JSON数据,可以不用完全解析HTML,直接用正则匹配
application/ld+json的内容,减少BeautifulSoup的工作量(不过可读性会下降)。 - 注意反爬限制:不要设置过高的并发数,避免被网站封禁IP,建议先测试小批量URL,观察网站的响应情况。
内容的提问来源于stack exchange,提问作者Seedizens
相关产品推荐
相关产品推荐

