20.5. urllib — 通过 URL 打开任意资源 — Python 文档

来自菜鸟教程
Python/docs/2.7/library/urllib
跳转至:导航、​搜索

20.5. 网址库 — 通过 URL 打开任意资源

笔记

urllib 模块已被拆分为多个部分,并在 Python 3 中重命名为 urllib.requesturllib.parseurllib.error2to3 工具将在将您的源代码转换为 Python 3 时自动调整导入。 另请注意,Python 3 中的 urllib.request.urlopen() 函数等效于 urllib2.urlopen() 并且 urllib.urlopen() 已被删除。


该模块提供了一个用于在万维网上获取数据的高级接口。 特别是,urlopen() 函数类似于内置函数 open(),但接受通用资源定位器 (URL) 而不是文件名。 有一些限制——它只能打开 URL 进行阅读,并且没有可用的搜索操作。

也可以看看

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


2.7.9 版更改: 对于 HTTPS URI,urllib 默认执行所有必要的证书和主机名检查。


警告

对于 2.7.9 之前的 Python 版本,urllib 不会尝试验证 HTTPS URI 的服务器证书。 使用风险自负!


20.5.1. 高级接口

urllib.urlopen(url[, data[, proxies[, context]]])

打开一个由 URL 表示的网络对象进行读取。 如果 URL 没有方案标识符,或者它有 file: 作为其方案标识符,这将打开一个本地文件(没有 通用换行符 ); 否则它会打开一个连接到网络上某处服务器的套接字。 如果无法建立连接,则会引发 IOError 异常。 如果一切顺利,将返回一个类似文件的对象。 这支持以下方法:read()readline()readlines()fileno()close()info() , getcode()geturl()。 它还对 迭代器 协议有适当的支持。 一个警告:read() 方法,如果 size 参数被省略或为负,可能直到数据流结束才读取; 在一般情况下,没有好的方法可以确定来自套接字的整个流是否已被读取。

除了 info()getcode()geturl() 方法外,这些方法与文件对象具有相同的接口 - 请参阅本手册中的 文件对象 部分。 (但是,它不是内置文件对象,因此不能在需要真正内置文件对象的少数地方使用。)

info() 方法返回类 mimetools.Message 的实例,其中包含与 URL 关联的元信息。 当方法为 HTTP 时,这些头是服务器在检索到的 HTML 页面的头部返回的头(包括 Content-Length 和 Content-Type)。 当方法是 FTP 时,如果(现在通常)服务器回传文件长度以响应 FTP 检索请求,则将出现 Content-Length 标头。 如果可以猜测 MIME 类型,则将出现 Content-Type 标头。 当方法是 local-file 时,返回的标头将包括表示文件上次修改时间的 Date、给出文件大小的 Content-Length 和包含对文件类型的猜测的 Content-Type。 另请参阅 mimetools 模块的说明。

geturl() 方法返回页面的真实 URL。 在某些情况下,HTTP 服务器会将客户端重定向到另一个 URL。 urlopen() 函数透明地处理这个,但在某些情况下,调用者需要知道客户端被重定向到哪个 URL。 geturl() 方法可用于获取此重定向 URL。

getcode() 方法返回随响应发送的 HTTP 状态代码,如果 URL 不是 HTTP URL,则返回 None

如果 url 使用 http: 方案标识符,则可以提供可选的 data 参数来指定 POST 请求(通常请求类型为 [ X168X])。 data 参数必须是标准的 application/x-www-form-urlencoded 格式; 请参阅下面的 urlencode() 函数。

urlopen() 函数与不需要身份验证的代理透明地工作。 在 Unix 或 Windows 环境中,在启动 Python 解释器之前,将 http_proxyftp_proxy 环境变量设置为标识代理服务器的 URL . 例如('%' 是命令提示符):

% http_proxy="http://www.someproxy.com:3128"
% export http_proxy
% python
...

no_proxy 环境变量可用于指定不应通过代理访问的主机; 如果设置,它应该是一个逗号分隔的主机名后缀列表,可选择附加 :port,例如 cern.ch,ncsa.uiuc.edu,some.host:8080

在 Windows 环境中,如果未设置代理环境变量,则从注册表的 Internet 设置部分获取代理设置。

在 Mac OS X 环境中,urlopen() 将从 OS X 系统配置框架中检索代理信息,可以通过网络系统首选项面板进行管理。

或者,可选的 proxies 参数可用于显式指定代理。 它必须是代理 URL 的字典映射方案名称,其中空字典导致不使用代理,None(默认值)导致使用环境代理设置,如上所述。 例如:

