Python 3.7.1 模块 urllib.request

urllib.request 模块

  • 1. 功能函数
    • urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
    • urllib.request.build_opener([handler, ...])
    • urllib.request.install_opener(opener)
    • urllib.request.pathname2url(path)
    • urllib.request.url2pathname(path)
    • urllib.request.getproxies()
  • 2. Request
    • class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
    • 2.1 方法和属性
      • full_url
      • type
      • host
      • origin_req_host
      • selector
      • data
      • unverifiable
      • method
      • get_method()
      • add_header(key, val)
      • add_unredirected_header(key, header)
      • has_header(header)
      • remove_header(header)
      • get_full_url()
      • set_proxy(host, type)
      • get_header(header_name, default=None)
      • Request.header_items()
    • 2.2 废弃的函数
  • 3. OpenerDirector
    • class urllib.request.OpenerDirector
    • 3.1 方法
      • add_handler(handler)
      • open(url, data=None[, timeout])
      • error(proto, *args)
    • 3.2 打开url流程
  • 4.BaseHandler
    • 4.1 方法
      • add_parent(director)
      • close()
      • parent
      • default_open(req)
      • protocol_open(req)
      • unknown_open(req)
      • http_error_default(req, fp, code, msg, hdrs)
      • http_error_nnn(req, fp, code, msg, hdrs)
      • protocol_request(req)
      • protocol_response(req, response)

源代码: Lib / urllib / request.py
未完待续…


此模块定义了有助于在现实环境中打开URL(主要是HTTP)的函数和类 - 基本和摘要式身份验证,重定向,cookie等。

也可以看看
建议将Requests包用于更高级别的HTTP客户端接口。

1. 功能函数

urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)

打开一个URL,url可以是字符串或 Request对象。

data必须是指定要发送到服务器的其他数据的对象,如果不需要此类数据可以指定为None。详情请见Request 。

urllib.request模块使用HTTP/1.1并且在其HTTP请求中包含请求头Connection:close

可选的timeout参数指定阻塞操作(如连接尝试)的超时时间(以秒为单位)。如果未指定,将使用全局默认超时设置。这实际上仅适用于HTTP,HTTPS,FTP连接。

如果指定了context,则它必须是ssl.SSLContext的实例,用来描述各种SSL选项。有关详细信息,请参阅HTTPSConnection。

可选的cafile和capath参数为HTTPS请求指定一组可信CA证书。 cafile应指向包含一组 CA证书的单个文件,而capath应指向哈希证书文件的目录。更多信息可以在ssl.SSLContext.load_verify_locations()中找到。

cadefault参数被忽略。

此函数始终返回一个对象,该对象可用作 上下文管理器 并具有如下的方法

  • geturl() - 返回检索到的资源的URL,通常用于确定是否遵循重定向
  • info()- 以email.message_from_string()实例的形式返回页面的元信息,例如请求头(请参阅 HTTP请求头的快速参考)
  • getcode() - 返回响应的HTTP状态代码。
