Python 装饰器装饰类中的方法这篇文章,使用了装饰器来捕获代码异常。这种方式可以让代码变得更加简洁和Pythonic。

在写代码的过程中,处理异常并重试是一个非常常见的需求。但是如何把捕获异常并重试写得简洁高效,这就是一个技术活了。

以爬虫开发为例,由于网页返回的源代码有各种不同的情况,因此捕获异常并重试是很常见的要求。下面这几段代码是我多年以前,在刚开始学习爬虫的时候,由于捕获异常并重试导致代码混乱化过程。

代码一开始的逻辑非常简单,获取网页后台API返回的JSON字符串,转化成字典,提取出里面data的数据,然后传递给save()函数:

def extract(url):
info_json = requests.get(url).content.decode()
info_dict = json.loads(info_json)
data = info_dict['data']
save(data)

代码运行一段时间,发现有时候JSON会随机出现解析错误。于是添加捕获异常并重试的功能:

def extract(url):
info_json = requests.get(url).text
try:
info_dict = json.loads(info_json)
except Exception:
print('网页返回的不是有效的JSON格式字符串,重试!')
extract(url)
return
data = info_dict['data']
save(data)

后来又发现,有部份的URL会导致递归深度超过最大值。这是因为有一些URL返回的是数据始终是错误的,而有些URL,重试几次又能返回正常的JSON数据,于是限制只重试3次:

def extract(url):
info_json = requests.get(url).text
try:
info_dict = json.loads(info_json)
except Exception:
print('网页返回的不是有效的JSON格式字符串,重试!')
for i in range(3):
if extract(url):
break data = info_dict['data']
save(data)
return True

后来又发现,不能立刻重试,重试要有时间间隔,并且时间间隔逐次增大......

从上面的例子中可以看到,对于异常的捕获和处理,一不小心就让整个代码变得很难看很难维护。为了解决这个问题,就需要通过装饰器来完成处理异常并重试的功能。

Python 有一个第三方库,叫做Tenacity,它实现了一种优雅的重试功能。

以上面爬虫最初的无限重试版本为例,如果想实现遇到异常就重试。只需要添加两行代码,爬虫的主体函数完全不需要做修改:

from tenacity import retry

@retry
def extract(url):
info_json = requests.get(url).content.decode()
info_dict = json.loads(info_json)
data = info_dict['data']
save(data)

现在要限制重试次数为3次,代码总行数不需要新增一行就能实现:

from tenacity import retry

@retry(stop=stop_after_attempt(3))
def extract(url):
info_json = requests.get(url).content.decode()
info_dict = json.loads(info_json)
data = info_dict['data']
save(data)

现在想每5秒钟重试一次,代码行数也不需要增加:

from tenacity import retry

@retry(wait=wait_fixed(5))
def extract(url):
info_json = requests.get(url).content.decode()
info_dict = json.loads(info_json)
data = info_dict['data']
save(data)

甚至重试的时间间隔想指数级递增,代码行数也不需要增加:

from tenacity import retry

@retry(wait=wait_exponential(multiplier=1, max=10)) # 重试时间间隔满足:2^n * multiplier, n为重试次数,但最多间隔10秒
def extract(url):
info_json = requests.get(url).content.decode()
info_dict = json.loads(info_json)
data = info_dict['data']
save(data)

重试不仅可以限制次数和间隔时间,还可以针对特定的异常进行重试。在爬虫主体中,其实有三个地方可能出现异常:

  • requests获取网页出错
  • 解析JSON出错
  • info_dict字典里面没有data这个key

如果只需要在JSON解析错误时重试,由于异常类型为json.decoder.JSONDecodeError,所以就可以通过参数来进行限制:

from tenacity import retry
from json.decoder import JSONDecodeError @retry(retry=retry_if_exception_type(JSONDecodeError))
def extract(url):
info_json = requests.get(url).content.decode()
info_dict = json.loads(info_json)
data = info_dict['data']
save(data)

当然,这些特性都可以进行组合,例如只对JSONDecodeError 进行重试,每次间隔5秒,重试三次,那就写成:

from tenacity import retry
from json.decoder import JSONDecodeError @retry(retry=retry_if_exception_type(JSONDecodeError), wait=wait_fixed(5), stop=stop_after_attempt(3))
def extract(url):
info_json = requests.get(url).content.decode()
info_dict = json.loads(info_json)
data = info_dict['data']
save(data)

自始至终,爬虫主体的代码完全不需要做任何修改。

Tenacity是我见过的,最 Pythonic ,最优雅的第三方库。

本文首发地址:https://kingname.info/2017/06/18/easy-retry/转载请注明出处。