# Use http://www.someproxy.com:3128 for HTTP proxying
proxies = {'http': 'http://www.someproxy.com:3128'}
filehandle = urllib.urlopen(some_url, proxies=proxies)
# Don't use any proxies
filehandle = urllib.urlopen(some_url, proxies={})
# Use proxies from environment - both versions are equivalent
filehandle = urllib.urlopen(some_url, proxies=None)
filehandle = urllib.urlopen(some_url)

当前不支持需要身份验证才能使用的代理; 这被视为实施限制。

context 参数可以设置为 ssl.SSLContext 实例,以配置在 urlopen() 建立 HTTPS 连接时使用的 SSL 设置。

2.3 版更改: 添加了 代理 支持。

2.6 版变更: 增加 getcode() 返回对象并支持 no_proxy 环境变量。

在 2.7.9 版更改: 添加了 上下文 参数。 默认情况下,所有必要的证书和主机名检查都已完成。

自 2.6 版起已弃用:urlopen() 函数已在 Python 3 中删除,取而代之的是 urllib2.urlopen()

urllib.urlretrieve(url[, filename[, reporthook[, data[, context]]]])

如有必要,将由 URL 表示的网络对象复制到本地文件。 如果 URL 指向本地文件,或者存在对象的有效缓存副本,则不会复制对象。 返回一个元组 (filename, headers),其中 filename 是可以在其下找到对象的本地文件名,headers 是任何 info() 方法的返回的 urlopen() 返回的对象(对于远程对象,可能已缓存)。 例外情况与 urlopen() 相同。

第二个参数(如果存在)指定要复制到的文件位置(如果不存在,该位置将是具有生成名称的临时文件)。 第三个参数(如果存在)是一个可调用对象,它将在建立网络连接时调用一次,之后在每个块读取后调用一次。 callable 将传递三个参数; 到目前为止传输的块数、块大小(以字节为单位)以及文件的总大小。 第三个参数可能是 -1 在较旧的 FTP 服务器上,这些服务器不响应检索请求返回文件大小。

如果 url 使用 http: 方案标识符,则可以提供可选的 data 参数来指定 POST 请求(通常请求类型为 [ X168X])。 data 参数必须是标准的 application/x-www-form-urlencoded 格式; 请参阅下面的 urlencode() 函数。

context 参数可以设置为 ssl.SSLContext 实例以配置 SSL 设置,如果 urlretrieve() 进行 HTTPS 连接。

2.5 版更改: urlretrieve() 当检测到可用数据量小于预期数据量(即报告的大小)时,将引发 ContentTooShortError通过 Content-Length 标头)。 例如,当下载中断时可能会发生这种情况。

Content-Length 被视为下限:如果有更多数据要读取,urlretrieve() 读取更多数据,但如果可用数据较少,则会引发异常。

在这种情况下,您仍然可以检索下载的数据,它存储在异常实例的 content 属性中。

如果没有提供 Content-Length 标头,则 urlretrieve() 无法检查它下载的数据的大小,只会返回它。 在这种情况下,您只需假设下载成功。

在 2.7.9 版更改: 添加了 上下文 参数。 默认情况下,所有必要的证书和主机名检查都已完成。

urllib._urlopener

公共函数 urlopen()urlretrieve() 创建了一个 FancyURLopener 类的实例,并使用它来执行他们请求的操作。 要覆盖此功能,程序员可以创建 URLopenerFancyURLopener 的子类,然后在调用所需函数之前将该类的实例分配给 urllib._urlopener 变量。 例如,应用程序可能想要指定与 URLopener 定义不同的 User-Agent 标头。 这可以通过以下代码完成:

import urllib

class AppURLopener(urllib.FancyURLopener):
    version = "App/1.7"

urllib._urlopener = AppURLopener()
urllib.urlcleanup()
清除之前调用 urlretrieve() 可能建立的缓存。


20.5.2. 实用功能

urllib.quote(string[, safe])

使用 %xx 转义替换 string 中的特殊字符。 从不引用字母、数字和字符 '_.-'。 默认情况下,此函数用于引用 URL 的路径部分。 可选的 safe 参数指定不应被引用的附加字符——其默认值为 '/'

示例:quote('/~connolly/') 产生 '/%7econnolly/'

urllib.quote_plus(string[, safe])
quote() 类似,但也用加号替换空格,这是在构建查询字符串以进入 URL 时引用 HTML 表单值所必需的。 原始字符串中的加号会被转义,除非它们包含在 safe 中。 它也没有 safe 默认为 '/'
urllib.unquote(string)

用等效的单字符替换 %xx 转义符。

示例:unquote('/%7Econnolly/') 产生 '/~connolly/'