>>> from urllib import request
>>> r = request.urlopen("http://www.baidu.com")
>>> r.geturl()
'http://www.baidu.com'
>>> r.info()
<http.client.HTTPMessage object at 0x01C19C50>
>>> r.getcode()
200
>>> r.info()._headers
[('Bdpagetype', '1'), ('Bdqid', '0xd0d3ce2a00001272'), ('Cache-Control', 'private'), ('Content-Type', 'text/html'), ('Cxy_all', 'baidu+8c65a65315fd91f
1324cd1a322701b91'), ('Date', 'Tue, 18 Dec 2018 13:06:43 GMT'), ('Expires', 'Tue, 18 Dec 2018 13:06:05 GMT'), ('P3p', 'CP=" OTI DSP COR IVA OUR IND CO
M "'), ('Server', 'BWS/1.1'), ('Set-Cookie', 'BAIDUID=064B59D7B588D0DD317AE0B14B544F69:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647;
path=/; domain=.baidu.com'), ('Set-Cookie', 'BIDUPSID=064B59D7B588D0DD317AE0B14B544F69; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=
/; domain=.baidu.com'), ('Set-Cookie', 'PSTM=1545138403; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com'), ('Set-C
ookie', 'delPer=0; path=/; domain=.baidu.com'), ('Set-Cookie', 'BDSVRTM=0; path=/'), ('Set-Cookie', 'BD_HOME=0; path=/'), ('Set-Cookie', 'H_PS_PSSID=1
431_21082_28131_27751_27244_22158; path=/; domain=.baidu.com'), ('Vary', 'Accept-Encoding'), ('X-Ua-Compatible', 'IE=Edge,chrome=1'), ('Connection', '
close'), ('Transfer-Encoding', 'chunked')]

对于HTTP和HTTPS的URL,此函数返回经过稍微修改的 http.client.HTTPResponse 对象。除了上面的三个新方法之外,msg属性包含与reason属性相同的信息 - 服务器返回的原因 - 而不是 HTTPResponse文档中指定的响应头。

>>> type(r)
<class 'http.client.HTTPResponse'>
>>> r.reason
'OK'
>>> r.msg
'OK'

对于FTP,文件和数据URL,请求显示地由历史遗留的URLopener和FancyURLopener类处理 ,此函数返回一个urllib.response.addinfourl对象。

协议错误会引发引发URLError。

>>> r = request.urlopen("httpf://www.baidu.com")
Traceback (most recent call last):
  File "", line 1, in <module>
  ...
      raise URLError('unknown url type: %s' % type)
urllib.error.URLError: <urlopen error unknown url type: httpf>

请注意,如果没有handler来处理请求,则可能会返回None(尽管默认安装的全局 OpenerDirector使用 UnknownHandler确保永远不会发生这种情况)。

此外,如果检测到代理设置(例如,当一个*_proxy 环境变量如http_proxy已设置),默认安装的 ProxyHandler确保通过代理处理请求。

Python 2.6及更早版本的遗留函数urllib.urlopen已经停止使用; urllib.request.urlopen()对应旧的 urllib2.urlopen。通过传递字典参数给urllib.urlopen来完成代理的处理,可以通过使用ProxyHandler对象来获得这些参数 。

在版本3.2中更改:添加了cafile和capath。

版本3.2中已更改:如果可能,现在支持HTTPS虚拟主机(即,如果 ssl.HAS_SNI为true)。

版本3.2中的新功能:data可以是可迭代的对象。

在版本3.3中更改:添加了cadefault。

版本3.4.3中已更改:已添加context。

从版本3.6 开始不推荐使用:不推荐使用cafile,capath和cadefault以支持上下文context。请改用ssl.SSLContext.load_cert_chain(),或者让ssl.create_default_context()为您自动选择系统的可信CA证书。

urllib.request.build_opener([handler, …])

返回一个OpenerDirector实例,它按照handler的给定顺序工作。handler可以是BaseHandler的实例或者是BaseHandler的子类(在这种情况下,必须可以在没有任何参数的情况下调用构造函数)。以下类的实例将可用于handler:ProxyHandler(如果检测到代理设置),UnknownHandler,HTTPHandler, HTTPDefaultErrorHandler,HTTPRedirectHandler, FTPHandler,FileHandler,HTTPErrorProcessor

如果Python安装具有SSL支持(即,如果可以导入模块ssl),HTTPSHandler也将添加到可用的handler中。

一个BaseHandler子类,还可以改变其handler_order 属性,修改其在处理程序列表中的位置。

译者实例
为什么不使用urlopen(),而使用opener,因为前者有些高级特性无法使用,比如代理等,此时就要自定义opener来使用这些高级特性。

from urllib import request

old_url = 'http://www.baidu.com/'
# 普通的opener和handler,和request.urlopen()一样
http_handler = request.HTTPHandler()
opener = request.build_opener(http_handler)
r = opener.open(old_url)
print(r.reason,type(r)
# 输出结果
OK <class 'urllib.request.OpenerDirector'>
# 创建了一个ProxyHandler()
def bh_proxy():
    old_url = 'http://www.ip111.cn/'
    http_proxy_handler = request.ProxyHandler({"http":"122.227.139.170:3128"})
    opener = request.build_opener(http_proxy_handler)
    r = opener.open(old_url)
    print(r.reason,type(r))
    import re
    content = r.read().decode(encoding="utf-8",errors="replace")
    #print(content)
    print(re.search(r'(\d{1,3}\.){3}\d{1,3}',content))
bh_proxy()
# 输出结果
OK <class 'urllib.request.OpenerDirector'>
<re.Match object; span=(1788, 1803), match='122.227.139.170'>

urllib.request.install_opener(opener)

将OpenerDirector实例安装为默认的全局opener。只有当你想让urlopen使用那个opener时才需要用到此函数; 否则,只需要调用OpenerDirector.open()来替代 urlopen()。代码不检查是否真的是一个OpenerDirector,任何具有适当接口的类都可以使用。

译者注
让我们在上面例子中的bh_proxy()增加一段代码:

def bh_proxy():
	# ...
	# 第二部分
	print("-".center(50,'-'))
    r = request.urlopen(old_url)
    content = r.read().decode(encoding="utf-8", errors="replace")
    # print(content)
    print(re.search(r'(\d{1,3}\.){3}\d{1,3}', content))
# 输出结果
OK <class 'urllib.request.OpenerDirector'>
<re.Match object; span=(1788, 1803), match='122.227.139.170'>
--------------------------------------------------
<re.Match object; span=(1788, 1802), match='111.222.333.444'>

可以看到,如果urlopen()没有走代理,如果想让它一直走代理,就可以使用install_opener函数,继续对上面的例子修改。

def bh_proxy():
	# ...
	http_proxy_handler = request.ProxyHandler({"http":"122.227.139.170:3128"})
    opener = request.build_opener(http_proxy_handler)
    request.install_opener(opener)
    r = opener.open(old_url)
    # ...
# 输出结果
OK <class 'urllib.request.OpenerDirector'>
<re.Match object; span=(1788, 1803), match='122.227.139.170'>
--------------------------------------------------
<re.Match object; span=(1788, 1803), match='122.227.139.170'>

urllib.request.pathname2url(path)

将路径名(path)从路径的本地语法转换为URL路径组件中使用的形式。这不会产生完整的URL。返回值已使用quote()函数进行url编码。

urllib.request.url2pathname(path)

将路径组件路径从百分比编码的URL 转换为路径的本地语法。这不接受完整的URL。此函数用于 unquote()解码路径。

def ulib_req_of():
    pu = request.pathname2url('/pag1/a=3&关键字=kwd')
    print(pu)
    print(request.url2pathname(pu))
ulib_req_of()

# 输出结果(本地windwos测试)
/pag1/a%3D3%26%E5%85%B3%E9%94%AE%E5%AD%97%3Dkwd
\pag1\a=3&关键字=kwd

urllib.request.getproxies()

此帮助函数返回代理服务器URL映射的方案(scheme)字典。它首先扫描操作系统环境变量中的_proxy变量(不区分大小写),当它找不到时,从Mac OSX系统配置和Windows系统注册表中查找代理信息。如果小写和大写环境变量都存在(并且不一致),则首选小写。

注意
如果设置了环境变量REQUEST_METHOD(通常表示您的脚本在CGI环境中运行),则将忽略环境变量HTTP_PROXY(大写_PROXY)。这是因为客户端可以使用“Proxy":HTTP请求头执行注入(非法操作)。如果需要在CGI环境中使用HTTP代理,请显式使用ProxyHandler ,或确保变量名称为小写(或至少为_proxy后缀)。

import os,re
os.environ['my_proxy'] = '122.227.139.170:3128'
print(request.getproxies())
# 输出结果
{'my': '122.227.139.170:3128'}

2. Request

class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)

此类是对URL请求的抽象。

url应该是包含有效URL的字符串。

data必须是指定要发送到服务器的其他数据的对象,或者如果不需要此类数据,指定为None目前,HTTP请求是唯一使用data的请求。支持的对象类型包括字节,类文件对象和可迭代对象。如果没有提供 Content-LengthTransfer-Encoding请求头,HTTPHandler将根据data的类型来设置这些请求头。 Content-Length用于发送字节对象, Transfer-Encoding: chunked 用于发送文件和其他迭代对象(如RFC 7230 和下面所描述的那样)。

对于HTTP POST请求方法,data应该是具有标准application / x-www-form-urlencoded格式的缓冲区。urllib.parse.urlencode()函数接收映射或2元组的序列,并以ASCII格式返回字符串。在用作data参数之前,应将其编码为字节。

headers 应该是一个字典,并且将被视为在每个键值对上使用add_header()函数。这通常用于“欺骗” User-Agent头,浏览器使用该标头值来标识自身 - 某些HTTP服务器仅允许来自常见浏览器而非脚本的请求。例如,Mozilla Firefox可能将自己标识为"Mozilla/5.0 (X11; U; Linux i686) Gecko/20071127 Firefox/2.0.0.11",而urllib 默认的用户代理字符串是 "Python-urllib/2.6"(在Python 2.6上)。

如果存在data 参数,则应包含适当的Content-Type头。如果未提供此头且数据不是None,则将添加默认值Content-Type: application/x-www-form-urlencoded

最后两个参数仅对正确处理第三方HTTP cookie感兴趣:

origin_req_host应该是事务的原始请求主机,如下RFC 2965所定义。它默认为 http.cookiejar.request_host(self)。这是用户启动的原始请求的主机名或IP地址。例如,如果请求是针对HTML文档中的图像,则该请求应该是包含图像的页面请求的请求主机。

unverifiable 应表明该请求是否无法核实,如RFC 2965。它默认为False。无法核实的请求是用户无法批准的URL。例如,如果请求是针对HTML文档中的图像,并且用户没有选择批准自动获取图像,则应该设置为True。

method应该是一个字符串,表示将使用的HTTP请求方法(例如'HEAD')。如果提供,则其值存储在 method属性中并由get_method()使用。默认值是'GET'(如果data是None)或’POST'(如果data不是None)。子类可以通过在类本身中设置method属性来指示不同的默认方法 。

def ulib_req_reqcls():
    url = 'http://127.0.0.1:8004/findg/'
    mrequest = request.Request(url,method='GET',headers={"SELF_DEFINE":"GOOD123","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"})
    #print(mrequest.get_header("User-Agent"))
    resp = request.urlopen(mrequest)
    print(resp)
    
ulib_req_reqcls()
# 输出结果   
<http.client.HTTPResponse object at 0x00000124A8A072B0> 

注意
如果数据对象无法多次传递其内容(例如,只能生成一次内容的文件或可迭代文件),则请求将无法按预期工作,并且会重试HTTP重定向或身份验证请求。data在请求头被发送到HTTP服务器后马上发送。库不支持多次继续期望(原文There is no support for a 100-continue expectation in the library)。

在版本3.3中更改:Request.method参数被添加到Request类。
在版本3.4中更改:默认的Request.method可以在类中指示。
在版本3.6中更改:如果未提供Content-Length且data既不是None也不是字节对象也不引发错误。回过头来使用分块传输编码( chunked transfer encoding)。

2.1 方法和属性

以下方法描述了Request公共接口,因此可以在子类中重写所有方法。它还定义了几个公共属性,客户端可以使用这些属性来检查已解析的请求。

full_url

传递给构造函数的原始URL。

版本3.4中已更改。

Request.full_url具有setter,getter和deleter的属性。获取 full_url返回包含原始请求URL片段(如果存在)。

>>> r = request.Request('http://www.baidu.com')
>>> r.full_url
'http://www.baidu.com'
>>> r.full_url = 'http://www.ip111.cn'

type

URI类型。

>>> r.type
'http'
>>> r.full_url = 'ftp://www.ip111.cn'
>>> r.type
'ftp'

host

URI 权限,通常是主机,但也可能包含由冒号分隔的端口。

>>> r.full_url = 'http://www.ip111.cn'
>>> r.host
'www.ip111.cn'
>>> r.full_url = 'http://123.456.789.111:3243'
>>> r.host
'123.456.789.111:3243'

origin_req_host

请求的原始主机,没有端口。

>>> r = request.Request("http://123.456.789.111:3243")
>>> r.origin_req_host
'123.456.789.111'

selector

URI路径。如果Request使用代理,则selector将是传递给代理的完整URL。

>>> r = request.Request("http://123.456.789.111:3243/dir/stu/?a=3&b=4")
>>> r.selector
'/dir/stu/?a=3&b=4'

data

请求的实体主体,如果未指定则为None

版本3.4中更改:如果之前已设置或计算过data,则更改现在的data值将删除“Content-Length”头。

>>> r.data is None
True

unverifiable

boolean,表示请求是否无法验证(如 RFC 2965 中描述)。

method

要使用的HTTP请求方法。默认情况下,它的值为None,这意味着get_method()将对要使用的方法进行正常选取。可以通过修改在Request子类中的get_method()方法设置默认值,或者通过将method 参数值传递给Request构造函数来设置其值(从而覆盖默认选择)。

版本3.3中的新功能。

版本3.4中已更改:现在可以在子类中设置默认值; 以前它只能通过构造函数参数设置。

>>> r.method
Traceback (most recent call last):
  File "", line 1, in <module>
AttributeError: 'Request' object has no attribute 'method'
>>> r = request.Request("http://123.456.789.111:3243/dir/stu/?a=3&b=4",method='GET')
>>> r.method
'GET'

get_method()

返回表示HTTP请求方法的字符串。如果 Request.method不是None,返回其值,否则如果data是None,返回 'GET'或者如果data不是None,返回'POST'。这仅对HTTP请求有意义。

在版本3.3中更改: get_method现在查找method的值。

>>> r.get_method()
'GET'

add_header(key, val)

在请求中添加另一个头。除了HTTP handler之外,所有handler都会忽略请求头,并将它们添加到请求头列表中并发送到服务器。请注意,不能有多个具有相同名称的请求头,后面的会覆盖前面添加的,以防key发生冲突。目前,这不会损坏HTTP功能。

>>> r = request.Request("http://123.456.789.111:3243/dir/",method='GET')
>>> r.add_header("User-Agent","python3.7")
>>> r.header_items()
[('User-agent', 'python3.7')]

add_unredirected_header(key, header)

添加一个不会重定向请求的头

has_header(header)

返回实例是否具有指定请求头(检查常规和未重定向的头)

>>> r.header_items()
[('User-agent', 'python3.7')]
>>> r.has_header('User-agent')
True
>>> r.has_header('User-agentss')
False

remove_header(header)

从请求实例中删除某个请求头(包括常规和未重定向的头)

版本3.4中的新功能。

get_full_url()

返回构造函数中给出的URL

版本3.4中已更改

返回 full_url

>>> r = request.Request("http://123.456.789.111:3243/dir/",method='GET')
>>> r.get_full_url()
'http://123.456.789.111:3243/dir/'
>>>

set_proxy(host, type)

通过连接到代理服务器来准备请求。host和type将取代这些实例,并且实例的选择将是在构造函数中给出的原始URL。

def ulib_req_req_proxy():
    old_url = 'http://www.ip111.cn/'
    r = request.Request(old_url,method='GET')
    r.set_proxy('122.227.139.170:3128','http')
    resp =request.urlopen(r)
    import re
    content = resp.read().decode(encoding="utf-8", errors="replace")
    print(re.search(r'(\d{1,3}\.){3}\d{1,3}', content))
ulib_req_req_proxy()

#输出结果
<re.Match object; span=(1788, 1803), match='122.227.139.170'>

get_header(header_name, default=None)

返回给定请求头的值。如果请求头不存在,则返回默认值。

Request.header_items()

返回Request请求头的元组列表(header_name,header_value)

2.2 废弃的函数

版本3.4中已更改:已删除自3.3以来已弃用的请求方法add_data,has_data,get_data,get_type,get_host,get_selector,get_origin_req_host和is_unverifiable。

3. OpenerDirector

class urllib.request.OpenerDirector

OpenerDirector类打开URL ,处理BaseHandler链。它管理handler的链,并从错误中恢复。

3.1 方法

OpenerDirector 实例有以下方法:

add_handler(handler)

handler应该是一个BaseHandler实例。搜索以下方法,并将其添加到可能的链中(请注意,HTTP错误是一种特殊情况)。

  • protocol_open()- 表示处理程序知道如何打开拥有协议的URL 。
  • http_error_type()- 表示处理程序知道如何使用HTTP错误代码类型处理HTTP错误。
  • protocol_error()- 表示处理程序知道如何处理来自(非http)协议的错误。
  • protocol_request()- 表示处理程序知道如何预处理 协议请求。
  • protocol_response()- 表示处理程序知道如何后处理协议响应。

open(url, data=None[, timeout])

打开给定的url(可以是请求对象或字符串),可选地传递给定的data。返回值和异常与urlopen()(urlopen就是在全局OpenerDirector上调用open()方法)相同。可选的timeout参数指定阻塞操作(如连接尝试)的超时(以秒为单位)(如果未指定,将使用全局默认超时设置)。超时功能实际上仅适用于HTTP,HTTPS和FTP连接)。

error(proto, *args)

处理给定协议的错误。这将使用给定的参数(特定于协议)调用给定协议的已注册错误处理程序。HTTP协议是一种特殊情况,它使用HTTP响应代码来确定特定的错误处理程序; 请参阅handler类中的http_error_*() 方法。

返回值和爆出的异常同urlopen()。

3.2 打开url流程

OpenerDirector对象分三个阶段打开一个URL:

在每个阶段中调用这些方法的顺序是通过handler实例的先后顺序来确定的。

1 . 每个具有名字像 protocol_request()的handler都调用该方法来预处理请求。

2 .每个具有名字像 protocol_open()的handler都调用该方法来处理请求。当handler返回非None 值(如响应)或引发异常(通常是 URLError)时,此阶段结束。允许异常传递。

实际上,上述算法首先尝试default_open()。如果所有这些方法都返回None,则对名称像protocol_open()的方法重复该算法。如果返回所有这些方法None,则对名称像unknown_open()的方法重复该算法。

请注意,这些方法的实现可能涉及父类 OpenerDirector实例open()和 error()方法的调用。

3 . 每个具有名字像 protocol_response()的handler都调用该方法来对响应进行后处理。

译者注
上面这三个步骤,如果不明白可以看下我从源码中提权的部分,
protocol 是个各种类型的协议

class OpenerDirector:
	#...省略
	    def open(self, fullurl, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
	        # ...
	
	        # pre-process request 阶段1
	        meth_name = protocol+"_request"
	        for processor in self.process_request.get(protocol, []):
	            meth = getattr(processor, meth_name)
	            req = meth(req)
			# 阶段2
	        response = self._open(req, data) 
	
	        # post-process response 阶段3
	        meth_name = protocol+"_response"
	        for processor in self.process_response.get(protocol, []):
	            meth = getattr(processor, meth_name)
	            response = meth(req, response)
	
	        return response
	
	

4.BaseHandler

BaseHandler对象提供了一些直接有用的方法,以及其他旨在由派生类使用的方法。这些是直接用途:

4.1 方法

add_parent(director)

添加director(某个OpenerDirector实例)作为parent。

>>> h = request.BaseHandler()
>>> opener = request.build_opener(h)
>>> h.add_parent(opener)
>>> h.parent
<urllib.request.OpenerDirector object at 0x000002A533713DA0>

close()

删除任何parent。

以下属性和方法只能由派生自BaseHandler的类使用 。

注意
已采用该约定,子类定义的 protocol_request()或protocol_response()方法 为*Processor; 其它的为*Handler

parent

一个可用的OpenerDirector,可用于使用不同的协议打开url,或处理错误。

default_open(req)

这种方法在BaseHandler中没有定义,但子类应该定义它,如果他们想捕获所有的URL。

如果实现了此方法,则parent OpenerDirector将调用此方法。它应该为OpenerDirector的open()函数返回一个类文件对象 或None。错误的类型不一定都是URLError(例如,MemoryError不等同于 URLError),所以要清楚引发的是什么错误。

在任何特定于协议的打开方法之前将调用此方法。

译者实例
我自己定义了一个BaseHandler子类,实现了default_open方法,为每次请求增加一个HTTP请求头name

def ulib_req_bh_cls():
    class MyHandler(request.BaseHandler):
        def __init__(self,headers):
            self.headers = headers
        def default_open(self,req):
            print(req.header_items())
            for k,v in self.headers.items():
                req.add_header(k,v)
            return None
    h = MyHandler({"name":"leng"})
    o = request.build_opener(h)
    resp  = o.open('http://127.0.0.1:8000/findg/')
    
ulib_req_bh_cls()
# 输出结果
[('Host', '127.0.0.1:8000'), ('User-agent', 'Python-urllib/3.7')]

在服务端打印请求头可以看到,name变成了HTTP_NAME

...
'HTTP_HOST': '127.0.0.1:8000', 
'HTTP_USER_AGENT': 'Python-urllib/3.7',
'HTTP_NAME': 'leng', 
'HTTP_CONNECTION': 'close'
...

protocol_open(req)

这种方法在BaseHandler中没有定义,但子类应该定义它,如果他们想处理给定协议的URL。

如果已定义此方法,将由 parent OpenerDirector调用。返回值应与default_open()中的相同。

unknown_open(req)

这种方法在BaseHandler中没有定义,但子类应该定义它,如果他们想捕获URL(这些URL没有一个注册的handler能将其打开)。

如果已定义此方法,将由 parent OpenerDirector调用。返回值应与default_open()中的相同。

http_error_default(req, fp, code, msg, hdrs)

这种方法在BaseHandler中没有定义,但子类应该定义它,如果他们打算处理其他handler无法处理的HTTP错误。它将通过OpenerDirector自动调用并获取错误 ,通常不应在其他情况下调用。

req将是一个Request对象,fp将是一个带有HTTP错误体的文件类对象,code是错误的三位数代码,msg 将是用户可见的code解释,hdrs是一个映射对象带有错误的标题。

返回值和异常同 urlopen()。

http_error_nnn(req, fp, code, msg, hdrs)

nnn应该是一个三位数的HTTP错误代码。BaseHandler类中此方法也未定义,当发生代码为nnn的HTTP错误时,如果子类实现了此方法,将会被调用。

子类应重写此方法以处理特定的HTTP错误。

返回值和异常同 http_error_default()。

protocol_request(req)

这种方法在BaseHandler中没有定义,但子类应该定义它,如果他们想预先处理给定协议的请求。

req是一个Request对象。返回值应该是一个 Request对象。

protocol_response(req, response)

这种方法在BaseHandler中没有定义,但子类应该定义它,如果他们想进行后处理指定协议的响应。

req是一个Request对象。response是一个实现与urlopen()返回值相同的接口的对象。返回值应该实现为与urlopen()返回值相同的接口 。

你可能感兴趣的:(翻译,Python,3.7,Python)