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

Scrapy无Href分页问题:无法切换前一页表格的解决方法

解决Scrapy爬虫无href前一页按钮的翻页问题

嘿,这个场景我太熟悉了!这种不带href的翻页按钮,本质是靠JavaScript动态触发数据加载的,Scrapy本身不执行JS,所以不能用常规的提取链接方式,得换这几个思路来搞定:

1. 优先抓背后的AJAX接口(效率最高)

这类体育数据网站的翻页,大多是通过AJAX请求后端接口返回表格数据的。你可以这么操作:

  • 打开浏览器开发者工具(F12),切换到Network标签,筛选XHR/ Fetch类型的请求
  • 手动点击页面上的「« anterior」按钮,观察新出现的请求
  • 找到那个返回表格HTML或者JSON数据的请求,查看它的URL、请求方法(GET/POST)和携带的参数(比如page、team_id、block_id这类)

比如Soccerway的这类请求,通常会是类似https://br.soccerway.com/a/block_team_matches_summary的接口,参数里会包含当前页码、球队ID、区块ID(对应你看到的page_team_1_block_team_matches_summary_7里的team_matches_summary_7)。

拿到这些参数后,你就可以在Scrapy里直接构造请求,不用管前端的按钮了:

import scrapy

class RomaSpider(scrapy.Spider):
    name = 'roma_spider'
    start_urls = ['https://br.soccerway.com/teams/italy/as-roma/1241/']
    base_api_url = 'https://br.soccerway.com/a/block_team_matches_summary'

    def parse(self, response):
        # 先解析当前页面的表格数据
        self.parse_table(response)

        # 构造前一页的请求(假设当前页码是2,前一页是1,这里可以从页面提取当前页码)
        current_page = self.extract_current_page(response)
        if current_page > 1:
            yield scrapy.FormRequest(
                url=self.base_api_url,
                formdata={
                    'block_id': 'team_matches_summary_7',
                    'team_id': '1241',
                    'page': str(current_page - 1),
                    # 其他你抓到的参数,比如联赛ID之类的
                },
                callback=self.parse_table_response
            )

    def parse_table(self, response):
        # 你的表格数据解析逻辑
        for row in response.css('#page_team_1_block_team_matches_summary_7 table tr:not(.header)'):
            yield {
                'date': row.css('td.date::text').get(),
                'home_team': row.css('td.team a::text').get(),
                'away_team': row.css('td.team + td.team a::text').get(),
                'score': row.css('td.score a::text').get(),
            }

    def parse_table_response(self, response):
        # 解析AJAX返回的表格片段
        self.parse_table(response)
        # 继续递归翻页...

    def extract_current_page(self, response):
        # 从页面提取当前页码,比如找页码指示器里的active项
        return int(response.css('.pagination .active a::text').get())

2. 用浏览器自动化工具模拟点击(备选方案)

如果接口参数太复杂找不到规律,那就直接模拟用户点击按钮的操作,用Scrapy结合Playwright或者Selenium来执行JS加载页面。

这里以Playwright为例,需要先安装scrapy-playwright插件,然后配置你的Scrapy项目:

# settings.py里添加配置
DOWNLOAD_HANDLERS = {
    "http": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler",
    "https": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler",
}

PLAYWRIGHT_LAUNCH_OPTIONS = {
    "headless": True,  # 无头模式,不需要显示浏览器窗口
}

然后编写爬虫代码:

from scrapy import Spider
from scrapy_playwright.page import PageCoroutine

class RomaSpider(Spider):
    name = 'roma_spider'
    start_urls = ['https://br.soccerway.com/teams/italy/as-roma/1241/']

    def start_requests(self):
        for url in self.start_urls:
            yield scrapy.Request(
                url,
                meta={
                    'playwright': True,
                    'playwright_page_coroutines': [
                        # 点击前一页按钮
                        PageCoroutine('click', '#page_team_1_block_team_matches_summary_7_previous'),
                        # 等待表格加载完成
                        PageCoroutine('wait_for_selector', '#page_team_1_block_team_matches_summary_7 table'),
                    ],
                },
                callback=self.parse
            )

    def parse(self, response):
        # 解析加载后的表格数据
        self.parse_table(response)

        # 如果还有前一页,可以继续模拟点击(需要判断按钮是否可用,比如看class是否包含disabled)
        if 'disabled' not in response.css('#page_team_1_block_team_matches_summary_7_previous::attr(class)').get():
            yield scrapy.Request(
                response.url,
                meta={
                    'playwright': True,
                    'playwright_page_coroutines': [
                        PageCoroutine('click', '#page_team_1_block_team_matches_summary_7_previous'),
                        PageCoroutine('wait_for_selector', '#page_team_1_block_team_matches_summary_7 table'),
                    ],
                },
                callback=self.parse
            )

    def parse_table(self, response):
        # 你的表格解析逻辑
        for row in response.css('#page_team_1_block_team_matches_summary_7 table tr:not(.header)'):
            yield {
                'date': row.css('td.date::text').get(),
                'home_team': row.css('td.team a::text').get(),
                'away_team': row.css('td.team + td.team a::text').get(),
                'score': row.css('td.score a::text').get(),
            }

注意事项

  • 优先用第一种方法,因为直接请求接口的速度比模拟浏览器快很多,也更稳定
  • 如果用浏览器自动化,注意设置合理的等待时间,避免页面还没加载完就开始解析
  • 注意网站的反爬机制,不要请求太频繁,必要时添加延迟或者代理

内容的提问来源于stack exchange,提问作者Hiago Bonamelli

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 08:32:29