“Python/docs/3.9/howto/urllib2”的版本间差异

来自菜鸟教程
Python/docs/3.9/howto/urllib2
跳转至:导航、​搜索
(autoload)
 
(Page commit)
 
第1行: 第1行:
 +
{{DISPLAYTITLE:HOWTO 使用 urllib 包获取 Internet 资源 — Python 文档}}
 
<div id="howto-fetch-internet-resources-using-the-urllib-package" class="section">
 
<div id="howto-fetch-internet-resources-using-the-urllib-package" class="section">
  
 
<span id="urllib-howto"></span>
 
<span id="urllib-howto"></span>
= HOWTO Fetch Internet Resources Using The urllib Package =
+
= HOWTO 使用 urllib 包获取 Internet 资源 =
  
; Author
+
; 作者
: [http://www.voidspace.org.uk/python/index.shtml Michael Foord]
+
: [http://www.voidspace.org.uk/python/index.shtml 迈克尔·福特]
  
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
There is a French translation of an earlier revision of this
+
HOWTO 的早期修订版有法语翻译,可从 [http://www.voidspace.org.uk/python/articles/urllib2_francais.shtml urllib2 - Le Manuel manquant] 获得。
HOWTO, available at [http://www.voidspace.org.uk/python/articles/urllib2_francais.shtml urllib2 - Le Manuel manquant].
 
  
  
第18行: 第18行:
 
<div id="introduction" class="section">
 
<div id="introduction" class="section">
  
== Introduction ==
+
== 简介 ==
  
<div class="sidebar">
+
相关文章
  
Related Articles
+
您可能还会发现以下有关使用 Python 获取 Web 资源的文章很有用:
 
 
You may also find useful the following article on fetching web resources
 
with Python:
 
  
 
<ul>
 
<ul>
<li><p>[http://www.voidspace.org.uk/python/articles/authentication.shtml Basic Authentication]</p>
+
<li><p>[http://www.voidspace.org.uk/python/articles/authentication.shtml 基本认证]</p>
 
<blockquote><div>
 
<blockquote><div>
  
<p>A tutorial on ''Basic Authentication'', with examples in Python.</p>
+
<p>关于 ''基本身份验证'' 的教程,以及 Python 中的示例。</p>
  
 
</div></blockquote></li></ul>
 
</div></blockquote></li></ul>
  
 +
'''urllib.request''' 是一个用于获取 URL(统一资源定位器)的 Python 模块。 它以 ''urlopen'' 函数的形式提供了一个非常简单的界面。 这能够使用各种不同的协议获取 URL。 它还提供了一个稍微复杂一些的界面来处理常见情况——比如基本身份验证、cookies、代理等等。 这些由称为处理程序和开启程序的对象提供。
  
</div>
+
urllib.request 支持获取许多“URL 方案”的 URL(由 URL 中 <code>&quot;:&quot;</code> 之前的字符串标识 - 例如 <code>&quot;ftp&quot;</code> 是 <code>&quot;ftp://python.org/&quot;</code> 的 URL 方案)使用它们的相关的网络协议(例如 FTP、HTTP)。 本教程重点介绍最常见的情况,HTTP。
'''urllib.request''' is a Python module for fetching URLs
 
(Uniform Resource Locators). It offers a very simple interface, in the form of
 
the ''urlopen'' function. This is capable of fetching URLs using a variety of
 
different protocols. It also offers a slightly more complex interface for
 
handling common situations - like basic authentication, cookies, proxies and so
 
on. These are provided by objects called handlers and openers.
 
  
urllib.request supports fetching URLs for many &quot;URL schemes&quot; (identified by the string
+
对于简单的情况 ''urlopen'' 非常容易使用。 但是,一旦您在打开 HTTP URL 时遇到错误或非平凡情况,您就需要对超文本传输协议有所了解。 HTTP 最全面、最权威的参考文献是 <span id="index-0" class="target"></span>[https://tools.ietf.org/html/rfc2616.html RFC 2616]。 这是一份技术文档,并非旨在易于阅读。 本 HOWTO 旨在说明如何使用 ''urllib'',其中包含有关 HTTP 的足够详细信息以帮助您完成。 它不是要替换 [[../../library/urllib.request#module-urllib|urllib.request]] 文档,而是对它们的补充。
before the <code>&quot;:&quot;</code> in URL - for example <code>&quot;ftp&quot;</code> is the URL scheme of
 
<code>&quot;ftp://python.org/&quot;</code>) using their associated network protocols (e.g. FTP, HTTP).
 
This tutorial focuses on the most common case, HTTP.
 
 
 
For straightforward situations ''urlopen'' is very easy to use. But as soon as you
 
encounter errors or non-trivial cases when opening HTTP URLs, you will need some
 
understanding of the HyperText Transfer Protocol. The most comprehensive and
 
authoritative reference to HTTP is <span id="index-0" class="target"></span>[https://tools.ietf.org/html/rfc2616.html '''RFC 2616''']. This is a technical document and
 
not intended to be easy to read. This HOWTO aims to illustrate using ''urllib'',
 
with enough detail about HTTP to help you through. It is not intended to replace
 
the [[../../library/urllib.request#module-urllib|<code>urllib.request</code>]] docs, but is supplementary to them.
 
  
  
第61行: 第42行:
 
<div id="fetching-urls" class="section">
 
<div id="fetching-urls" class="section">
  
== Fetching URLs ==
+
== 获取网址 ==
  
The simplest way to use urllib.request is as follows:
+
最简单的使用 urllib.request 的方法如下:
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第69行: 第50行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>import urllib.request
+
<syntaxhighlight lang="python3">import urllib.request
 
with urllib.request.urlopen('http://python.org/') as response:
 
with urllib.request.urlopen('http://python.org/') as response:
   html = response.read()</pre>
+
   html = response.read()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
If you wish to retrieve a resource via URL and store it in a temporary
+
如果您希望通过 URL 检索资源并将其存储在临时位置,您可以通过 [[../../library/shutil#shutil|shutil.copyfileobj()]] [[../../library/tempfile#tempfile|tempfile.NamedTemporaryFile()]] 函数来实现:
location, you can do so via the [[../../library/shutil#shutil|<code>shutil.copyfileobj()</code>]] and
 
[[../../library/tempfile#tempfile|<code>tempfile.NamedTemporaryFile()</code>]] functions:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第84行: 第63行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>import shutil
+
<syntaxhighlight lang="python3">import shutil
 
import tempfile
 
import tempfile
 
import urllib.request
 
import urllib.request
第93行: 第72行:
  
 
with open(tmp_file.name) as html:
 
with open(tmp_file.name) as html:
     pass</pre>
+
     pass</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Many uses of urllib will be that simple (note that instead of an 'http:' URL we
+
urllib 的许多用途就是这么简单(请注意,我们可以使用以 'ftp:''file:' 等开头的 URL,而不是 'http:' URL)。 但是,本教程的目的是解释更复杂的情况,重点是 HTTP。
could have used a URL starting with 'ftp:', 'file:', etc.). However, it's the
 
purpose of this tutorial to explain the more complicated cases, concentrating on
 
HTTP.
 
  
HTTP is based on requests and responses - the client makes requests and servers
+
HTTP 基于请求和响应 - 客户端发出请求,服务器发送响应。 urllib.request <code>Request</code> 对象反映了这一点,该对象代表您正在发出的 HTTP 请求。 以最简单的形式创建一个 Request 对象,该对象指定要获取的 URL。 使用此请求对象调用 <code>urlopen</code> 会返回所请求 URL 的响应对象。 这个响应是一个类似文件的对象,这意味着你可以在响应上调用 <code>.read()</code>
send responses. urllib.request mirrors this with a <code>Request</code> object which represents
 
the HTTP request you are making. In its simplest form you create a Request
 
object that specifies the URL you want to fetch. Calling <code>urlopen</code> with this
 
Request object returns a response object for the URL requested. This response is
 
a file-like object, which means you can for example call <code>.read()</code> on the
 
response:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第115行: 第85行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>import urllib.request
+
<syntaxhighlight lang="python3">import urllib.request
  
 
req = urllib.request.Request('http://www.voidspace.org.uk')
 
req = urllib.request.Request('http://www.voidspace.org.uk')
 
with urllib.request.urlopen(req) as response:
 
with urllib.request.urlopen(req) as response:
   the_page = response.read()</pre>
+
   the_page = response.read()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Note that urllib.request makes use of the same Request interface to handle all URL
+
请注意, urllib.request 使用相同的 Request 接口来处理所有 URL 方案。 例如,您可以像这样发出 FTP 请求:
schemes. For example, you can make an FTP request like so:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第131行: 第100行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>req = urllib.request.Request('ftp://example.com/')</pre>
+
<syntaxhighlight lang="python3">req = urllib.request.Request('ftp://example.com/')</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
In the case of HTTP, there are two extra things that Request objects allow you
+
HTTP 的情况下,Request 对象允许您做两件额外的事情:首先,您可以传递要发送到服务器的数据。 其次,您可以将额外信息(“元数据”)''关于'' 数据或关于请求本身的信息传递给服务器——这些信息作为 HTTP“标头”发送。 让我们依次看看这些。
to do: First, you can pass data to be sent to the server. Second, you can pass
 
extra information (&quot;metadata&quot;) ''about'' the data or the about request itself, to
 
the server - this information is sent as HTTP &quot;headers&quot;. Let's look at each of
 
these in turn.
 
  
 
<div id="data" class="section">
 
<div id="data" class="section">
  
=== Data ===
+
=== 数据 ===
  
Sometimes you want to send data to a URL (often the URL will refer to a CGI
+
有时您想将数据发送到 URL(通常 URL 将引用 CGI(通用网关接口)脚本或其他 Web 应用程序)。 对于 HTTP,这通常使用所谓的 '''POST''' 请求来完成。 当您提交在 Web 上填写的 HTML 表单时,浏览器通常会执行此操作。 并非所有 POST 都必须来自表单:您可以使用 POST 将任意数据传输到您自己的应用程序。 在 HTML 表单的常见情况下,数据需要以标准方式进行编码,然后作为 <code>data</code> 参数传递给 Request 对象。 编码是使用 [[../../library/urllib.parse#module-urllib|urllib.parse]] 库中的函数完成的。
(Common Gateway Interface) script or other web application). With HTTP,
 
this is often done using what's known as a '''POST''' request. This is often what
 
your browser does when you submit a HTML form that you filled in on the web. Not
 
all POSTs have to come from forms: you can use a POST to transmit arbitrary data
 
to your own application. In the common case of HTML forms, the data needs to be
 
encoded in a standard way, and then passed to the Request object as the <code>data</code>
 
argument. The encoding is done using a function from the [[../../library/urllib.parse#module-urllib|<code>urllib.parse</code>]]
 
library.
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第160行: 第117行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>import urllib.parse
+
<syntaxhighlight lang="python3">import urllib.parse
 
import urllib.request
 
import urllib.request
  
第172行: 第129行:
 
req = urllib.request.Request(url, data)
 
req = urllib.request.Request(url, data)
 
with urllib.request.urlopen(req) as response:
 
with urllib.request.urlopen(req) as response:
   the_page = response.read()</pre>
+
   the_page = response.read()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Note that other encodings are sometimes required (e.g. for file upload from HTML
+
请注意,有时需要其他编码(例如 用于从 HTML 表单上传文件 - 有关更多详细信息,请参阅 [https://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13 HTML 规范,表单提交] )。
forms - see [https://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13 HTML Specification, Form Submission] for more
 
details).
 
  
If you do not pass the <code>data</code> argument, urllib uses a '''GET''' request. One
+
如果不传递 <code>data</code> 参数,urllib 将使用 '''GET''' 请求。 GET POST 请求的不同之处在于 POST 请求通常具有“副作用”:它们以某种方式改变系统的状态(例如,通过向网站下订单以交付一百磅罐装垃圾邮件到你家门口)。 尽管 HTTP 标准明确指出 POST 旨在 ''总是'' 引起副作用,而 GET 请求 ''从不'' 引起副作用,但没有什么能阻止 GET 请求产生副作用,也没有 POST 请求没有副作用。 数据也可以通过在 URL 本身中编码来在 HTTP GET 请求中传递。
way in which GET and POST requests differ is that POST requests often have
 
&quot;side-effects&quot;: they change the state of the system in some way (for example by
 
placing an order with the website for a hundredweight of tinned spam to be
 
delivered to your door). Though the HTTP standard makes it clear that POSTs are
 
intended to ''always'' cause side-effects, and GET requests ''never'' to cause
 
side-effects, nothing prevents a GET request from having side-effects, nor a
 
POST requests from having no side-effects. Data can also be passed in an HTTP
 
GET request by encoding it in the URL itself.
 
  
This is done as follows:
+
这是按如下方式完成的:
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第197行: 第144行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; import urllib.request
+
<syntaxhighlight lang="python3">>>> import urllib.request
&gt;&gt;&gt; import urllib.parse
+
>>> import urllib.parse
&gt;&gt;&gt; data = {}
+
>>> data = {}
&gt;&gt;&gt; data['name'] = 'Somebody Here'
+
>>> data['name'] = 'Somebody Here'
&gt;&gt;&gt; data['location'] = 'Northampton'
+
>>> data['location'] = 'Northampton'
&gt;&gt;&gt; data['language'] = 'Python'
+
>>> data['language'] = 'Python'
&gt;&gt;&gt; url_values = urllib.parse.urlencode(data)
+
>>> url_values = urllib.parse.urlencode(data)
&gt;&gt;&gt; print(url_values)  # The order may differ from below.   
+
>>> print(url_values)  # The order may differ from below.   
name=Somebody+Here&amp;language=Python&amp;location=Northampton
+
name=Somebody+Here&language=Python&location=Northampton
&gt;&gt;&gt; url = 'http://www.example.com/example.cgi'
+
>>> url = 'http://www.example.com/example.cgi'
&gt;&gt;&gt; full_url = url + '?' + url_values
+
>>> full_url = url + '?' + url_values
&gt;&gt;&gt; data = urllib.request.urlopen(full_url)</pre>
+
>>> data = urllib.request.urlopen(full_url)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Notice that the full URL is created by adding a <code>?</code> to the URL, followed by
+
请注意,完整的 URL 是通过向 URL 添加 <code>?</code> 后跟编码值来创建的。
the encoded values.
 
  
  
第220行: 第166行:
 
<div id="headers" class="section">
 
<div id="headers" class="section">
  
=== Headers ===
+
=== 标题 ===
  
We'll discuss here one particular HTTP header, to illustrate how to add headers
+
我们将在此处讨论一个特定的 HTTP 标头,以说明如何向 HTTP 请求添加标头。
to your HTTP request.
 
  
Some websites [[#id8|1]] dislike being browsed by programs, or send different versions
+
一些网站[[#id8|1]]不喜欢被程序浏览,或者向不同浏览器发送不同版本[[#id9|2]]。 默认情况下,urllib 将自身标识为 <code>Python-urllib/x.y</code>(其中 <code>x</code> <code>y</code> Python 版本的主要和次要版本号,例如 <code>Python-urllib/2.5</code>),这可能会混淆站点,或者根本不起作用。 浏览器识别自己的方式是通过 <code>User-Agent</code> 标头 [[#id10|3]]。 当你创建一个 Request 对象时,你可以传入一个标头字典。 以下示例发出与上述相同的请求,但将自身标识为 Internet Explorer [[#id11|4]] 的版本。
to different browsers [[#id9|2]]. By default urllib identifies itself as
 
<code>Python-urllib/x.y</code> (where <code>x</code> and <code>y</code> are the major and minor version
 
numbers of the Python release,
 
e.g. <code>Python-urllib/2.5</code>), which may confuse the site, or just plain
 
not work. The way a browser identifies itself is through the
 
<code>User-Agent</code> header [[#id10|3]]. When you create a Request object you can
 
pass a dictionary of headers in. The following example makes the same
 
request as above, but identifies itself as a version of Internet
 
Explorer [[#id11|4]].
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第240行: 第176行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>import urllib.parse
+
<syntaxhighlight lang="python3">import urllib.parse
 
import urllib.request
 
import urllib.request
  
第254行: 第190行:
 
req = urllib.request.Request(url, data, headers)
 
req = urllib.request.Request(url, data, headers)
 
with urllib.request.urlopen(req) as response:
 
with urllib.request.urlopen(req) as response:
   the_page = response.read()</pre>
+
   the_page = response.read()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
The response also has two useful methods. See the section on [[#info-and-geturl|info and geturl]]
+
响应也有两个有用的方法。 请参阅关于 [[#info-and-geturl|info geturl]] 的部分,在我们了解出现问题时会发生什么之后。
which comes after we have a look at what happens when things go wrong.
 
  
  
第268行: 第203行:
 
<div id="handling-exceptions" class="section">
 
<div id="handling-exceptions" class="section">
  
== Handling Exceptions ==
+
== 处理异常 ==
  
''urlopen'' raises <code>URLError</code> when it cannot handle a response (though as
+
''urlopen'' 在无法处理响应时引发 <code>URLError</code>(尽管与 Python API 一样,内置异常如 [[../../library/exceptions#ValueError|ValueError]][[../../library/exceptions#TypeError|TypeError]] . 也可以提高)。
usual with Python APIs, built-in exceptions such as [[../../library/exceptions#ValueError|<code>ValueError</code>]],
 
[[../../library/exceptions#TypeError|<code>TypeError</code>]] etc. may also be raised).
 
  
<code>HTTPError</code> is the subclass of <code>URLError</code> raised in the specific case of
+
<code>HTTPError</code> <code>URLError</code> 的子类,在 HTTP URL 的特定情况下提出。
HTTP URLs.
 
  
The exception classes are exported from the [[../../library/urllib.error#module-urllib|<code>urllib.error</code>]] module.
+
异常类是从 [[../../library/urllib.error#module-urllib|urllib.error]] 模块导出的。
  
 
<div id="urlerror" class="section">
 
<div id="urlerror" class="section">
  
=== URLError ===
+
=== 网址错误 ===
  
Often, URLError is raised because there is no network connection (no route to
+
通常,由于没有网络连接(没有到指定服务器的路由)或指定的服务器不存在,会引发 URLError。 在这种情况下,引发的异常将具有“原因”属性,它是一个包含错误代码和文本错误消息的元组。
the specified server), or the specified server doesn't exist. In this case, the
 
exception raised will have a 'reason' attribute, which is a tuple containing an
 
error code and a text error message.
 
  
e.g.
+
例如
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第294行: 第223行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; req = urllib.request.Request('http://www.pretend_server.org')
+
<syntaxhighlight lang="python3">>>> req = urllib.request.Request('http://www.pretend_server.org')
&gt;&gt;&gt; try: urllib.request.urlopen(req)
+
>>> try: urllib.request.urlopen(req)
 
... except urllib.error.URLError as e:
 
... except urllib.error.URLError as e:
 
...    print(e.reason)       
 
...    print(e.reason)       
 
...
 
...
(4, 'getaddrinfo failed')</pre>
+
(4, 'getaddrinfo failed')</syntaxhighlight>
  
 
</div>
 
</div>
第308行: 第237行:
 
<div id="httperror" class="section">
 
<div id="httperror" class="section">
  
=== HTTPError ===
+
=== HTTP错误 ===
  
Every HTTP response from the server contains a numeric &quot;status code&quot;. Sometimes
+
来自服务器的每个 HTTP 响应都包含一个数字“状态代码”。 有时状态码表示服务器无法满足请求。 默认处理程序将为您处理其中一些响应(例如,如果响应是请求客户端从不同 URL 获取文档的“重定向”,则 urllib 将为您处理)。 对于无法处理的那些,urlopen 将引发 <code>HTTPError</code>。 典型的错误包括“404”(未找到页面)、“403”(请求被禁止)和“401”(需要身份验证)。
the status code indicates that the server is unable to fulfil the request. The
 
default handlers will handle some of these responses for you (for example, if
 
the response is a &quot;redirection&quot; that requests the client fetch the document from
 
a different URL, urllib will handle that for you). For those it can't handle,
 
urlopen will raise an <code>HTTPError</code>. Typical errors include '404' (page not
 
found), '403' (request forbidden), and '401' (authentication required).
 
  
See section 10 of <span id="index-1" class="target"></span>[https://tools.ietf.org/html/rfc2616.html '''RFC 2616'''] for a reference on all the HTTP error codes.
+
有关所有 HTTP 错误代码的参考,请参阅 <span id="index-1" class="target"></span>[https://tools.ietf.org/html/rfc2616.html RFC 2616] 的第 10 节。
  
The <code>HTTPError</code> instance raised will have an integer 'code' attribute, which
+
引发的 <code>HTTPError</code> 实例将具有一个整数 'code' 属性,它对应于服务器发送的错误。
corresponds to the error sent by the server.
 
  
 
<div id="error-codes" class="section">
 
<div id="error-codes" class="section">
  
==== Error Codes ====
+
==== 错误代码 ====
  
Because the default handlers handle redirects (codes in the 300 range), and
+
因为默认处理程序处理重定向(300 范围内的代码),并且 100-299 范围内的代码表示成功,所以您通常只会看到 400-599 范围内的错误代码。
codes in the 100--299 range indicate success, you will usually only see error
 
codes in the 400--599 range.
 
  
[[../../library/http.server#http.server.BaseHTTPRequestHandler|<code>http.server.BaseHTTPRequestHandler.responses</code>]] is a useful dictionary of
+
[[../../library/http.server#http.server.BaseHTTPRequestHandler|http.server.BaseHTTPRequestHandler.responses]] 是一个有用的响应代码字典,其中显示了 <span id="index-2" class="target"></span>[https://tools.ietf.org/html/rfc2616.html RFC 2616] 使用的所有响应代码。 为方便起见,此处转载词典
response codes in that shows all the response codes used by <span id="index-2" class="target"></span>[https://tools.ietf.org/html/rfc2616.html '''RFC 2616''']. The
 
dictionary is reproduced here for convenience
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第339行: 第257行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre># Table mapping response codes to messages; entries have the
+
<syntaxhighlight lang="python3"># Table mapping response codes to messages; entries have the
 
# form {code: (shortmessage, longmessage)}.
 
# form {code: (shortmessage, longmessage)}.
 
responses = {
 
responses = {
第405行: 第323行:
 
           'The gateway server did not receive a timely response'),
 
           'The gateway server did not receive a timely response'),
 
     505: ('HTTP Version Not Supported', 'Cannot fulfill request.'),
 
     505: ('HTTP Version Not Supported', 'Cannot fulfill request.'),
     }</pre>
+
     }</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
When an error is raised the server responds by returning an HTTP error code
+
当出现错误时,服务器通过返回 HTTP 错误代码 '''' 错误页面进行响应。 您可以使用 <code>HTTPError</code> 实例作为返回页面上的响应。 这意味着除了 code 属性外,它还具有 <code>urllib.response</code> 模块返回的 read、geturl 和 info 方法:
''and'' an error page. You can use the <code>HTTPError</code> instance as a response on the
 
page returned. This means that as well as the code attribute, it also has read,
 
geturl, and info, methods as returned by the <code>urllib.response</code> module:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第419行: 第334行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; req = urllib.request.Request('http://www.python.org/fish.html')
+
<syntaxhighlight lang="python3">>>> req = urllib.request.Request('http://www.python.org/fish.html')
&gt;&gt;&gt; try:
+
>>> try:
 
...    urllib.request.urlopen(req)
 
...    urllib.request.urlopen(req)
 
... except urllib.error.HTTPError as e:
 
... except urllib.error.HTTPError as e:
第427行: 第342行:
 
...
 
...
 
404
 
404
b'&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot;
+
b'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;\n\n\n&lt;html
+
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n\n\n<html
 
   ...
 
   ...
   &lt;title&gt;Page Not Found&lt;/title&gt;\n
+
   <title>Page Not Found</title>\n
   ...</pre>
+
   ...</syntaxhighlight>
  
 
</div>
 
</div>
第442行: 第357行:
 
<div id="wrapping-it-up" class="section">
 
<div id="wrapping-it-up" class="section">
  
=== Wrapping it Up ===
+
=== 包起来 ===
  
So if you want to be prepared for <code>HTTPError</code> ''or'' <code>URLError</code> there are two
+
因此,如果您想为 <code>HTTPError</code> '''' <code>URLError</code> 做好准备,有两种基本方法。 我更喜欢第二种方法。
basic approaches. I prefer the second approach.
 
  
 
<div id="number-1" class="section">
 
<div id="number-1" class="section">
  
==== Number 1 ====
+
==== 1号 ====
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第455行: 第369行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from urllib.request import Request, urlopen
+
<syntaxhighlight lang="python3">from urllib.request import Request, urlopen
 
from urllib.error import URLError, HTTPError
 
from urllib.error import URLError, HTTPError
 
req = Request(someurl)
 
req = Request(someurl)
第467行: 第381行:
 
     print('Reason: ', e.reason)
 
     print('Reason: ', e.reason)
 
else:
 
else:
     # everything is fine</pre>
+
     # everything is fine</syntaxhighlight>
  
 
</div>
 
</div>
第474行: 第388行:
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
The <code>except HTTPError</code> ''must'' come first, otherwise <code>except URLError</code>
+
<code>except HTTPError</code> ''必须''在前,否则<code>except URLError</code>''''赶上<code>HTTPError</code>
will ''also'' catch an <code>HTTPError</code>.
 
  
  
第485行: 第398行:
 
<div id="number-2" class="section">
 
<div id="number-2" class="section">
  
==== Number 2 ====
+
==== 2号 ====
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第491行: 第404行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>from urllib.request import Request, urlopen
+
<syntaxhighlight lang="python3">from urllib.request import Request, urlopen
 
from urllib.error import URLError
 
from urllib.error import URLError
 
req = Request(someurl)
 
req = Request(someurl)
第504行: 第417行:
 
         print('Error code: ', e.code)
 
         print('Error code: ', e.code)
 
else:
 
else:
     # everything is fine</pre>
+
     # everything is fine</syntaxhighlight>
  
 
</div>
 
</div>
第517行: 第430行:
 
<div id="info-and-geturl" class="section">
 
<div id="info-and-geturl" class="section">
  
== info and geturl ==
+
== 信息和获取网址 ==
  
The response returned by urlopen (or the <code>HTTPError</code> instance) has two
+
urlopen(或 <code>HTTPError</code> 实例)返回的响应有两个有用的方法 <code>info()</code> <code>geturl()</code> 并在模块 [[../../library/urllib.request#module-urllib|urllib.response]] 中定义..
useful methods <code>info()</code> and <code>geturl()</code> and is defined in the module
 
[[../../library/urllib.request#module-urllib|<code>urllib.response</code>]]..
 
  
'''geturl''' - this returns the real URL of the page fetched. This is useful
+
'''geturl''' - 这将返回所获取页面的真实 URL。 这很有用,因为 <code>urlopen</code>(或使用的 opener 对象)可能跟随重定向。 获取的页面的 URL 可能与请求的 URL 不同。
because <code>urlopen</code> (or the opener object used) may have followed a
 
redirect. The URL of the page fetched may not be the same as the URL requested.
 
  
'''info''' - this returns a dictionary-like object that describes the page
+
'''info''' - 这将返回一个类似字典的对象,描述所获取的页面,特别是服务器发送的标头。 它目前是一个 <code>http.client.HTTPMessage</code> 实例。
fetched, particularly the headers sent by the server. It is currently an
 
<code>http.client.HTTPMessage</code> instance.
 
  
Typical headers include 'Content-length', 'Content-type', and so on. See the
+
典型的标头包括“Content-length”、“Content-type”等。 请参阅 [http://jkorpela.fi/http.html HTTP 标头快速参考] 以获取有用的 HTTP 标头列表及其含义和用途的简要说明。
[http://jkorpela.fi/http.html Quick Reference to HTTP Headers]
 
for a useful listing of HTTP headers with brief explanations of their meaning
 
and use.
 
  
  
第540行: 第444行:
 
<div id="openers-and-handlers" class="section">
 
<div id="openers-and-handlers" class="section">
  
== Openers and Handlers ==
+
== 开瓶器和处理程序 ==
  
When you fetch a URL you use an opener (an instance of the perhaps
+
当你获取一个 URL 时,你使用了一个 opener(一个可能令人困惑的名称 [[../../library/urllib.request#urllib.request|urllib.request.OpenerDirector]] 的实例)。 通常我们一直使用默认的开启器 - 通过 <code>urlopen</code> - 但您可以创建自定义开启器。 开启者使用处理程序。 所有的“繁重工作”都由搬运工完成。 每个处理程序都知道如何打开特定 URL 方案(http、ftp 等)的 URL,或者如何处理 URL 打开的一个方面,例如 HTTP 重定向或 HTTP cookie。
confusingly-named [[../../library/urllib.request#urllib.request|<code>urllib.request.OpenerDirector</code>]]). Normally we have been using
 
the default opener - via <code>urlopen</code> - but you can create custom
 
openers. Openers use handlers. All the &quot;heavy lifting&quot; is done by the
 
handlers. Each handler knows how to open URLs for a particular URL scheme (http,
 
ftp, etc.), or how to handle an aspect of URL opening, for example HTTP
 
redirections or HTTP cookies.
 
  
You will want to create openers if you want to fetch URLs with specific handlers
+
如果您想获取安装了特定处理程序的 URL,您将需要创建 opener,例如获取处理 cookie 的 opener,或获取不处理重定向的 opener。
installed, for example to get an opener that handles cookies, or to get an
 
opener that does not handle redirections.
 
  
To create an opener, instantiate an <code>OpenerDirector</code>, and then call
+
要创建开启器,请实例化 <code>OpenerDirector</code>,然后重复调用 <code>.add_handler(some_handler_instance)</code>
<code>.add_handler(some_handler_instance)</code> repeatedly.
 
  
Alternatively, you can use <code>build_opener</code>, which is a convenience function for
+
或者,您可以使用 <code>build_opener</code>,这是一个使用单个函数调用创建 opener 对象的便捷函数。 <code>build_opener</code> 默认添加多个处理程序,但提供了一种快速添加和/或覆盖默认处理程序的方法。
creating opener objects with a single function call. <code>build_opener</code> adds
 
several handlers by default, but provides a quick way to add more and/or
 
override the default handlers.
 
  
Other sorts of handlers you might want to can handle proxies, authentication,
+
您可能想要的其他类型的处理程序可以处理代理、身份验证和其他常见但稍微特殊的情况。
and other common but slightly specialised situations.
 
  
<code>install_opener</code> can be used to make an <code>opener</code> object the (global) default
+
<code>install_opener</code> 可用于使 <code>opener</code> 对象成为(全局)默认开启器。 这意味着对 <code>urlopen</code> 的调用将使用您安装的开启程序。
opener. This means that calls to <code>urlopen</code> will use the opener you have
 
installed.
 
  
Opener objects have an <code>open</code> method, which can be called directly to fetch
+
Opener 对象有一个 <code>open</code> 方法,可以像 <code>urlopen</code> 函数一样直接调用它来获取 url:不需要调用 <code>install_opener</code>,除非是为了方便.
urls in the same way as the <code>urlopen</code> function: there's no need to call
 
<code>install_opener</code>, except as a convenience.
 
  
  
第577行: 第464行:
 
<div id="id5" class="section">
 
<div id="id5" class="section">
  
== Basic Authentication ==
+
== 基本认证 ==
  
To illustrate creating and installing a handler we will use the
+
为了说明创建和安装处理程序,我们将使用 <code>HTTPBasicAuthHandler</code>。 有关此主题的更详细讨论 - 包括对基本身份验证如何工作的解释 - 请参阅 [http://www.voidspace.org.uk/python/articles/authentication.shtml 基本身份验证教程]
<code>HTTPBasicAuthHandler</code>. For a more detailed discussion of this subject --
 
including an explanation of how Basic Authentication works - see the [http://www.voidspace.org.uk/python/articles/authentication.shtml Basic
 
Authentication Tutorial].
 
  
When authentication is required, the server sends a header (as well as the 401
+
当需要身份验证时,服务器会发送一个请求身份验证的标头(以及 401 错误代码)。 这指定了身份验证方案和“领域”。 标题看起来像:<code>WWW-Authenticate: SCHEME realm=&quot;REALM&quot;</code>
error code) requesting authentication. This specifies the authentication scheme
 
and a 'realm'. The header looks like: <code>WWW-Authenticate: SCHEME realm=&quot;REALM&quot;</code>.
 
  
e.g.
+
例如
  
 
<div class="highlight-none notranslate">
 
<div class="highlight-none notranslate">
第594行: 第476行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>WWW-Authenticate: Basic realm=&quot;cPanel Users&quot;</pre>
+
<pre class="none">WWW-Authenticate: Basic realm=&quot;cPanel Users&quot;</pre>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
The client should then retry the request with the appropriate name and password
+
然后,客户端应使用作为请求标头包含的领域的适当名称和密码重试请求。 这是“基本身份验证”。 为了简化这个过程,我们可以创建一个 <code>HTTPBasicAuthHandler</code> 的实例和一个开启器来使用这个处理程序。
for the realm included as a header in the request. This is 'basic
 
authentication'. In order to simplify this process we can create an instance of
 
<code>HTTPBasicAuthHandler</code> and an opener to use this handler.
 
  
The <code>HTTPBasicAuthHandler</code> uses an object called a password manager to handle
+
<code>HTTPBasicAuthHandler</code> 使用一个称为密码管理器的对象来处理 URL 和领域到密码和用户名的映射。 如果您知道领域是什么(来自服务器发送的身份验证标头),那么您可以使用 <code>HTTPPasswordMgr</code>。 通常人们并不关心领域是什么。 在这种情况下,使用 <code>HTTPPasswordMgrWithDefaultRealm</code> 会很方便。 这允许您为 URL 指定默认用户名和密码。 这将在您没有为特定领域提供替代组合的情况下提供。 我们通过提供 <code>None</code> 作为 <code>add_password</code> 方法的领域参数来表明这一点。
the mapping of URLs and realms to passwords and usernames. If you know what the
 
realm is (from the authentication header sent by the server), then you can use a
 
<code>HTTPPasswordMgr</code>. Frequently one doesn't care what the realm is. In that
 
case, it is convenient to use <code>HTTPPasswordMgrWithDefaultRealm</code>. This allows
 
you to specify a default username and password for a URL. This will be supplied
 
in the absence of you providing an alternative combination for a specific
 
realm. We indicate this by providing <code>None</code> as the realm argument to the
 
<code>add_password</code> method.
 
  
The top-level URL is the first URL that requires authentication. URLs &quot;deeper&quot;
+
顶级 URL 是第一个需要身份验证的 URL。 比传递给 .add_password() 的 URL“更深”的 URL 也将匹配。
than the URL you pass to .add_password() will also match.
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第621行: 第491行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre># create a password manager
+
<syntaxhighlight lang="python3"># create a password manager
 
password_mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
 
password_mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
  
 
# Add the username and password.
 
# Add the username and password.
 
# If we knew the realm, we could use it instead of None.
 
# If we knew the realm, we could use it instead of None.
top_level_url = &quot;http://example.com/foo/&quot;
+
top_level_url = "http://example.com/foo/"
 
password_mgr.add_password(None, top_level_url, username, password)
 
password_mgr.add_password(None, top_level_url, username, password)
  
 
handler = urllib.request.HTTPBasicAuthHandler(password_mgr)
 
handler = urllib.request.HTTPBasicAuthHandler(password_mgr)
  
# create &quot;opener&quot; (OpenerDirector instance)
+
# create "opener" (OpenerDirector instance)
 
opener = urllib.request.build_opener(handler)
 
opener = urllib.request.build_opener(handler)
  
第639行: 第509行:
 
# Install the opener.
 
# Install the opener.
 
# Now all calls to urllib.request.urlopen use our opener.
 
# Now all calls to urllib.request.urlopen use our opener.
urllib.request.install_opener(opener)</pre>
+
urllib.request.install_opener(opener)</syntaxhighlight>
  
 
</div>
 
</div>
第646行: 第516行:
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
In the above example we only supplied our <code>HTTPBasicAuthHandler</code> to
+
在上面的示例中,我们仅将 <code>HTTPBasicAuthHandler</code> 提供给 <code>build_opener</code>。 默认情况下,开启程序具有正常情况下的处理程序 - <code>ProxyHandler</code>(如果设置了代理设置,例如 <span id="index-3" class="target"></span><code>http_proxy</code> 环境变量)、<code>UnknownHandler</code>、[ X162X]、<code>HTTPDefaultErrorHandler</code><code>HTTPRedirectHandler</code><code>FTPHandler</code><code>FileHandler</code><code>DataHandler</code><code>HTTPErrorProcessor</code>.
<code>build_opener</code>. By default openers have the handlers for normal situations
 
-- <code>ProxyHandler</code> (if a proxy setting such as an <span id="index-3" class="target"></span><code>http_proxy</code>
 
environment variable is set), <code>UnknownHandler</code>, <code>HTTPHandler</code>,
 
<code>HTTPDefaultErrorHandler</code>, <code>HTTPRedirectHandler</code>, <code>FTPHandler</code>,
 
<code>FileHandler</code>, <code>DataHandler</code>, <code>HTTPErrorProcessor</code>.
 
  
  
 
</div>
 
</div>
<code>top_level_url</code> is in fact ''either'' a full URL (including the 'http:' scheme
+
<code>top_level_url</code> 实际上是 ''或者'' 一个完整的 URL(包括“http:”方案组件和主机名以及可选的端口号)例如 <code>&quot;http://example.com/&quot;</code> '''' 一个“权威”(即 主机名,可选地包括端口号)例如 <code>&quot;example.com&quot;</code> <code>&quot;example.com:8080&quot;</code>(后一个示例包括端口号)。 权限(如果存在)不得包含“userinfo”组件 - 例如 <code>&quot;joe:password@example.com&quot;</code> 不正确。
component and the hostname and optionally the port number)
 
e.g. <code>&quot;http://example.com/&quot;</code> ''or'' an &quot;authority&quot; (i.e. the hostname,
 
optionally including the port number) e.g. <code>&quot;example.com&quot;</code> or <code>&quot;example.com:8080&quot;</code>
 
(the latter example includes a port number). The authority, if present, must
 
NOT contain the &quot;userinfo&quot; component - for example <code>&quot;joe:password@example.com&quot;</code> is
 
not correct.
 
  
  
第669行: 第528行:
 
<div id="proxies" class="section">
 
<div id="proxies" class="section">
  
== Proxies ==
+
== 代理 ==
  
'''urllib''' will auto-detect your proxy settings and use those. This is through
+
'''urllib''' 将自动检测您的代理设置并使用这些设置。 这是通过 <code>ProxyHandler</code>,当检测到代理设置时,它是正常处理程序链的一部分。 通常这是一件好事,但有时它可能没有帮助 [[#id12|5]]。 一种方法是设置我们自己的 <code>ProxyHandler</code>,没有定义代理。 这是使用设置 [http://www.voidspace.org.uk/python/articles/authentication.shtml 基本身份验证] 处理程序的类似步骤完成的:
the <code>ProxyHandler</code>, which is part of the normal handler chain when a proxy
 
setting is detected. Normally that's a good thing, but there are occasions
 
when it may not be helpful [[#id12|5]]. One way to do this is to setup our own
 
<code>ProxyHandler</code>, with no proxies defined. This is done using similar steps to
 
setting up a [http://www.voidspace.org.uk/python/articles/authentication.shtml Basic Authentication] handler:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第682行: 第536行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; proxy_support = urllib.request.ProxyHandler({})
+
<syntaxhighlight lang="python3">>>> proxy_support = urllib.request.ProxyHandler({})
&gt;&gt;&gt; opener = urllib.request.build_opener(proxy_support)
+
>>> opener = urllib.request.build_opener(proxy_support)
&gt;&gt;&gt; urllib.request.install_opener(opener)</pre>
+
>>> urllib.request.install_opener(opener)</syntaxhighlight>
  
 
</div>
 
</div>
第691行: 第545行:
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
Currently <code>urllib.request</code> ''does not'' support fetching of <code>https</code> locations
+
目前 <code>urllib.request</code> '''' 支持通过代理获取 <code>https</code> 位置。 但是,这可以通过扩展 urllib.request 来启用,如配方 [[#id13|6]] 中所示。
through a proxy. However, this can be enabled by extending urllib.request as
 
shown in the recipe [[#id13|6]].
 
  
  
第701行: 第553行:
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
<code>HTTP_PROXY</code> will be ignored if a variable <code>REQUEST_METHOD</code> is set; see
+
如果设置了变量 <code>REQUEST_METHOD</code>,则 <code>HTTP_PROXY</code> 将被忽略; 请参阅有关 [[../../library/urllib.request#urllib.request|getproxies()]] 的文档。
the documentation on [[../../library/urllib.request#urllib.request|<code>getproxies()</code>]].
 
  
  
第712行: 第563行:
 
<div id="sockets-and-layers" class="section">
 
<div id="sockets-and-layers" class="section">
  
== Sockets and Layers ==
+
== 套接字和层 ==
  
The Python support for fetching resources from the web is layered. urllib uses
+
从 Web 获取资源的 Python 支持是分层的。 urllib 使用 [[../../library/http.client#module-http|http.client]] 库,后者又使用套接字库。
the [[../../library/http.client#module-http|<code>http.client</code>]] library, which in turn uses the socket library.
 
  
As of Python 2.3 you can specify how long a socket should wait for a response
+
Python 2.3 开始,您可以指定套接字在超时之前应等待响应的时间。 这在必须获取网页的应用程序中很有用。 默认情况下,套接字模块具有 ''无超时'' 并且可以挂起。 目前,套接字超时未在 http.client urllib.request 级别公开。 但是,您可以使用以下命令为所有套接字全局设置默认超时
before timing out. This can be useful in applications which have to fetch web
 
pages. By default the socket module has ''no timeout'' and can hang. Currently,
 
the socket timeout is not exposed at the http.client or urllib.request levels.
 
However, you can set the default timeout globally for all sockets using
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第727行: 第573行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>import socket
+
<syntaxhighlight lang="python3">import socket
 
import urllib.request
 
import urllib.request
  
第737行: 第583行:
 
# we have set in the socket module
 
# we have set in the socket module
 
req = urllib.request.Request('http://www.voidspace.org.uk')
 
req = urllib.request.Request('http://www.voidspace.org.uk')
response = urllib.request.urlopen(req)</pre>
+
response = urllib.request.urlopen(req)</syntaxhighlight>
  
 
</div>
 
</div>
第744行: 第590行:
  
 
</div>
 
</div>
 +
 +
-----
 +
 
<div id="footnotes" class="section">
 
<div id="footnotes" class="section">
  
== Footnotes ==
+
== 脚注 ==
  
This document was reviewed and revised by John Lee.
+
本文档由 John Lee 审阅和修订。
  
 
; <span class="brackets">[[#id1|1]]</span>
 
; <span class="brackets">[[#id1|1]]</span>
: Google for example.
+
: 以谷歌为例。
 
; <span class="brackets">[[#id2|2]]</span>
 
; <span class="brackets">[[#id2|2]]</span>
: Browser sniffing is a very bad practice for website design - building sites using web standards is much more sensible. Unfortunately a lot of sites still send different versions to different browsers.
+
: 浏览器嗅探对于网站设计来说是一种非常糟糕的做法 - 使用网络标准构建网站更为明智。 不幸的是,许多网站仍然向不同的浏览器发送不同的版本。
 
; <span class="brackets">[[#id3|3]]</span>
 
; <span class="brackets">[[#id3|3]]</span>
: The user agent for MSIE 6 is '''Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)'''
+
: MSIE 6 的用户代理是 '''Mozilla/4.0(兼容;MSIE 6.0;Windows NT 5.1;SV1;.NET CLR 1.1.4322)'''
 
; <span class="brackets">[[#id4|4]]</span>
 
; <span class="brackets">[[#id4|4]]</span>
: For details of more HTTP request headers, see [http://jkorpela.fi/http.html Quick Reference to HTTP Headers].
+
: 更多 HTTP 请求头的详细信息,请参见 [http://jkorpela.fi/http.html HTTP 头快速参考]
 
; <span class="brackets">[[#id6|5]]</span>
 
; <span class="brackets">[[#id6|5]]</span>
: In my case I have to use a proxy to access the internet at work. If you attempt to fetch ''localhost'' URLs through this proxy it blocks them. IE is set to use the proxy, which urllib picks up on. In order to test scripts with a localhost server, I have to prevent urllib from using the proxy.
+
: 就我而言,我必须使用代理才能在工作中访问互联网。 如果您尝试通过此代理获取 ''localhost'' URL,它会阻止它们。 IE 设置为使用 urllib 接收的代理。 为了使用本地主机服务器测试脚本,我必须阻止 urllib 使用代理。
 
; <span class="brackets">[[#id7|6]]</span>
 
; <span class="brackets">[[#id7|6]]</span>
: urllib opener for SSL proxy (CONNECT method): [https://code.activestate.com/recipes/456195/ ASPN Cookbook Recipe].
+
: SSL 代理的 urllib 开启器(CONNECT 方法):[https://code.activestate.com/recipes/456195/ ASPN Cookbook Recipe]
  
  
 
</div>
 
</div>
 +
 +
</div>
 +
<div class="clearer">
 +
 +
  
 
</div>
 
</div>
  
[[Category:Python 3.9 中文文档]]
+
[[Category:Python 3.9 文档]]

2021年10月31日 (日) 04:51的最新版本

HOWTO 使用 urllib 包获取 Internet 资源

作者
迈克尔·福特

笔记

本 HOWTO 的早期修订版有法语翻译,可从 urllib2 - Le Manuel manquant 获得。


简介

相关文章

您可能还会发现以下有关使用 Python 获取 Web 资源的文章很有用:

  • 基本认证

    关于 基本身份验证 的教程,以及 Python 中的示例。

urllib.request 是一个用于获取 URL(统一资源定位器)的 Python 模块。 它以 urlopen 函数的形式提供了一个非常简单的界面。 这能够使用各种不同的协议获取 URL。 它还提供了一个稍微复杂一些的界面来处理常见情况——比如基本身份验证、cookies、代理等等。 这些由称为处理程序和开启程序的对象提供。

urllib.request 支持获取许多“URL 方案”的 URL(由 URL 中 ":" 之前的字符串标识 - 例如 "ftp""ftp://python.org/%22 的 URL 方案)使用它们的相关的网络协议(例如 FTP、HTTP)。 本教程重点介绍最常见的情况,HTTP。

对于简单的情况 urlopen 非常容易使用。 但是,一旦您在打开 HTTP URL 时遇到错误或非平凡情况,您就需要对超文本传输协议有所了解。 HTTP 最全面、最权威的参考文献是 RFC 2616。 这是一份技术文档,并非旨在易于阅读。 本 HOWTO 旨在说明如何使用 urllib,其中包含有关 HTTP 的足够详细信息以帮助您完成。 它不是要替换 urllib.request 文档,而是对它们的补充。


获取网址

最简单的使用 urllib.request 的方法如下:

import urllib.request
with urllib.request.urlopen('http://python.org/') as response:
   html = response.read()

如果您希望通过 URL 检索资源并将其存储在临时位置,您可以通过 shutil.copyfileobj()tempfile.NamedTemporaryFile() 函数来实现:

import shutil
import tempfile
import urllib.request

with urllib.request.urlopen('http://python.org/') as response:
    with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
        shutil.copyfileobj(response, tmp_file)

with open(tmp_file.name) as html:
    pass

urllib 的许多用途就是这么简单(请注意,我们可以使用以 'ftp:'、'file:' 等开头的 URL,而不是 'http:' URL)。 但是,本教程的目的是解释更复杂的情况,重点是 HTTP。

HTTP 基于请求和响应 - 客户端发出请求,服务器发送响应。 urllib.request 用 Request 对象反映了这一点,该对象代表您正在发出的 HTTP 请求。 以最简单的形式创建一个 Request 对象,该对象指定要获取的 URL。 使用此请求对象调用 urlopen 会返回所请求 URL 的响应对象。 这个响应是一个类似文件的对象,这意味着你可以在响应上调用 .read()

import urllib.request

req = urllib.request.Request('http://www.voidspace.org.uk')
with urllib.request.urlopen(req) as response:
   the_page = response.read()

请注意, urllib.request 使用相同的 Request 接口来处理所有 URL 方案。 例如,您可以像这样发出 FTP 请求:

req = urllib.request.Request('ftp://example.com/')

在 HTTP 的情况下,Request 对象允许您做两件额外的事情:首先,您可以传递要发送到服务器的数据。 其次,您可以将额外信息(“元数据”)关于 数据或关于请求本身的信息传递给服务器——这些信息作为 HTTP“标头”发送。 让我们依次看看这些。

数据

有时您想将数据发送到 URL(通常 URL 将引用 CGI(通用网关接口)脚本或其他 Web 应用程序)。 对于 HTTP,这通常使用所谓的 POST 请求来完成。 当您提交在 Web 上填写的 HTML 表单时,浏览器通常会执行此操作。 并非所有 POST 都必须来自表单:您可以使用 POST 将任意数据传输到您自己的应用程序。 在 HTML 表单的常见情况下,数据需要以标准方式进行编码,然后作为 data 参数传递给 Request 对象。 编码是使用 urllib.parse 库中的函数完成的。

import urllib.parse
import urllib.request

url = 'http://www.someserver.com/cgi-bin/register.cgi'
values = {'name' : 'Michael Foord',
          'location' : 'Northampton',
          'language' : 'Python' }

data = urllib.parse.urlencode(values)
data = data.encode('ascii') # data should be bytes
req = urllib.request.Request(url, data)
with urllib.request.urlopen(req) as response:
   the_page = response.read()

请注意,有时需要其他编码(例如 用于从 HTML 表单上传文件 - 有关更多详细信息,请参阅 HTML 规范,表单提交 )。

如果不传递 data 参数,urllib 将使用 GET 请求。 GET 和 POST 请求的不同之处在于 POST 请求通常具有“副作用”:它们以某种方式改变系统的状态(例如,通过向网站下订单以交付一百磅罐装垃圾邮件到你家门口)。 尽管 HTTP 标准明确指出 POST 旨在 总是 引起副作用,而 GET 请求 从不 引起副作用,但没有什么能阻止 GET 请求产生副作用,也没有 POST 请求没有副作用。 数据也可以通过在 URL 本身中编码来在 HTTP GET 请求中传递。

这是按如下方式完成的:

>>> import urllib.request
>>> import urllib.parse
>>> data = {}
>>> data['name'] = 'Somebody Here'
>>> data['location'] = 'Northampton'
>>> data['language'] = 'Python'
>>> url_values = urllib.parse.urlencode(data)
>>> print(url_values)  # The order may differ from below.  
name=Somebody+Here&language=Python&location=Northampton
>>> url = 'http://www.example.com/example.cgi'
>>> full_url = url + '?' + url_values
>>> data = urllib.request.urlopen(full_url)

请注意,完整的 URL 是通过向 URL 添加 ? 后跟编码值来创建的。


标题

我们将在此处讨论一个特定的 HTTP 标头,以说明如何向 HTTP 请求添加标头。

一些网站1不喜欢被程序浏览,或者向不同浏览器发送不同版本2。 默认情况下,urllib 将自身标识为 Python-urllib/x.y(其中 xy 是 Python 版本的主要和次要版本号,例如 Python-urllib/2.5),这可能会混淆站点,或者根本不起作用。 浏览器识别自己的方式是通过 User-Agent 标头 3。 当你创建一个 Request 对象时,你可以传入一个标头字典。 以下示例发出与上述相同的请求,但将自身标识为 Internet Explorer 4 的版本。

import urllib.parse
import urllib.request

url = 'http://www.someserver.com/cgi-bin/register.cgi'
user_agent = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64)'
values = {'name': 'Michael Foord',
          'location': 'Northampton',
          'language': 'Python' }
headers = {'User-Agent': user_agent}

data = urllib.parse.urlencode(values)
data = data.encode('ascii')
req = urllib.request.Request(url, data, headers)
with urllib.request.urlopen(req) as response:
   the_page = response.read()

响应也有两个有用的方法。 请参阅关于 info 和 geturl 的部分,在我们了解出现问题时会发生什么之后。


处理异常

urlopen 在无法处理响应时引发 URLError(尽管与 Python API 一样,内置异常如 ValueErrorTypeError 等. 也可以提高)。

HTTPErrorURLError 的子类,在 HTTP URL 的特定情况下提出。

异常类是从 urllib.error 模块导出的。

网址错误

通常,由于没有网络连接(没有到指定服务器的路由)或指定的服务器不存在,会引发 URLError。 在这种情况下,引发的异常将具有“原因”属性,它是一个包含错误代码和文本错误消息的元组。

例如

>>> req = urllib.request.Request('http://www.pretend_server.org')
>>> try: urllib.request.urlopen(req)
... except urllib.error.URLError as e:
...     print(e.reason)      
...
(4, 'getaddrinfo failed')

HTTP错误

来自服务器的每个 HTTP 响应都包含一个数字“状态代码”。 有时状态码表示服务器无法满足请求。 默认处理程序将为您处理其中一些响应(例如,如果响应是请求客户端从不同 URL 获取文档的“重定向”,则 urllib 将为您处理)。 对于无法处理的那些,urlopen 将引发 HTTPError。 典型的错误包括“404”(未找到页面)、“403”(请求被禁止)和“401”(需要身份验证)。

有关所有 HTTP 错误代码的参考,请参阅 RFC 2616 的第 10 节。

引发的 HTTPError 实例将具有一个整数 'code' 属性,它对应于服务器发送的错误。

错误代码

因为默认处理程序处理重定向(300 范围内的代码),并且 100-299 范围内的代码表示成功,所以您通常只会看到 400-599 范围内的错误代码。

http.server.BaseHTTPRequestHandler.responses 是一个有用的响应代码字典,其中显示了 RFC 2616 使用的所有响应代码。 为方便起见,此处转载词典

# Table mapping response codes to messages; entries have the
# form {code: (shortmessage, longmessage)}.
responses = {
    100: ('Continue', 'Request received, please continue'),
    101: ('Switching Protocols',
          'Switching to new protocol; obey Upgrade header'),

    200: ('OK', 'Request fulfilled, document follows'),
    201: ('Created', 'Document created, URL follows'),
    202: ('Accepted',
          'Request accepted, processing continues off-line'),
    203: ('Non-Authoritative Information', 'Request fulfilled from cache'),
    204: ('No Content', 'Request fulfilled, nothing follows'),
    205: ('Reset Content', 'Clear input form for further input.'),
    206: ('Partial Content', 'Partial content follows.'),

    300: ('Multiple Choices',
          'Object has several resources -- see URI list'),
    301: ('Moved Permanently', 'Object moved permanently -- see URI list'),
    302: ('Found', 'Object moved temporarily -- see URI list'),
    303: ('See Other', 'Object moved -- see Method and URL list'),
    304: ('Not Modified',
          'Document has not changed since given time'),
    305: ('Use Proxy',
          'You must use proxy specified in Location to access this '
          'resource.'),
    307: ('Temporary Redirect',
          'Object moved temporarily -- see URI list'),

    400: ('Bad Request',
          'Bad request syntax or unsupported method'),
    401: ('Unauthorized',
          'No permission -- see authorization schemes'),
    402: ('Payment Required',
          'No payment -- see charging schemes'),
    403: ('Forbidden',
          'Request forbidden -- authorization will not help'),
    404: ('Not Found', 'Nothing matches the given URI'),
    405: ('Method Not Allowed',
          'Specified method is invalid for this server.'),
    406: ('Not Acceptable', 'URI not available in preferred format.'),
    407: ('Proxy Authentication Required', 'You must authenticate with '
          'this proxy before proceeding.'),
    408: ('Request Timeout', 'Request timed out; try again later.'),
    409: ('Conflict', 'Request conflict.'),
    410: ('Gone',
          'URI no longer exists and has been permanently removed.'),
    411: ('Length Required', 'Client must specify Content-Length.'),
    412: ('Precondition Failed', 'Precondition in headers is false.'),
    413: ('Request Entity Too Large', 'Entity is too large.'),
    414: ('Request-URI Too Long', 'URI is too long.'),
    415: ('Unsupported Media Type', 'Entity body in unsupported format.'),
    416: ('Requested Range Not Satisfiable',
          'Cannot satisfy request range.'),
    417: ('Expectation Failed',
          'Expect condition could not be satisfied.'),

    500: ('Internal Server Error', 'Server got itself in trouble'),
    501: ('Not Implemented',
          'Server does not support this operation'),
    502: ('Bad Gateway', 'Invalid responses from another server/proxy.'),
    503: ('Service Unavailable',
          'The server cannot process the request due to a high load'),
    504: ('Gateway Timeout',
          'The gateway server did not receive a timely response'),
    505: ('HTTP Version Not Supported', 'Cannot fulfill request.'),
    }

当出现错误时,服务器通过返回 HTTP 错误代码 错误页面进行响应。 您可以使用 HTTPError 实例作为返回页面上的响应。 这意味着除了 code 属性外,它还具有 urllib.response 模块返回的 read、geturl 和 info 方法:

>>> req = urllib.request.Request('http://www.python.org/fish.html')
>>> try:
...     urllib.request.urlopen(req)
... except urllib.error.HTTPError as e:
...     print(e.code)
...     print(e.read())  
...
404
b'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n\n\n<html
  ...
  <title>Page Not Found</title>\n
  ...

包起来

因此,如果您想为 HTTPError URLError 做好准备,有两种基本方法。 我更喜欢第二种方法。

1号

from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError
req = Request(someurl)
try:
    response = urlopen(req)
except HTTPError as e:
    print('The server couldn\'t fulfill the request.')
    print('Error code: ', e.code)
except URLError as e:
    print('We failed to reach a server.')
    print('Reason: ', e.reason)
else:
    # everything is fine

笔记

except HTTPError 必须在前,否则except URLError赶上HTTPError


2号

from urllib.request import Request, urlopen
from urllib.error import URLError
req = Request(someurl)
try:
    response = urlopen(req)
except URLError as e:
    if hasattr(e, 'reason'):
        print('We failed to reach a server.')
        print('Reason: ', e.reason)
    elif hasattr(e, 'code'):
        print('The server couldn\'t fulfill the request.')
        print('Error code: ', e.code)
else:
    # everything is fine

信息和获取网址

urlopen(或 HTTPError 实例)返回的响应有两个有用的方法 info()geturl() 并在模块 urllib.response 中定义..

geturl - 这将返回所获取页面的真实 URL。 这很有用,因为 urlopen(或使用的 opener 对象)可能跟随重定向。 获取的页面的 URL 可能与请求的 URL 不同。

info - 这将返回一个类似字典的对象,描述所获取的页面,特别是服务器发送的标头。 它目前是一个 http.client.HTTPMessage 实例。

典型的标头包括“Content-length”、“Content-type”等。 请参阅 HTTP 标头快速参考 以获取有用的 HTTP 标头列表及其含义和用途的简要说明。


开瓶器和处理程序

当你获取一个 URL 时,你使用了一个 opener(一个可能令人困惑的名称 urllib.request.OpenerDirector 的实例)。 通常我们一直使用默认的开启器 - 通过 urlopen - 但您可以创建自定义开启器。 开启者使用处理程序。 所有的“繁重工作”都由搬运工完成。 每个处理程序都知道如何打开特定 URL 方案(http、ftp 等)的 URL,或者如何处理 URL 打开的一个方面,例如 HTTP 重定向或 HTTP cookie。

如果您想获取安装了特定处理程序的 URL,您将需要创建 opener,例如获取处理 cookie 的 opener,或获取不处理重定向的 opener。

要创建开启器,请实例化 OpenerDirector,然后重复调用 .add_handler(some_handler_instance)

或者,您可以使用 build_opener,这是一个使用单个函数调用创建 opener 对象的便捷函数。 build_opener 默认添加多个处理程序,但提供了一种快速添加和/或覆盖默认处理程序的方法。

您可能想要的其他类型的处理程序可以处理代理、身份验证和其他常见但稍微特殊的情况。

install_opener 可用于使 opener 对象成为(全局)默认开启器。 这意味着对 urlopen 的调用将使用您安装的开启程序。

Opener 对象有一个 open 方法,可以像 urlopen 函数一样直接调用它来获取 url:不需要调用 install_opener,除非是为了方便.


基本认证

为了说明创建和安装处理程序,我们将使用 HTTPBasicAuthHandler。 有关此主题的更详细讨论 - 包括对基本身份验证如何工作的解释 - 请参阅 基本身份验证教程

当需要身份验证时,服务器会发送一个请求身份验证的标头(以及 401 错误代码)。 这指定了身份验证方案和“领域”。 标题看起来像:WWW-Authenticate: SCHEME realm="REALM"

例如

WWW-Authenticate: Basic realm="cPanel Users"

然后,客户端应使用作为请求标头包含的领域的适当名称和密码重试请求。 这是“基本身份验证”。 为了简化这个过程,我们可以创建一个 HTTPBasicAuthHandler 的实例和一个开启器来使用这个处理程序。

HTTPBasicAuthHandler 使用一个称为密码管理器的对象来处理 URL 和领域到密码和用户名的映射。 如果您知道领域是什么(来自服务器发送的身份验证标头),那么您可以使用 HTTPPasswordMgr。 通常人们并不关心领域是什么。 在这种情况下,使用 HTTPPasswordMgrWithDefaultRealm 会很方便。 这允许您为 URL 指定默认用户名和密码。 这将在您没有为特定领域提供替代组合的情况下提供。 我们通过提供 None 作为 add_password 方法的领域参数来表明这一点。

顶级 URL 是第一个需要身份验证的 URL。 比传递给 .add_password() 的 URL“更深”的 URL 也将匹配。

# create a password manager
password_mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()

# Add the username and password.
# If we knew the realm, we could use it instead of None.
top_level_url = "http://example.com/foo/"
password_mgr.add_password(None, top_level_url, username, password)

handler = urllib.request.HTTPBasicAuthHandler(password_mgr)

# create "opener" (OpenerDirector instance)
opener = urllib.request.build_opener(handler)

# use the opener to fetch a URL
opener.open(a_url)

# Install the opener.
# Now all calls to urllib.request.urlopen use our opener.
urllib.request.install_opener(opener)

笔记

在上面的示例中,我们仅将 HTTPBasicAuthHandler 提供给 build_opener。 默认情况下,开启程序具有正常情况下的处理程序 - ProxyHandler(如果设置了代理设置,例如 http_proxy 环境变量)、UnknownHandler、[ X162X]、HTTPDefaultErrorHandlerHTTPRedirectHandlerFTPHandlerFileHandlerDataHandlerHTTPErrorProcessor.


top_level_url 实际上是 或者 一个完整的 URL(包括“http:”方案组件和主机名以及可选的端口号)例如 "http://example.com/%22 一个“权威”(即 主机名,可选地包括端口号)例如 "example.com""example.com:8080"(后一个示例包括端口号)。 权限(如果存在)不得包含“userinfo”组件 - 例如 "joe:password@example.com" 不正确。


代理

urllib 将自动检测您的代理设置并使用这些设置。 这是通过 ProxyHandler,当检测到代理设置时,它是正常处理程序链的一部分。 通常这是一件好事,但有时它可能没有帮助 5。 一种方法是设置我们自己的 ProxyHandler,没有定义代理。 这是使用设置 基本身份验证 处理程序的类似步骤完成的:

>>> proxy_support = urllib.request.ProxyHandler({})
>>> opener = urllib.request.build_opener(proxy_support)
>>> urllib.request.install_opener(opener)

笔记

目前 urllib.request 支持通过代理获取 https 位置。 但是,这可以通过扩展 urllib.request 来启用,如配方 6 中所示。


笔记

如果设置了变量 REQUEST_METHOD,则 HTTP_PROXY 将被忽略; 请参阅有关 getproxies() 的文档。


套接字和层

从 Web 获取资源的 Python 支持是分层的。 urllib 使用 http.client 库,后者又使用套接字库。

从 Python 2.3 开始,您可以指定套接字在超时之前应等待响应的时间。 这在必须获取网页的应用程序中很有用。 默认情况下,套接字模块具有 无超时 并且可以挂起。 目前,套接字超时未在 http.client 或 urllib.request 级别公开。 但是,您可以使用以下命令为所有套接字全局设置默认超时

import socket
import urllib.request

# timeout in seconds
timeout = 10
socket.setdefaulttimeout(timeout)

# this call to urllib.request.urlopen now uses the default timeout
# we have set in the socket module
req = urllib.request.Request('http://www.voidspace.org.uk')
response = urllib.request.urlopen(req)

脚注

本文档由 John Lee 审阅和修订。

1
以谷歌为例。
2
浏览器嗅探对于网站设计来说是一种非常糟糕的做法 - 使用网络标准构建网站更为明智。 不幸的是,许多网站仍然向不同的浏览器发送不同的版本。
3
MSIE 6 的用户代理是 Mozilla/4.0(兼容;MSIE 6.0;Windows NT 5.1;SV1;.NET CLR 1.1.4322)
4
更多 HTTP 请求头的详细信息,请参见 HTTP 头快速参考
5
就我而言,我必须使用代理才能在工作中访问互联网。 如果您尝试通过此代理获取 localhost URL,它会阻止它们。 IE 设置为使用 urllib 接收的代理。 为了使用本地主机服务器测试脚本,我必须阻止 urllib 使用代理。
6
SSL 代理的 urllib 开启器(CONNECT 方法):ASPN Cookbook Recipe