浅谈urllib2中内部调用流程

本文详细介绍了使用urllib2库发起HTTP请求的过程,包括urllib2库的基本使用方法、内部调用流程以及相关参数的详细说明。通过实例展示了如何在客户端完成数据操作并触发服务器响应,同时解释了urllib2库如何根据传入参数构建并执行请求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


一. 背景

最近项目中有个需求:策划表数据修改完成后,向服务器发送一个https请求,通知服务器从svn服务器拉去代码,并且重启服务器。对于服务端来说,需要提供一个url,当有主机发出https请求,通过认证后,完成后续的工作。对于客户端来说,也非常简单:在使用倒表工具完成倒表后,将数据commit到svn服务器,然后发送一个url请求即可。


二. urllib2库使用

urllib2是python的一个获取URL的组件。它以 urllib2.urlopen接口的形式提供了一个非常简单的接口。具体的使用方法可以参考 extensible library for opening URLs 的接口说明。一般的使用:

<代码段1>
import urllib2
response = urllib2.urlopen('http://python.org/')
html = response.read()

从表面看,通过urlopen接口输入一个url字符串即可获得response,其实接口urlopen接口的参数非常丰富。来看一下签名:

<代码段2>
def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
            cafile=None, capath=None, cadefault=False, context=None):

其中:
data 发给服务器附带的数据,可以通过一定的格式编码;
cafile, capath 是特别为https请求提供的证书;
context 是用来描述SSL选项的 ssl.SSLContext 实例;

返回是一个类似 file 类型的实例,并且附带了三个接口:geturl, info, getcode,具体参考上述链接。

三. 内部调用流程


urlopen是urllib2库中的全局函数,进入函数体后,它会根据其传入的7个参数(少于7个以签名默认值填充)的值,或者采用全局默认值 _opener(注意这里有下划线),或者新建一个OpenerDirector实例,来作为该接口内的局部变量 opener(无下划线)<urllib2.OpenerDirector>

<代码段3>
    global _opener
    if cafile or capath or cadefault:
        ...
        opener = build_opener(https_handler)
    elif context:
        https_handler = HTTPSHandler(context=context)
        opener = build_opener(https_handler)
    elif _opener is None:
        _opener = opener = build_opener()
    else:
        opener = _opener

并且返回OpenerDirector类中的open方法的返回值:

<代码段4>
    return opener.open(url, data, timeout)

从源码中可以看出opener变量可以由 build_opener 接口生成:

<代码段5>
def build_opener(*handlers):
    ...
    opener = OpenerDirector()
    ...
    return opener

细心的读者可能看到了opener也可以由全局变量_opener赋值。如果调用者没有特别的需求,而这个global _opener在urllib2中也是一个默认的OpenDirector全局实例,如果需要新建一个自定义的默认全局_opener,可以通过install_opener(opener)来设置:

<代码段6>:
def install_opener(opener): 
      global _opener
      _opener = opener

OpenerDirector 类管理了一些处理器 handlers ,所有的任务都有 handlers 处理。每个 handlers 知道如何通过特定协议打开URLs,或者如何处理URL打开方式。build_opener接口参数 handlers ,是要加入到新建的opener的(他是OpenerDirector类实例,调用该类的add_handler(h) ),不过在加入之前要经过筛选:如果handlers里面是default_class的子类或是其中的类,那么这些handlers会被排除(skip),将筛选后的handlers和default_class加入到opener中:

<代码段7>
	opener = OpenerDirector()
	...
	# 如果handlers里面是default_class的子类或是其中的类,那么这些handlers会被排除(skip)
	for klass in default_classes:
	    for check in handlers:
	        if isclass(check):
	            if issubclass(check, klass):
	                skip.add(klass)
	        elif isinstance(check, klass):
	            skip.add(klass)
	for klass in skip:
	    default_classes.remove(klass)
	
	#将筛选后的handlers和default_class加入到opener中:
	for klass in default_classes:
	    opener.add_handler(klass())
	
	for h in handlers:
	    if isclass(h):
	        h = h()
	    opener.add_handler(h)
	return opener

讲好了opener是怎么来,再简说一下代码段4中opener.open是怎么回事,他的输入是url, data, timeout,前面介绍过了,返回类型与urlopen接口返回相同:

<代码段8>
def open(self, fullurl, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
接口内首先根据fullurl的类型,生成一个Request,并将data加入到request当中:

<代码段9>
if isinstance(fullurl, basestring):
    req = Request(fullurl, data)
else:
    req = fullurl
if data is not None:
        req.add_data(data)

所以无论传入的fullurl是str类型,还是Request类型,open接口都能讲fullurl转成Request类型的一个实例。经过request的预处理后,调用了OpenerDirector类的 _open接口,在_open接口内部,就是handler处理的机制了。根据 使用默认的形式获取url,如果默认获取不到,再使用req的获取url的形式,并且调用_call_chain的接口:

<代码段10>
def _call_chain(self, chain, kind, meth_name, *args):
在这个接口中,会从chain取出handlers,对handlers中的每一个handler获取,名为meth_name的函数对象func,并且将*args作为func的参数调用,将不为None的结果返回,即为response:

<代码段11>
handlers = chain.get(kind, ()) 
for handler in handlers:
      func = getattr(handler, meth_name) 
      result = func(*args) 
      if result is not None: 
            return result

四. 结语


文章简要的描述了一下urllib2库中,由urllib2.open(url)发起后的内部调用流程,这里并没有讲述网络中的一些专业知识,例如Request的类型(type),handlers的default_class的种类,以及相关的作用。更多的使用方式和细节请移步urllib2总结







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值