Scrapy POST爬取多页 不同serial参数批量爬取实现方法
全量数据爬取实现方法
整体流程分为两个阶段,先拿全所有有效serial参数,再批量构造详情页请求:
- 第一阶段:访问列表页,逐页提取页面中嵌入的所有详情页对应
serial值,处理完所有列表分页 - 第二阶段:遍历去重后的
serial集合,逐个构造POST请求抓取详情页内容
具体代码修改
- 初始请求不要直接写死参数请求详情接口,改为先访问列表页,由浏览器自动维护有效Cookie,避免硬编码Cookie过期失效
- 新增列表页解析逻辑,从页面DOM中提取
serial:这类站点的serial一般存放在详情提交按钮所在表单的隐藏域、按钮自定义属性或者跳转链接参数中,根据实际DOM结构写XPath提取即可 - 处理列表分页逻辑,抓全所有分页的
serial后再批量发起详情请求 - 用
urlencode构造POST请求体,避免手动拼接字符串出现编码错误
改完的完整参考代码如下:
import scrapy from urllib.parse import urlencode from scrapy_selenium import SeleniumRequest class TestSpider(scrapy.Spider): name = 'test' list_url = "https://www.benrishi-navi.com/english/english1_2.php" detail_url = "https://www.benrishi-navi.com/english/english1_3.php" # 存储已爬取的serial,避免重复请求 seen_serial = set() headers = { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', 'Accept-Language': 'en-GB,en-US;q=0.9,en;q=0.8,pt;q=0.7', 'Content-Type': 'application/x-www-form-urlencoded', 'Origin': 'https://www.benrishi-navi.com', 'Referer': 'https://www.benrishi-navi.com/english/english1_2.php', 'Sec-Fetch-Dest': 'document', 'Sec-Fetch-Mode': 'navigate', 'Sec-Fetch-Site': 'same-origin', 'Sec-Fetch-User': '?1', 'Upgrade-Insecure-Requests': '1', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36', 'sec-ch-ua': '".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"Windows"' } def start_requests(self): # 先访问列表页,自动获取站点下发的有效Cookie yield SeleniumRequest( url=self.list_url, wait_time=2, headers=self.headers, callback=self.parse_list, dont_filter=True, ) def parse_list(self, response): # 提取当前页所有serial,示例XPath针对serial存于表单隐藏域的场景,可根据实际DOM结构调整 serial_list = response.xpath("//form[contains(@action,'english1_3.php')]//input[@name='serial']/@value").getall() for serial in serial_list: if serial not in self.seen_serial: self.seen_serial.add(serial) # 构造详情页POST请求 payload = urlencode({ 'serial': serial, 'office_serial': '', 'submit2': 'Details' }) yield SeleniumRequest( url=self.detail_url, method='POST', body=payload, wait_time=2, headers=self.headers, callback=self.parse_detail, dont_filter=True, ) # 处理列表分页,提取下一页地址,存在则继续爬取下一页内容 next_page = response.xpath("//a[contains(text(),'Next')]/@href").get() if next_page: yield SeleniumRequest( url=response.urljoin(next_page), wait_time=2, headers=self.headers, callback=self.parse_list, dont_filter=True, ) def parse_detail(self, response): # 解析详情页数据 item = {} item['title'] = response.xpath("//td[@class='kiso']//text()").getall() # 其余字段按业务需求补充提取逻辑 yield item
注意事项
- 提取
serial的XPath需要和实际页面对应,如果Details按钮是通过onclick事件传参跳转,对应调整XPath从onclick属性值中提取serial数值即可 - 如果站点对请求频率有限制,可以适当调大SeleniumRequest的
wait_time参数,或者在配置中设置DOWNLOAD_DELAY降低并发,避免被拦截 - 不需要硬编码Cookie值,Selenium启动浏览器访问列表页时会自动生成并携带站点下发的有效Cookie,写死的Cookie过有效期后会直接导致请求失败
内容的提问来源于stack exchange,提问作者Amen Aziz
相关产品推荐
相关产品推荐

