原文:What problem does it solve?
对于Python新手来说,Python较令人费解的方面之一就是,当涉及到编写HTTP(S)协议客户端时,标准库的urllib
模块和流行的(及备受推崇的)第三方模块requests
之间鲜明的可用性差异。当你的问题是“与HTTP服务器进行通信”时,可用性方面的差异并不是那么明显,但一涉及到额外的需求,像SSL/TLS、鉴权、重定向处理、会话管理和JSON请求/响应主题,差异就明显起来。
想要记录易用性的差异是诱人而完全可以理解的,直到requests
是"Pythonic" (在2016年), 而urllib
现在已经不Pythonic了 (尽管被包含在了标准库中)。
虽然当然有那么点因素(例如,内置的property
是在Python 2.2才添加进来的,而urllib2
被包含在原始的Python 2.0发布中,因此在其API设计中无法考虑这点),但是绝大多数的可用性差异涉及到了我们经常忘记问问所使用的软件的一个完全不同的问题:它解决了什么问题?
即,urllib
/urllib2
和requests
之间的许多令人惊讶的其他差异可以由它们_解决不同的问题_这一事实,以及较之于Jeremy Hylton在十年前想要解决的问题,现今大多数HTTP客户端开发者所遇到的问题更接近于Kenneth Reitz在2010/2011年设计requests
用以解决的问题来解释。
引用当前的Python 3 urllib
包文档:“urllib是一个收集几个处理URL模块的包”。
以及来自Jeremy的添加urllib2
到CPython的原始提交信息的文档字符串:“使用各种协议,用于打开URL的可扩展库”。
等等,神马?我们只是想写一个HTTP客户端,所以为什么文档谈到一般的URL相关工作?
虽然,对于那些习惯于现代的HTTPS+JSON驱动的交互式web的开发者来说有点奇怪,但是事情为什么会变成这样并不总是清晰的。
在世纪之交,所期望的是,保留丰富多样的数据传输协议,并且为不同的目的进行不同的特点优化,而标准库中最为有用的客户端则是那个可以用来与多种不同类型的服务器(例如HTTP, FTP, NFS等等)进行通信,客户端开发者无需过多担心使用的特定的协议(由URL schema所示)。
然而,在实践中,事情并非如此(大多数是因为严格的防火墙制度,这意味着HTTP服务器是唯一一个可被可靠访问的远程服务),所以,在2016年,人们现在经常拿专用的仅HTTP(S)客户端库的可用性和在获取大多数HTTP(S)特性之前需要专门配置使用HTTP(S)的通用的URL处理库进行比较。
在编写它的时候,urllib2
是被设计来适合“通用URL处理”这一方孔的方钉。相比之下,大多数的现代客户端开发者在寻找适合“HTTPS+JSON处理”这一圆孔的圆钉 —— 如果你先把角磨圆,那么urllib
/urllib2
就会适用,但requests
则已经是圆的了。
对"它解决了什么问题?"这个不那么明显的问题的回答,会到一个明显得多的后续问题:如果urllib
/urllib2
被设计来解决的问题不再常见,而requests
解决的问题是常见的,那么为什么不把requests
添加到标准库中呢?
如果我记得没错,在2013年左右(在requests
1.0发布后)的一次语言提交中,Guido在原则上认可了这个想法,而在核心开发者团队中,无论是requests
本身(可能作为一个独立升级组件的捆绑快照),还是带有不一样实现的的API兼容子集,最终都会出现在标准库中,这是一个相当常见的假设。
然而,即使撇开requests开发者关于此想法的疑惑,让requests
作为标准库组件的一部分,仍然有一些不一般的系统集成问题要解决。
特别是,其中之一是,requests确实更可靠地以跨平台的方式处理SSL/TLS证书是捆绑包含在certifi
项目中的Mozilla证书捆绑(Mozilla
Certificate Bundle)。这是默认情况下(由于以跨平台的方式获得对系统的安全证书的可靠访问的困难)的一个明智之举,但它与标准库的安全策略(具体是将证书管理委托给底层操作系统)相冲突。这项策略的目的是解决两个需求:允许Python应用程序访问添加到系统证书存储的自定义机构证书(最值得注意的是,适用于大型组织的私有CA证书),并避免增加当根证书捆绑出于任何其他原因而改变时,需要更新的额外的证书存储到终端用户系统。
这类问题在技术上是可以解决的,但解决它们并不好玩,并且帮助解决它们的人手头上已经有许许多多其他的要求。这意味着,只要大部分的CPython和requests
开发者将其贡献作为业余时间的活动,而不是专门被雇佣来做的事,那么在这个方面我们可能不会看到太多进展。