urllib.unquote_plus(string)
unquote() 类似,但也用空格替换加号,这是取消引用 HTML 表单值所需的。
urllib.urlencode(query[, doseq])
将映射对象或二元素元组序列转换为“百分比编码”字符串,适合作为可选的 data 参数传递给上面的 urlopen()。 这对于将表单字段字典传递给 POST 请求很有用。 结果字符串是一系列由 '&' 字符分隔的 key=value 对,其中 keyvalue 都使用 quote_plus() 引用 以上。 当一个双元素元组序列用作 query 参数时,每个元组的第一个元素是一个键,第二个是一个值。 值元素本身可以是一个序列,在这种情况下,如果可选参数 doseq 的计算结果为 True,单个 key=value 对由 '&' 分隔] 是为键的值序列的每个元素生成的。 编码字符串中参数的顺序将与序列中参数元组的顺序匹配。 urlparse 模块提供了函数 parse_qs()parse_qsl(),用于将查询字符串解析为 Python 数据结构。
urllib.pathname2url(path)
将路径名 path 从路径的本地语法转换为 URL 的路径组件中使用的形式。 这不会生成完整的 URL。 已经使用 quote() 函数引用了返回值。
urllib.url2pathname(path)
将路径组件 path 从百分比编码的 URL 转换为路径的本地语法。 这不接受完整的 URL。 此函数使用 unquote() 解码 path
urllib.getproxies()

这个辅助函数返回一个方案字典到代理服务器 URL 映射。 它扫描环境中名为 <scheme>_proxy 的变量,以不区分大小写的方式,首先针对所有操作系统,当找不到时,从 Mac OS X 的 Mac OS X 系统配置和 Windows 系统注册表中查找代理信息。视窗。 如果小写和大写环境变量都存在(并且不同意),则首选小写。

笔记

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

笔记

urllib 还公开了某些实用功能,如 splittype、splithost 和其他将 URL 解析为各种组件的功能。 但是建议使用 urlparse 来解析 URL,而不是直接使用这些函数。 Python 3 没有从 urllib.parse 模块公开这些辅助函数。


20.5.3. URL 打开器对象

class urllib.URLopener([proxies[, context[, **x509]]])

打开和读取 URL 的基类。 除非您需要支持使用 http:ftp:file: 以外的方案打开对象,您可能想要使用 FancyURLopener

默认情况下,URLopener 类发送 urllib/VVVUser-Agent 标头,其中 VVVurllib 版本数字。 应用程序可以通过子类化 URLopenerFancyURLopener 并将类属性 version 设置为适当的字符串值来定义自己的 User-Agent 标头子类定义。

可选的 proxies 参数应该是将方案名称映射到代理 URL 的字典,其中空字典完全关闭代理。 它的默认值为 None,在这种情况下,将使用环境代理设置(如果存在),如上面 urlopen() 的定义中所述。

context 参数可以是一个 ssl.SSLContext 实例。 如果给定,它定义了 opener 用于建立 HTTPS 连接的 SSL 设置。

x509 中收集的附加关键字参数可用于在使用 https: 方案时对客户端进行身份验证。 支持关键字 key_filecert_file 提供 SSL 密钥和证书; 两者都需要支持客户端身份验证。

如果服务器返回错误代码,URLopener 对象将引发 IOError 异常。

在 2.7.9 版更改: 添加了 上下文 参数。 默认情况下,所有必要的证书和主机名检查都已完成。

open(fullurl[, data])

使用适当的协议打开 fullurl。 此方法设置缓存和代理信息,然后使用其输入参数调用适当的 open 方法。 如果方案未被识别,则调用 open_unknown()data 参数与 urlopen()data 参数含义相同。

open_unknown(fullurl[, data])

用于打开未知 URL 类型的可覆盖接口。

retrieve(url[, filename[, reporthook[, data]]])

检索 url 的内容并将其放置在 filename 中。 返回值是一个元组,由本地文件名和包含响应标头的 mimetools.Message 对象(对于远程 URL)或 None(对于本地 URL)组成。 然后调用者必须打开并读取 filename 的内容。 如果未给出 filename 并且 URL 引用本地文件,则返回输入文件名。 如果 URL 是非本地的且未给出 filename,则文件名是 tempfile.mktemp() 的输出,后缀与最后一个路径组件的后缀匹配输入网址。 如果给出 reporthook,它必须是一个接受三个数字参数的函数。 它将在从网络读取每个数据块后调用。 reporthook 被本地 URL 忽略。

如果 url 使用 http: 方案标识符,则可以提供可选的 data 参数来指定 POST 请求(通常请求类型为 [ X168X])。 data 参数必须是标准的 application/x-www-form-urlencoded 格式; 请参阅下面的 urlencode() 函数。

version

指定 opener 对象的用户代理的变量。 要让 urllib 告诉服务器它是一个特定的用户代理,请在调用基本构造函数之前在子类中将其设置为类变量或在构造函数中。

class urllib.FancyURLopener(...)

