You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何提升使用request模块发送GET请求获取并解析网页内容的速度

爬虫请求与解析速度优化方案

你感知到的tree = html.fromstring(page.content)行卡顿,大概率是请求IO等待+大页面解析冗余共同导致的,可从以下几个方向优化:

一、请求侧优化(解决80%以上的耗时问题)

  • 复用TCP会话:不要每次调用requests.get都新建连接,改用requests.Session()复用同域名下的连接,减少TCP握手、TLS认证开销,速度可提升30%以上。
  • 并行爬取:原代码是串行请求,一个URL请求结束才会发起下一个,爬虫属于IO密集型任务,改用多线程/异步IO并行发起请求,可根据站点并发限制调整同时请求数,速度可提升数倍到数十倍。
  • 开启压缩传输:显式指定请求头Accept-Encoding: gzip, deflate(requests默认已携带,可确认未被修改),降低响应体的传输体积,减少下载耗时。
  • 增加缓存:如果有重复爬取相同URL的场景,可接入缓存逻辑,爬过的内容直接读本地缓存,无需重复请求。

二、解析侧优化(针对html.fromstring卡顿问题)

lxml本身是Python生态中最快的HTML解析库之一,卡顿主要是解析冗余内容、遍历范围太大导致:

  • 定制解析器,跳过无用内容:创建解析器时配置参数,跳过注释、处理指令等无用内容,同时开启容错模式,减少无效解析开销:
# 全局只需要初始化一次解析器,不要放在循环里
fast_parser = html.HTMLParser(
    recover=True, # 容错,避免不规范HTML报错
    remove_comments=True, # 移除HTML注释
    remove_pis=True, # 移除处理指令
    no_network=True # 禁止加载外部资源
)
# 解析时传入定制解析器
tree = html.fromstring(page.content, parser=fast_parser)
  • 缩小XPath遍历范围:不要每次都从根节点全文档搜索,先定位到父节点再搜索子节点,减少遍历的DOM节点数:
# 原写法是多次全文档扫描
# 优化后先拿父节点
table_headers = tree_1.xpath("//tr[@id = 'tableHeader']")[0]
race_number = table_headers.xpath("./td[1]/text()")
Distance = table_headers.xpath("./td[3]/text()")
TGR_Grade = table_headers.xpath("./td[4]/text()")

tip_rows = tree_1.xpath("//tbody/tr[@class='fieldsTableRow raceTipsRow']//div")[0]
TGR1 = tip_rows.xpath("./span[1]/text()")
TGR2 = tip_rows.xpath("./span[2]/text()")
# 其余字段同理
  • 规则简单的场景直接用正则匹配:如果提取的字段规则非常固定,可直接对page.content的字符串做正则匹配,跳过DOM树构建流程,解析速度可提升50%以上。

三、其他代码优化

  • 替换重依赖调用:不要用pd.to_datetime('now').year获取当前年份,改用标准库datetime实现,开销降低一个数量级:
from datetime import date
year = date.today().year
  • 移除无用代码:原代码中clean_title变量定义后未使用,可直接删除。

优化后参考代码(核心部分)

import lxml
from lxml import html
import requests
import re
import pandas as pd
from requests.exceptions import ConnectionError
from datetime import date
from concurrent.futures import ThreadPoolExecutor, as_completed

# 全局初始化复用对象
session = requests.Session()
# 加请求头模拟浏览器,避免被站点限流
session.headers.update({
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
    "Accept-Encoding": "gzip, deflate"
})
fast_parser = html.HTMLParser(recover=True, remove_comments=True, remove_pis=True, no_network=True)
# 并发数可根据站点实际情况调整,建议不超过10避免被封
MAX_WORKERS = 8

greyhound_url = 'http://thegreyhoundrecorder.com.au/form-guides/'
def get_page(url):
    page = session.get(url)
    tree = html.fromstring(page.content, parser=fast_parser)
    my_list = tree.xpath('//tbody/tr/td[2]/a/@href')
    print('Length of all links = ', len(my_list))
    my_url = [page.url.split('/form-guides')[0] + str(s) for s in my_list]
    return my_url

def parse_single_page(t):
    page_detail = session.get(t)
    tree_1 = html.fromstring(page_detail.content, parser=fast_parser)
    title = ''.join(tree_1.xpath('//div/h1[@class="title"]/text()'))
    # 缩小XPath扫描范围
    try:
        table_header = tree_1.xpath("//tr[@id = 'tableHeader']")[0]
        race_number = table_header.xpath("./td[1]/text()")
        Distance = table_header.xpath("./td[3]/text()")
        TGR_Grade = table_header.xpath("./td[4]/text()")
        tip_row = tree_1.xpath("//tbody/tr[@class='fieldsTableRow raceTipsRow']//div")[0]
        TGR1 = tip_row.xpath("./span[1]/text()")
        TGR2 = tip_row.xpath("./span[2]/text()")
        TGR3 = tip_row.xpath("./span[3]/text()")
        TGR4 = tip_row.xpath("./span[4]/text()")
    except IndexError:
        # 处理结构异常的页面
        return None
    Track = title.split(' ')[0].strip()
    date_str = title.split('-')[1].strip()
    year = date.today().year 
    race_date =  pd.to_datetime(date_str + ' ' + str(year)).strftime('%d/%m/%Y')
    new_rn = []
    for number in race_number:
        match = re.search(r'^(.).*?(\d+)$', number)
        if match:
            new_rn.append(match.group(1) + match.group(2))
    return (race_date,Track,new_rn,Distance,TGR_Grade,TGR1,TGR2,TGR3,TGR4)

def extract_data(my_url):
    new_list = []
    try:
        with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
            futures = [executor.submit(parse_single_page, t) for t in my_url]
            for future in as_completed(futures):
                res = future.result()
                if res:
                    new_list.append(res)
        return new_list
    except ConnectionError as e:
        print('Connection error, connect to a stronger network or reload the page')

内容的提问来源于stack exchange,提问作者chuky pedro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 01:48:03