如何使用Scrapy爬取带省略号且无href的分页按钮数据?
解决无Href分页及省略号的爬取方案
嘿,我来帮你搞定这个分页爬取的问题!这种不带href的分页按钮,本质都是前端通过动态逻辑加载数据的,咱们一步步拆解解决:
1. 先搞清楚分页的触发逻辑
打开浏览器开发者工具(按F12),切到Network标签,然后手动点击分页按钮(比如第2页、第3页),盯着新出现的请求看:
- 要么是XHR/fetch类型的AJAX请求,这类请求的URL里一般会带
page、pageNum这类参数,返回JSON或者HTML片段; - 要么是直接修改了原URL的查询参数(比如原URL是
xxx/sale,点第2页后变成xxx/sale?page=2),这种情况最简单,直接构造带参数的URL就行。
针对你爬的Forever21站点,我印象里它的分页就是用URL参数page控制的,你可以手动试试https://www.forever21.com/us/shop/catalog/category/f21/sale?page=2,应该就能打开第2页的内容。
2. 对付分页里的省略号(...)
省略号只是前端隐藏了中间页码,但只要找到了分页参数的规律,完全可以跳过省略号直接构造所有页码的URL:
- 先找总页数:可以看看页面底部有没有类似“共X页”的提示;或者点击最后一个可见页码(比如你说的5),看看还有没有下一页按钮,没有的话5就是总页数;
- 更灵活的方式是循环递增
page参数,直到请求回来的页面没有商品数据,自动停止爬取,不用提前知道总页数。
3. 修改你的Scrapy代码实现分页
给你改好了代码示例,以URL参数分页的情况为例,两种实现方式都给你写上:
方式1:提前指定总页数(适合已知总页数的情况)
def start_requests(self): base_url = 'https://www.forever21.com/us/shop/catalog/category/f21/sale?page={}' # 假设总页数是5,直接构造1到5页的URL for page in range(1, 6): url = base_url.format(page) yield scrapy.Request(url, callback=self.parse_2) def parse_2(self, response): for product_item_forever in response.css('div.pi_container'): forever_item = { 'forever-title': product_item_forever.css('p.p_name::text').extract_first(), 'forever-regular-price': product_item_forever.css('span.p_old_price::text').extract_first(), 'forever-sale-price': product_item_forever.css('span.p_sale.t_pink::text').extract_first(), 'forever-photo-url': product_item_forever.css('img::attr(data-original)').extract_first(), 'forever-description-url': product_item_forever.css('a.item_slider.product_link::attr(href)').extract_first(), } yield forever_item
方式2:自动翻页(不用提前知道总页数,更灵活)
def start_requests(self): # 先请求第1页 first_page_url = 'https://www.forever21.com/us/shop/catalog/category/f21/sale?page=1' yield scrapy.Request(first_page_url, callback=self.parse_2) def parse_2(self, response): # 先检查当前页面有没有商品,没有就直接返回停止爬取 product_items = response.css('div.pi_container') if not product_items: return # 提取商品数据 for product_item_forever in product_items: forever_item = { 'forever-title': product_item_forever.css('p.p_name::text').extract_first(), 'forever-regular-price': product_item_forever.css('span.p_old_price::text').extract_first(), 'forever-sale-price': product_item_forever.css('span.p_sale.t_pink::text').extract_first(), 'forever-photo-url': product_item_forever.css('img::attr(data-original)').extract_first(), 'forever-description-url': product_item_forever.css('a.item_slider.product_link::attr(href)').extract_first(), } yield forever_item # 构造下一页URL current_page = int(response.url.split('page=')[-1]) next_page_num = current_page + 1 next_page_url = f'https://www.forever21.com/us/shop/catalog/category/f21/sale?page={next_page_num}' # 发送下一页请求 yield scrapy.Request(next_page_url, callback=self.parse_2)
4. 如果是AJAX动态加载的情况(备用方案)
要是点击分页按钮时,页面没刷新而是发送了AJAX请求,那你需要:
- 在Network标签里找到那个AJAX请求的URL(比如类似
https://www.forever21.com/api/products?sale&page=2); - 复制请求的Headers(比如User-Agent、Referer这些,有些站点还需要token);
- 构造AJAX请求,然后解析返回的JSON数据来提取商品信息(这时候就不能用css选择器,得用
response.json()来处理)。
小提醒
- 爬的时候别太急,设置个
DOWNLOAD_DELAY = 2(在settings.py里),避免被站点封IP; - 用
scrapy shell 目标URL来测试选择器和URL,调试起来更方便。
内容的提问来源于stack exchange,提问作者Christian Read




