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
相关产品推荐
相关产品推荐