FancyURLopener 子类 URLopener 为以下 HTTP 响应代码提供默认处理:301、302、303、307 和 401。 对于上面列出的 30x 响应代码,Location 标头用于获取实际 URL。 对于 401 响应代码(需要身份验证),执行基本的 HTTP 身份验证。 对于 30x 响应代码,递归受 maxtries 属性值的限制,默认为 10。

对于所有其他响应代码,将调用方法 http_error_default(),您可以在子类中覆盖该方法以适当地处理错误。

笔记

根据 RFC 2616 的信函,对 POST 请求的 301 和 302 响应不得在未经用户确认的情况下自动重定向。 实际上,浏览器确实允许自动重定向这些响应,将 POST 更改为 GET,并且 urllib 重现了这种行为。

构造函数的参数与 URLopener 的参数相同。

笔记

执行基本身份验证时,FancyURLopener 实例调用其 prompt_user_passwd() 方法。 默认实现要求用户提供有关控制终端的所需信息。 如果需要,子类可以覆盖此方法以支持更合适的行为。

FancyURLopener 类提供了一种额外的方法,应该重载以提供适当的行为:

prompt_user_passwd(host, realm)

返回在指定安全领域中的给定主机上对用户进行身份验证所需的信息。 返回值应该是一个元组,(user, password),可用于基本认证。

实现在终端提示输入此信息; 应用程序应覆盖此方法以在本地环境中使用适当的交互模型。

exception urllib.ContentTooShortError(msg[, content])

urlretrieve() 函数检测到下载的数据量小于预期量(由 Content-Length 标头给出)时,会引发此异常。 content 属性存储下载的(并且可能被截断的)数据。

2.5 版中的新功能。


20.5.4. 网址库限制


  • 目前,仅支持以下协议:HTTP(版本 0.9 和 1.0)、FTP 和本地文件。

  • urlretrieve() 的缓存功能已被禁用,直到我有时间破解对过期时间标头的正确处理。

  • 应该有一个函数来查询特定的 URL 是否在缓存中。

  • 为了向后兼容,如果 URL 似乎指向本地文件但无法打开该文件,则使用 FTP 协议重新解释该 URL。 这有时会导致令人困惑的错误消息。

  • urlopen()urlretrieve() 函数在等待建立网络连接时可能会导致任意长的延迟。 这意味着在不使用线程的情况下很难使用这些功能构建交互式 Web 客户端。

  • urlopen()urlretrieve()返回的数据是服务器返回的原始数据。 这可能是二进制数据(例如图像)、纯文本或(例如)HTML。 HTTP 协议在回复标头中提供类型信息,可以通过查看 Content-Type 标头进行检查。 如果返回的数据是HTML,可以使用模块htmllib来解析。

  • 处理 FTP 协议的代码无法区分文件和目录。 在尝试读取指向无法访问的文件的 URL 时,这可能会导致意外行为。 如果 URL 以 / 结尾,则假定指向一个目录并将进行相应处理。 但是如果尝试读取文件导致 550 错误(意味着 URL 无法找到或无法访问,通常是出于权限原因),则该路径将被视为目录以处理指定目录的情况通过 URL 但尾随的 / 已被删除。 当您尝试获取读取权限使其无法访问的文件时,这可能会导致误导结果; FTP 代码将尝试读取它,失败并显示 550 错误,然后为不可读文件执行目录列表。 如果需要细粒度控制,可以考虑使用 ftplib 模块,子类化 FancyURLopener,或者更改 _urlopener 以满足您的需求。

  • 此模块不支持使用需要身份验证的代理。 这可能会在未来实施。

  • 尽管 urllib 模块包含(未记录的)用于解析和解解析 URL 字符串的例程,但推荐的 URL 操作接口在模块 urlparse 中。


20.5.5. 例子

以下是使用 GET 方法检索包含参数的 URL 的示例会话:

>>> import urllib
>>> params = urllib.urlencode({'spam': 1, 'eggs': 2, 'bacon': 0})
>>> f = urllib.urlopen("http://www.musi-cal.com/cgi-bin/query?%s" % params)
>>> print f.read()

以下示例改用 POST 方法:

>>> import urllib
>>> params = urllib.urlencode({'spam': 1, 'eggs': 2, 'bacon': 0})
>>> f = urllib.urlopen("http://www.musi-cal.com/cgi-bin/query", params)
>>> print f.read()

以下示例使用显式指定的 HTTP 代理,覆盖环境设置:

>>> import urllib
>>> proxies = {'http': 'http://proxy.example.com:8080/'}
>>> opener = urllib.FancyURLopener(proxies)
>>> f = opener.open("http://www.python.org")
>>> f.read()

以下示例根本不使用代理,覆盖环境设置:

>>> import urllib
>>> opener = urllib.FancyURLopener({})
>>> f = opener.open("http://www.python.org/")
>>> f.read()