Scrapy设置之提前终止爬虫

本文介绍了Scrapy框架中CloseSpider扩展的使用方法及其配置项,包括如何通过设置不同的参数来控制爬虫程序的自动终止条件,如时间限制、抓取的item数量等。

转载自  https://blog.youkuaiyun.com/Q_AN1314/article/details/51104701

Scrapy的CloseSpider扩展会在满足条件时自动终止爬虫程序。可以设置CLOSESPIDER_TIMEOUT(秒)、CLOSESPIDER_ITEMCOUNTCLOSESPIDER_PAGECOUNTCLOSESPIDER_ERRORCOUNT分别代表在指定时间过后、在抓取了指定数目的Item之后、在收到了指定数目的响应之后、在发生了指定数目的错误之后就终止爬虫程序。通常情况下可以在命令行中设置:

$ scrapy crawl fast -s CLOSESPIDER_ITEMCOUNT=10
$ scrapy crawl fast -s CLOSESPIDER_PAGECOUNT=10
$ scrapy crawl fast -s CLOSESPIDER_TIMEOUT=10
<think>我们面对的问题是如何在Scrapy爬虫终止parse函数。在给定的代码中,parse函数通过递归调用自己(通过生成新的请求)来翻页爬取,直到page>=100时停止生成新的请求。但是,如果我们想要在某个条件下提前终止(例如,当某一页没有数据时,或者满足某个特定条件时),应该如何操作? 在Scrapy中,终止爬虫通常有以下几种方式: 1. 不yield新的Request对象,这样就不会产生新的请求,当所有请求处理完毕后爬虫自然结束。 2. 抛出CloseSpider异常,立即终止爬虫。 在给定的代码中,终止parse函数其实就是停止生成新的请求(即不再yield新的scrapy.Request)。因为parse函数是通过递归调用来翻页的,所以只要在某个条件下不执行yield scrapy.Request,那么就不会有新的请求产生,从而在完成当前页的解析后,如果队列中没有其他请求,爬虫就会停止。 但是,如果我们希望提前终止整个爬虫,可以使用CloseSpider异常。例如,当某一页没有数据时,我们可以选择停止整个爬虫。 下面我们具体分析代码: 当前代码中,当self.page<100时,会生成下一页的请求。如果我们想要在某个条件下提前终止,可以在循环结束后添加一个条件判断,例如当没有获取到任何数据时,我们就不生成新的请求,这样就不会继续翻页了。 但是,如果我们想要立即停止整个爬虫,可以在需要终止的地方抛出CloseSpider异常。 例如: from scrapy.exceptions import CloseSpider 然后在需要终止的地方: if no_data_condition: raise CloseSpider('终止原因') 但是注意,在parse函数中抛出CloseSpider异常会立即终止整个爬虫,包括所有正在进行的请求。 根据用户的问题,他可能只是想要停止parse函数的递归调用(即停止翻页),而不是立即停止整个爬虫。所以,我们只需要在某个条件下不生成新的请求即可。 在用户给出的代码中,我们可以在循环结束后判断是否满足终止条件,然后决定是否生成下一页请求。 例如,我们可以在循环中统计当前页解析到的项目数量,如果数量为0,说明这一页没有数据,那么就不再生成下一页请求。 修改后的代码示例: 在parse函数中: count = 0 for lii in lii_list: ... # 解析数据 count += 1 if count == 0: # 这一页没有数据,不再翻页 return if self.page < 100: ... # 生成下一页 或者,如果我们希望立即停止整个爬虫(包括可能正在处理的其他请求),则使用CloseSpider: from scrapy.exceptions import CloseSpider ... if count == 0: raise CloseSpider('没有数据,终止爬虫') 根据需求选择合适的方式。 注意:在Scrapy中,parse函数是通过生成器来工作的,所以即使我们想提前退出函数,也可以使用return(在生成器中,return表示生成器结束,不会产生新的项,但已经yield的请求或item会正常处理)。 因此,针对用户代码,我们可以这样修改: 如果我们只是要停止翻页(即不再递归调用parse),那么可以在某个条件下(比如当前页没有解析到任何数据)跳过生成新请求的代码。 修改后的代码片段如下: lii_list = response.xpath('//ul[@id="component_59"]//li') if not lii_list: # 如果当前页没有找到任何li元素,说明可能没有数据 # 可以选择不生成下一页请求,直接返回 return count = 0 for lii in lii_list: ... # 原来的解析代码 count += 1 # 或者我们也可以根据count的值来判断,如果count为0,说明没有解析到任何图书 if count == 0: return # 不再生成新的请求,这样就不会继续翻页了 if self.page < 100: ... # 生成下一页请求 但是,这里要注意,用户代码中每次翻页都会增加page,然后生成新的请求。如果我们因为某一页没有数据而停止,那么爬虫会继续处理队列中可能存在的其他请求(如果有的话)。但在这个爬虫中,只有这个parse函数生成的请求,所以当没有新的请求生成时,爬虫就会停止。 另外,有时候我们可能希望即使当前页没有数据,也要继续翻页(因为可能是偶然情况),所以终止条件需要根据实际情况设定。 总结:终止parse函数的递归调用(即翻页)的方法就是在满足条件时不生成新的请求(即跳过`yield scrapy.Request`的代码)。 如果用户想要在特定条件下立即停止整个爬虫,则应该使用`CloseSpider`异常。 下面我们给出两种方式的代码示例: 方式一:停止翻页(不生成新请求) 在循环结束后判断当前页是否解析到数据,如果没有则不再生成下一页请求。 方式二:立即终止整个爬虫 引入CloseSpider异常,并在条件满足时抛出。 根据用户的问题描述,他可能只是希望停止parse函数的递归调用,所以我们采用方式一。 修改后的完整代码(仅修改parse函数)如下: ```python import scrapy from ddang.items import DdangItem from scrapy.exceptions import CloseSpider # 如果使用方式二,需要导入 class DangSpider(scrapy.Spider): name = "dang" allowed_domains = ["category.dangdang.com"] start_urls = ["https://category.dangdang.com/cp01.01.02.00.00.00.html"] base_url='https://category.dangdang.com/pg' page=1 def parse(self, response): print('===============================================') print('===============================================') print('===============================================') print('===============================================') lii_list=response.xpath('//ul[@id="component_59"]//li') # 如果没有找到任何li,则lii_list为空列表,不执行循环 count = 0 for lii in lii_list: src=lii.xpath('.//img/@data-original').extract_first() if src: src=src else: src=lii.xpath('.//img/@src').extract_first() name=lii.xpath('.//img/@alt').extract_first() price=lii.xpath('.//p[@class="price"]/span[1]/text()').extract_first() print(src,name,price) book=DdangItem(src=src,name=name,price=price) yield book count += 1 # 每解析一个就计数 # 方式一:如果这一页没有解析到任何图书,则不再翻页 if count == 0: # 注意:这里只是停止生成新的请求,但已经发出去的请求(如果有)还会继续处理 # 因为在这个爬虫中,只有parse函数自己生成的请求,所以当不再生成新请求时,爬虫在完成已有请求后就会停止。 return # 方式二:也可以选择抛出CloseSpider异常立即终止整个爬虫(包括其他请求) # 注意:使用方式二时需要导入CloseSpider # if count == 0: # raise CloseSpider('没有解析到数据,爬虫终止') if self.page<100: self.page=self.page+1 url=self.base_url+str(self.page)+'-cp01.01.02.00.00.00.html' # 注意:原代码中这里是02,但起始URL是01.01.02,所以保持一致。用户原代码中翻页的url后缀是'...-cp01.02.00.00.00.html',但起始URL是'cp01.01.02.00.00.00.html',这里可能不一致,需要用户确认。但这不是本次问题的重点。 # 为了确保正确,这里按照用户原来的翻页URL格式 # 注意:用户原代码中翻页URL是'...-cp01.02.00.00.00.html',而起始URL是'cp01.01.02.00.00.00.html',所以可能是不同的分类。这里我们按照用户原来的写法。 yield scrapy.Request(url=url,callback=self.parse) ``` 注意:用户原代码中翻页URL的构造可能存在问题,因为起始URL是"cp01.01.02.00.00.00.html",而翻页URL是"cp01.02.00.00.00.html",这可能是两个不同的分类。但用户没有提出这个问题,所以我们只关注如何终止parse函数。 在上面的修改中,我们添加了一个计数器count,当解析完一页后,如果count为0(即该页没有数据),则不再生成下一页请求,从而终止翻页。 最后,我们生成相关问题。 </think> 在Scrapy爬虫终止`parse`函数(停止后续请求)有两种主要方法,具体实现取决于需求场景: ### 方法1:条件控制停止翻页(推荐) 通过修改翻页逻辑,在满足条件时不生成新请求。这是最常用的方法,不会中断正在处理的请求队列: ```python def parse(self, response): # ... 原有的数据解析代码 ... # 添加终止条件(示例:当价格为空时停止) price_empty_found = False for lii in lii_list: # ... 解析代码 ... if not price: # 发现空价格时标记 price_empty_found = True # 翻页控制(同时检查终止条件) if self.page < 100 and not price_empty_found: self.page += 1 url = f"{self.base_url}{self.page}-cp01.01.02.00.00.00.html" yield scrapy.Request(url=url, callback=self.parse) else: self.logger.info(f"终止翻页:page={self.page}, 触发条件:price_empty_found={price_empty_found}") ``` ### 方法2:强制停止爬虫(立即终止) 使用Scrapy内置异常`CloseSpider`立即停止整个爬虫进程: ```python from scrapy.exceptions import CloseSpider # 添加导入 def parse(self, response): # ... 原有的数据解析代码 ... # 强制终止条件(示例:遇到空书名时) for lii in lii_list: name = lii.xpath('.//img/@alt').extract_first() if not name: raise CloseSpider("检测到空书名,强制终止爬虫") # 立即停止 # ... 翻页代码 ... ``` ### 关键区别说明 | **方法** | 适用场景 | 行为特点 | |----------------|---------------------------------|----------------------------| | 条件控制翻页 | 优雅停止后续请求 | 完成当前页处理,不产生新请求 | | `CloseSpider` | 发现严重错误需立即停止 | 整个爬虫进程立即终止 | ### 针对您的代码优化建议 ```python def parse(self, response): # ... 打印和解析代码保持不变 ... # 添加终止条件检查 valid_items = 0 for lii in lii_list: # ... 解析代码 ... if src and name and price: # 关键数据都存在 valid_items += 1 yield DdangItem(src=src, name=name, price=price) # 终止条件:有效数据<5 或 达到100页 if valid_items < 5 or self.page >= 100: self.logger.warning(f"停止爬取:第{self.page}页有效数据不足") return # 不再生成新请求 # 继续翻页 self.page += 1 yield scrapy.Request( url=f"{self.base_url}{self.page}-cp01.01.02.00.00.00.html", callback=self.parse ) ``` > **注意事项** > 1. 翻页URL中的分类代码`cp01.02.00.00.00`与起始URL的`cp01.01.02.00.00.00`不一致,可能导致翻页失败 [^2] > 2. 建议使用`/pg1`代替`/pg1-`,观察目标网站实际翻页规则 > 3. 使用Scrapy的`logging`模块替代`print`:`self.logger.debug("状态提示")`
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值