python捕获所有异常状态_如何在scrapy中捕获并处理各种异常
前言
使用scrapy进行大型爬取任务的时候(爬取耗时以天为单位),无论主机网速多好,爬完之后总会发现scrapy日志中“item_scraped_count”不等于预先的种子数量,总有一部分种子爬取失败,失败的类型可能有如下图两种(下图为scrapy爬取结束完成时的日志):
scrapy中常见的异常包括但不限于:download error(蓝色区域), http code 403/500(橙色区域)。
不管是哪种异常,我们都可以参考scrapy自带的retry中间件写法来编写自己的中间件。
正文
使用IDE,现在scrapy项目中任意一个文件敲上以下代码:
from scrapy.downloadermiddlewares.retry import RetryMiddleware
按住ctrl键,鼠标左键点击RetryMiddleware进入该中间件所在的项目文件的位置,也可以通过查看文件的形式找到该中间件的位置,路径是:site-packages/scrapy/downloadermiddlewares/retry.RetryMiddleware
该中间件的源代码如下:
class RetryMiddleware(object):
# IOError is raised by the HttpCompression middleware when trying to
# decompress an empty response
EXCEPTIONS_TO_RETRY = (defer.TimeoutError, TimeoutError, DNSLookupError,
ConnectionRefusedError, ConnectionDone, ConnectError,
ConnectionLost, TCPTimedOutError, ResponseFailed,
IOError, TunnelError)
def __init__(self, settings):
if not settings.getbool('RETRY_ENABLED'):
raise NotConfigured
self.max_retry_times = settings.getint('RETRY_TIMES')
self.retry_http_codes = set(int(x) for x in settings.getlist('RETRY_HTTP_CODES'))
self.priority_adjust = settings.getint('RETRY_PRIORITY_ADJUST')
@classmethod
def from_crawler(cls, crawler):
return cls(crawler.settings)
def process_response(self, request, response, spider):
if request.meta.get('dont_retry', False):
return response
if response.status in self.retry_http_codes:
reason = response_status_message(response.status)
return self._retry(request, reason, spider) or response
return response
def process_exception(self, request, exception, spider):
if isinstance(exception, self.EXCEPTIONS_TO_RETRY) \
and not request.meta.get('dont_retry', False):
return self._retry(request, exception, spider)
def _retry(self, request, reason, spider):
retries = request.meta.get('retry_times', 0) + 1
retry_times = self.max_retry_times
if 'max_retry_times' in request.meta:
retry_times = request.meta['max_retry_times']
stats = spider.crawler.stats
if retries <= retry_times:
logger.debug("Retrying %(request)s (failed %(retries)d times): %(reason)s",
{'request': request, 'retries': retries, 'reason': reason},
extra={'spider': spider})
retryreq = request.copy()
retryreq.meta['retry_times'] = retries
retryreq.dont_filter = True
retryreq.priority = request.priority + self.priority_adjust
if isinstance(reason, Exception):
reason = global_object_name(reason.__class__)
stats.inc_value('retry/count')
stats.inc_value('retry/reason_count/%s' % reason)
return retryreq
else:
stats.inc_value('retry/max_reached')
logger.debug("Gave up retrying %(request)s (failed %(retries)d times): %(reason)s",
{'request': request, 'retries': retries, 'reason': reason},
extra={'spider': spider})
查看源码我们可以发现,对于返回http code的response,该中间件会通过process_response方法来处理,处理办法比较简单,大概是判断response.status是否在定义好的self.retry_http_codes集合中,通过向前查找,这个集合是一个列表,定义在default_settings.py文件中,定义如下:
RETRY_HTTP_CODES = [500, 502, 503, 504, 522, 524, 408]
也就是先判断http code是否在这个集合中,如果在,就进入retry的逻辑,不在集合中就直接return response。这样就已经实现对返回http code但异常的response的处理了。
但是对另一种异常的处理方式就不一样了,刚才的异常准确的说是属于HTTP请求error(超时),而另一种异常发生的时候则是如下图这种实实在在的代码异常(不处理的话):
你可以创建一个scrapy项目,start_url中填入一个无效的url即可模拟出此类异常。比较方便的是,在RetryMiddleware中同样提供了对这类异常的处理办法:process_exception
通过查看源码,可以分析出大概的处理逻辑:同样先定义一个集合存放所有的异常类型,然后判断传入的异常是否存在于该集合中,如果在(不分析dont try)就进入retry逻辑,不在就忽略。
OK,现在已经了解了scrapy是如何捕捉异常了,大概的思路也应该有了,下面贴出一个实用的异常处理的中间件模板:
from twisted.internet import defer
from twisted.internet.error import TimeoutError, DNSLookupError, \
ConnectionRefusedError, ConnectionDone, ConnectError, \
ConnectionLost, TCPTimedOutError
from scrapy.http import HtmlResponse
from twisted.web.client import ResponseFailed
from scrapy.core.downloader.handlers.http11 import TunnelError
class ProcessAllExceptionMiddleware(object):
ALL_EXCEPTIONS = (defer.TimeoutError, TimeoutError, DNSLookupError,
ConnectionRefusedError, ConnectionDone, ConnectError,
ConnectionLost, TCPTimedOutError, ResponseFailed,
IOError, TunnelError)
def process_response(self,request,response,spider):
#捕获状态码为40x/50x的response
if str(response.status).startswith('4') or str(response.status).startswith('5'):
#随意封装,直接返回response,spider代码中根据url==''来处理response
response = HtmlResponse(url='')
return response
#其他状态码不处理
return response
def process_exception(self,request,exception,spider):
#捕获几乎所有的异常
if isinstance(exception, self.ALL_EXCEPTIONS):
#在日志中打印异常类型
print('Got exception: %s' % (exception))
#随意封装一个response,返回给spider
response = HtmlResponse(url='exception')
return response
#打印出未捕获到的异常
print('not contained exception: %s'%exception)
spider解析代码示例:
class TESTSpider(scrapy.Spider):
name = 'TEST'
allowed_domains = ['TTTTT.com']
start_urls = ['http://www.TTTTT.com/hypernym/?q=']
custom_settings = {
'DOWNLOADER_MIDDLEWARES': {
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
'TESTSpider.middlewares.ProcessAllExceptionMiddleware': 120,
},
'DOWNLOAD_DELAY': 1, # 延时最低为2s
'AUTOTHROTTLE_ENABLED': True, # 启动[自动限速]
'AUTOTHROTTLE_DEBUG': True, # 开启[自动限速]的debug
'AUTOTHROTTLE_MAX_DELAY': 10, # 设置最大下载延时
'DOWNLOAD_TIMEOUT': 15,
'CONCURRENT_REQUESTS_PER_DOMAIN': 4 # 限制对该网站的并发请求数
}
def parse(self, response):
if not response.url: #接收到url==''时
print('500')
yield TESTItem(key=response.meta['key'], _str=500, alias='')
elif 'exception' in response.url:
print('exception')
yield TESTItem(key=response.meta['key'], _str='EXCEPTION', alias='')
Note:该中间件的Order_code不能过大,如果过大就会越接近下载器,就会优先于RetryMiddleware处理response,但这个中间件是用来兜底的,即当一个response 500进入中间件链时,需要先经过retry中间件处理,不能先由我们写的中间件来处理,它不具有retry的功能,接收到500的response就直接放弃掉该request直接return了,这是不合理的。只有经过retry后仍然有异常的request才应当由我们写的中间件来处理,这时候你想怎么处理都可以,比如再次retry、return一个重新构造的response。
下面来验证一下效果如何(测试一个无效的URL),下图为未启用中间件的情况:
再启用中间件查看效果:
ok,达到预期效果:即使程序运行时抛出异常也能被捕获并处理。
到此这篇关于如何在scrapy中捕获并处理各种异常的文章就介绍到这了,更多相关scrapy 捕获处理异常内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
python捕获所有异常状态_如何在scrapy中捕获并处理各种异常相关推荐
- [Scrapy使用技巧] 如何在scrapy中捕获并处理各种异常
前言 使用scrapy进行大型爬取任务的时候(爬取耗时以天为单位),无论主机网速多好,爬完之后总会发现scrapy日志中"item_scraped_count"不等于预先的 ...
- python绘图背景透明_如何在 Matplotlib 中更改绘图背景
介绍Matplotlib是Python中使用最广泛的数据可视化库之一.无论是简单还是复杂的可视化项目,它都是大多数人的首选库.在本教程中,我们将研究如何在Matplotlib中更改绘图的背景.导入数据 ...
- 如何在scrapy中捕获并处理各种异常
前言 使用scrapy进行大型爬取任务的时候(爬取耗时以天为单位),无论主机网速多好,爬完之后总会发现scrapy日志中"item_scraped_count"不等于预先的 ...
- python的loc函数_如何在pandas中使用loc、iloc函数进行数据索引(入门篇)
在数据分析过程中,很多时候我们需要从数据表中提取出我们需要的部分,而这么做的前提是我们需要先索引出这一部分数据.今天我们就来探索一下,如何在pandas中使用loc函数和iloc函数索引数据. 今天我 ...
- python 运行r语言_如何在R中运行Python
python 运行r语言 尽管我很喜欢R,但很显然Python还是一种很棒的语言-既适用于数据科学又适用于通用计算. R用户想要在Python中做一些事情可能有充分的理由. 也许这是一个很棒的库,还没 ...
- python大于等于怎么表示_如何在rejectdb中应用python lambda表达式中的大于等于
我在reinstdb表中有下面的json记录.在[{ "pid": 12, "sk": [ { "sid": 30, "et&qu ...
- python tensorflow 文本提取_如何在tensorflow中保存文本分类模型?
阅读tensorflow documentation进行文本分类时,我在下面建立了一个脚本,用于训练文本分类模型(正/负).有一件事我不确定.如何保存模型以便以后重用?另外,如何测试我拥有的输入测试集 ...
- python tkinter frame滚动条_如何在Tkinter中创建带有滚动条的LabelFrame?
我正在使用Python和Tkinter为我正在编写的程序创建一个GUI,我遇到了一些问题. 我有三个从LabelFrame派生的对象在一个从Frame派生的对象中.LabelFrame子代之一是对应的 ...
- linux查询python的进程树_如何在Linux中查看所有正在运行的进程
你可以使用ps命令.它能显示当前运行中进程的相关信息,包括进程的PID.Linux和UNIX都支持ps命令,显示所有运行中进程的相关信息.ps命令能提供一份当前进程的快照.如果你想状态可以自动刷新,可 ...
最新文章
- Vs.net2008 下 Ajaxpro 使用
- 服务器出现 nginx 502 Bad Gateway
- UE4学习-4.25版本Possess无法继承、UNavigationSystem命名空间找不到的解决方法
- 前端学习之BOM(浏览器对象模型)
- linux免密后还是要输密码,ssh配置免密后依然需要输入密码的问题解决及排查过程...
- Odoo10教程---模块化三:模型约束,高级视图,工作流,安全性,向导,国际化和报表等
- 医学方面的创业计划书_【就业创业】设计学院开展2019“汉军杯”大学生创业大赛 初评及复评工作...
- 1093 字符串A+B (20分)
- code4906 删数问题
- 南邮JAVA程序设计实验3 流处理程序设计
- 户籍管理系统php,vue实现户籍管理系统的实例解析
- 乖离率背离公式_掌握这“八大底部买入形态+主力抄底逃顶选股公式”,把握底部起涨点...
- 如何创建水晶报表模板
- 电子书PDF文件的压缩实践
- 鹏鹏seo第三课:长尾关键词的认识!
- C++snprintf的使用
- 《网络安全应急响应技术实战指南》知识点总结(第9章 数据泄露网络安全应急响应)
- 越野越激情——“中国东川泥石流国际汽车越野赛”
- outlook 2007 通讯录分组导出导入
- Springboot+Redis初体验