Tenacity——Exception Retry 从此无比简单的更多相关文章

  1. 写一份简单的webpack2 的配置文件,无比简单

    这是一份自己用到的webpack2的配置写法,从看webpack2开始,发现自己越来越懒了,现在html文件都不想自己写了,直接自己生成... 哈哈,这次是可以无比完美的导入css啦 开发的时候在命令 ...

  2. 学习笔记:python3,PIP安装第三方库(2017)

    https://pip.pypa.io/en/latest/quickstart/ pip的使用文档 http://www.lfd.uci.edu/~gohlke/pythonlibs/   .whl ...

  3. python 流行库、库的基本用法

    进入github,输入python 点击see topic 进入python流行的库  链接 https://github.com/topics/python 1.QuantLib 金融衍生品数据库 ...

  4. python的重试库tenacity用法以及类似库retry、requests实现

    介绍 tenacity is an Apache 2.0 licensed general-purpose retrying library, written in Python, to simpli ...

  5. (数据科学学习手札135)tenacity:Python中最强大的错误重试库

    本文示例代码及文件已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 我们在编写程序尤其是与网络请求相关的程序, ...

  6. 如何更简单的使用Polly

    Polly 弹性瞬时错误处理库 Polly是一个C#实现的弹性瞬时错误处理库 它可以帮助我们做一些容错模式处理,比如: 超时与重试(Timeout and Retry) 熔断器(Circuit Bre ...

  7. mybatis 的简单使用

    须要用到的包:(这里仅仅是当中一个版本号.其它的百度) mysql-connector-java-5.1.6-bin mybatis-3.2.2 先看项目文件夹: 配置文件mybatisconfig. ...

  8. .NET Core微服务之路:利用DotNetty实现一个简单的通信过程

    上一篇我们已经全面的介绍过<基于gRPC服务发现与服务治理的方案>,我们先复习一下RPC的调用过程(笔者会在这一节的几篇文章中反复的强调这个过程调用方案),看下图

  9. Prism for WPF 搭建一个简单的模块化开发框架(五)添加聊天、消息模块

    原文:Prism for WPF 搭建一个简单的模块化开发框架(五)添加聊天.消息模块 中秋节假期没事继续搞了搞 做了各聊天的模块,需要继续优化 第一步画页面 页面参考https://github.c ...

随机推荐

  1. HTML5 拖放(Drag 和 Drop)功能开发——浅谈dataTransfer对象

    [前言] 之前我已经为大家介绍过一篇有关HTML5中原生拖拽的相关知识了.今天为大家介绍HTML5拖拽中的其他一些关于dataTransfer对象的知识和应用. dataTransfer对象 之前的一 ...

  2. 博弈论(Game Theory) - 01 - 前传之占优战略均衡

    博弈论(Game Theory) - 01 - 前传之占优战略均衡 开始 我们现在准备攀爬博弈论的几座高峰. 我们先看看在纳什均衡产生之前,博弈论的发展情况. 我们的第一座高峰是占优战略均衡. 囚徒困 ...

  3. 罪恶的SEO优化

    1. 基础概念开始 SEO,搜索引擎优化.概括来说就是针对分析搜索引擎的网站收录以及评价规律,来对网站的结构,内容以及其他因素作出一些合理调整,使得网站更容易被搜索引擎收录,并且能够尽量排在搜索引擎自 ...

  4. C#之lambda表达式

    从C#3.0开始,可以使用lambda表达式把实现代码赋予委托.lambda表达式与委托(http://www.cnblogs.com/afei-24/p/6762442.html)直接相关.当参数是 ...

  5. Thinkphp3.2———配置模块

    一.配置格式 Thinkphp框架中的所有配置都是数组形式定义的的格式为: //项目配置 return array( 'DEFAULT_MODULE'=>'Index',//默认模块 'URL_ ...

  6. ios随机数不重复

     NSMutableArray* Valuearr=[NSMutableArray new];     int counN=9;    for(int i=0;i<counN;i++)  //创 ...

  7. ZED 相机 && ORB-SLAM2安装环境配置与ROS下的调试

    注:1. 对某些地方进行了更新(红色标注),以方便进行配置. 2. ZED ROS Wrapper官方github已经更新,根据描述新的Wrapper可能已经不适用与Ros Indigo了,如果大家想 ...

  8. Servlet,过滤器,监听器,拦截器的区别

    1.过滤器 Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序,主要的用途是过滤字符编码.做一些业务逻辑判断等.其工作原理是,只要你在web.xml ...

  9. 用sftp上传文件至linux服务器

    1.项目环境 框架:springmvc    项目管理工具:maven 2.必须使用的jar com.jcraft jsch 0.1.27 test 3.新建一个FileUpDown工具类,在类中添加 ...

  10. API文档工具-Swagger的集成

    最近安装了API文档工具swagger,因为Github上已有详细安装教程,且安装过程中没有碰到大的阻碍,所以此文仅对这次安装做一份大致记录 相关网站 Swagger 官方地址: http://swa ...