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. Windows7系统的封装

    系统装到虚拟机优化完成以后,可以装上自己想要装的软件,也可以不装做个纯净版的.今天用ES封装,首先打开一般我们只点这四项就够了,如下图 写上自己想写的,也可以不写系统默认,直接点“下一步”关闭设备管理 ...

  2. scrapy(一)建立一个scrapy项目

    本项目实现了获取stack overflow的问题,语言使用python,框架scrapy框架,选取mongoDB作为持久化数据库,redis做为数据缓存 项目源码可以参考我的github:https ...

  3. 让div自适应浏览器窗口居中显示

    今天做 banner 时发现一个问题,就是浏览器窗口水平拉伸时 banner 图未能居中,所以网上找了些资料,自己写了个小 demo html代码: <div class="div1& ...

  4. iOS开发tips-UINavigationBar的切换

    概述 在iOS系统中,如果控制器是以push方式进行管理的话,那么事实上多个控制器是共享的同一个导航栏.当然iOS系统的设计无可厚非,但是国内的应用经常会遇到很多个性的设计,就比如说A push到 B ...

  5. redis 实例2 构建文章投票网站后端

    redis 实例2 构建文章投票网站后端   1.限制条件 一.如果网站获得200张支持票,那么这篇文章被设置成有趣的文章 二.如果网站发布的文章中有一定数量被认定为有趣的文章,那么这些文章需要被设置 ...

  6. Mysql数据库学习笔记之数据库索引(index)

    什么是索引: SQL索引有两种,聚集索引和非聚集索引,索引主要目的是提高了SQL Server系统的性能,加快数据的查询速度与减少系统的响应时间. 聚集索引:该索引中键值的逻辑顺序决定了表中相应行的物 ...

  7. Android学习资料整理

    1.官方网站 http://developer.android.com/index.html http://android-developers.blogspot.com/ 2.Android Des ...

  8. 如何将网站升级为HTTPS协议?

    基本概念: HTTP: 是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准,用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少. HT ...

  9. linux服务器对外打包处理

    案例描述 服务器遇到大流量攻击的处理过程.早上接到 IDC 的电话,说我们的一个网段 IP 不停的向外发包,应该是被攻击了,具体哪个 IP不知道,让我们检查一下. 按理分析及解决办法 首先我们要先确定 ...

  10. 读 Zepto 源码之神奇的 $

    经过前面三章的铺垫,这篇终于写到了戏肉.在用 zepto 时,肯定离不开这个神奇的 $ 符号,这篇文章将会看看 zepto 是如何实现 $ 的. 读Zepto源码系列文章已经放到了github上,欢迎 ...