Python 的 urllib.request 用于 HTTP 请求

2023-10-12

如果您需要使用 Python 发出 HTTP 请求,那么您可能会发现自己被引导到了精彩的地方要求图书馆。虽然它是一个很棒的库,但您可能已经注意到它不是 Python 的内置部分。如果您出于某种原因喜欢限制依赖项并坚持使用标准库 Python,那么您可以使用urllib.request!

在本教程中,您将:

  • 了解如何制作基本HTTP 请求urllib.request
  • 深入了解细节HTTP消息如何urllib.request代表它
  • 了解如何应对字符编码HTTP 消息数
  • 探索一些常见错误使用时urllib.request并学习如何解决它们
  • 将你的脚趾浸入世界经过身份验证的请求urllib.request
  • 了解为什么两者urllibrequests图书馆存在并且何时使用其中之一

如果您听说过 HTTP 请求,包括GET邮政,那么您可能已经准备好学习本教程了。另外,您应该已经使用过 Python读取和写入文件,理想情况下有一个上下文管理器, 至少一次。

最终,您会发现提出请求并不一定是一次令人沮丧的经历,尽管它确实往往享有这样的声誉。您可能遇到的许多问题都是由于互联网这一奇妙事物固有的复杂性造成的。好消息是urllib.request模块可以帮助揭开这种复杂性的大部分神秘面纱。

了解更多: 单击此处加入 Real Python Newsletter 上 290,000 多名 Python 开发人员的行列并获取新的 Python 教程和新闻,让您成为更高效的 Pythonista。

基本 HTTP GET 请求urllib.request

在深入了解 HTTP 请求是什么及其工作原理之前,您将通过向示例网址。您还将向模拟发出 GET 请求休息API对于一些JSON数据。如果您想了解 POST 请求,您将了解它们稍后在教程中,一旦你有了更多的了解urllib.request.

谨防:根据您的具体设置,您可能会发现其中一些示例不起作用。如果是这样,请跳至常见部分urllib.request错误用于故障排除。

如果您遇到此处未涵盖的问题,请务必在下面使用精确且可重现的示例进行评论。

首先,您需要向www.example.com,服务器将返回一条 HTTP 消息。确保您使用的是 Python 3 或更高版本,然后使用urlopen()函数来自urllib.request:

>>>
>>> from urllib.request import urlopen
>>> with urlopen("https://www.example.com") as response:
...     body = response.read()
...
>>> body[:15]
b'<!doctype html>'

在此示例中,您导入urlopen()urllib.request。使用上下文管理器 with,您发出请求并收到响应urlopen()。然后,您读取响应正文并关闭响应对象。这样,您就可以显示正文的前 15 个位置,并注意到它看起来像一个 HTML 文档。

你在这!您已成功提出请求,并且收到了回复。通过检查内容,您可以判断它很可能是 HTML 文档。请注意,正文的打印输出前面是b。这表明一个字节文字,您可能需要对其进行解码。在本教程的后面,您将学习如何将字节转换为细绳,将它们写入文件,或者将它们解析为字典.

如果您想要调用 REST API 来获取 JSON 数据,该过程仅略有不同。在以下示例中,您将请求{JSON} 占位符对于一些虚假的待办事项数据:

>>>
>>> from urllib.request import urlopen
>>> import json
>>> url = "https://jsonplaceholder.typicode.com/todos/1"
>>> with urlopen(url) as response:
...     body = response.read()
...
>>> todo_item = json.loads(body)
>>> todo_item
{'userId': 1, 'id': 1, 'title': 'delectus aut autem', 'completed': False}

在此示例中,您所做的与上一个示例中的操作几乎相同。但在这一个中,你导入urllib.request and json, 使用json.loads()功能与body将返回的 JSON 字节解码并解析为Python字典。瞧!

如果您足够幸运能够无错误地使用端点,例如这些示例中的那些,那么上面的内容可能就是您所需要的urllib.request。话又说回来,你可能会发现这还不够。

现在,在做一些事情之前urllib.request故障排除时,您将首先了解 HTTP 消息的底层结构并了解如何urllib.request处理它们。这种理解将为解决许多不同类型的问题提供坚实的基础。

HTTP 消息的具体细节

了解您在使用过程中可能遇到的一些问题urllib.request,您需要检查响应是如何表示的urllib.request。为此,您将受益于对什么是一个高层次的概述。HTTP消息是,这就是您将在本节中得到的内容。

在进行高级概述之前,先简要介绍一下参考来源。如果您想深入了解技术领域,互联网工程任务组 (IETF)有一套广泛的征求意见 (RFC)文件。这些文档最终成为 HTTP 消息等内容的实际规范。RFC 7230,第 1 部分:消息语法和路由例如,都是关于 HTTP 消息的。

如果您正在寻找一些比 RFC 更容易理解的参考材料,那么Mozilla 开发者网络 (MDN)有大量的参考文章。例如,他们的文章HTTP消息虽然仍然是技术性的,但更容易理解。

现在您已经了解了这些重要的参考信息来源,在下一节中您将获得对初学者友好的 HTTP 消息概述。

了解什么是 HTTP 消息

简而言之,HTTP 消息可以理解为文本,以流的形式传输字节,结构遵循 RFC 7230 指定的准则。解码的 HTTP 消息可以简单到两行:

GET / HTTP/1.1
Host: www.google.com

这指定了一个GET根请求(/) 使用HTTP/1.1协议。唯一的标头需要的是主机,www.google.com。目标服务器有足够的信息来利用该信息做出响应。

响应的结构与请求类似。 HTTP 消息有两个主要部分,元数据身体。在上面的请求示例中,消息都是没有正文的元数据。另一方面,响应确实有两个部分:

HTTP/1.1 200 OK
Content-Type: text/html; charset=ISO-8859-1
Server: gws
(... other headers ...)

<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage"
...

响应以状态行指定 HTTP 协议HTTP/1.1和状态200 OK。状态行之后,可以得到很多键值对,比如Server: gws,代表所有响应标头。这是响应的元数据。

元数据之后有一个空行,用作标题和正文之间的分隔符。空行后面的所有内容都构成了正文。这是您使用时读取的部分urllib.request.

笔记:空行在技术上通常被称为换行符。 HTTP 消息中的换行符必须是 Windows 样式回车 (\r)与一个行尾 (\n)。在类Unix系统中,换行符通常只是一个行结尾 (\n).

您可以假设所有 HTTP 消息都遵循这些规范,但有些消息可能会违反这些规则或遵循较旧的规范。它是异常地不过,这很少会引起任何问题。所以,请记住它,以防遇到奇怪的错误!

在下一节中,您将了解如何urllib.request处理原始 HTTP 消息。

了解如何做urllib.request代表一条 HTTP 消息

使用时将与之交互的 HTTP 消息的主要表示形式urllib.request是个HTTP响应目的。这urllib.request模块本身依赖于低层http模块,您不需要直接与之交互。您最终确实使用了一些数据结构http不过,提供了诸如HTTPResponseHTTPMessage.

笔记:Python 中表示 HTTP 响应和消息的对象的内部命名可能有点令人困惑。您通常只与以下实例交互HTTPResponse,而要求事情的最终结果是在内部处理的。

你可能会认为HTTPMessage是一种基类,其中HTTPResponse继承自,但事实并非如此。HTTPResponse直接继承自io.BufferedIOBase,而HTTPMessage类继承自电子邮件.消息.EmailMessage.

EmailMessage在源代码中定义为包含一堆标头和有效负载的对象,因此它不一定是电子邮件。HTTPResponse只是简单地使用HTTPMessage作为其标头的容器。

但是,如果您谈论的是 HTTP 本身而不是其 Python 实现,那么您将 HTTP 响应视为一种 HTTP 消息是正确的。

当您提出请求时urllib.request.urlopen(),你得到一个HTTPResponse对象作为回报。花一些时间探索HTTPResponse对象与打印()目录()查看属于它的所有不同方法和属性:

>>>
>>> from urllib.request import urlopen
>>> from pprint import pprint
>>> with urlopen("https://www.example.com") as response:
...     pprint(dir(response))
...

要显示此代码片段的输出,请单击展开下面的可折叠部分:

['__abstractmethods__',
 '__class__',
 '__del__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__enter__',
 '__eq__',
 '__exit__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__iter__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__next__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '_abc_impl',
 '_checkClosed',
 '_checkReadable',
 '_checkSeekable',
 '_checkWritable',
 '_check_close',
 '_close_conn',
 '_get_chunk_left',
 '_method',
 '_peek_chunked',
 '_read1_chunked',
 '_read_and_discard_trailer',
 '_read_next_chunk_size',
 '_read_status',
 '_readall_chunked',
 '_readinto_chunked',
 '_safe_read',
 '_safe_readinto',
 'begin',
 'chunk_left',
 'chunked',
 'close',
 'closed',
 'code',
 'debuglevel',
 'detach',
 'fileno',
 'flush',
 'fp',
 'getcode',
 'getheader',
 'getheaders',
 'geturl',
 'headers',
 'info',
 'isatty',
 'isclosed',
 'length',
 'msg',
 'peek',
 'read',
 'read1',
 'readable',
 'readinto',
 'readinto1',
 'readline',
 'readlines',
 'reason',
 'seek',
 'seekable',
 'status',
 'tell',
 'truncate',
 'url',
 'version',
 'will_close',
 'writable',
 'write',
 'writelines']

有很多方法和属性,但您最终只会使用其中的一小部分。除了.read(),重要的通常涉及获取有关标头.

检查所有标头的一种方法是访问.headers的属性HTTPResponse目的。这将返回一个HTTPMessage目的。您可以方便地治疗HTTPMessage像字典一样通过调用.items()在其上获取所有标头作为元组:

>>>
>>> with urlopen("https://www.example.com") as response:
...     pass
...
>>> response.headers
<http.client.HTTPMessage object at 0x000001E029D9F4F0>
>>> pprint(response.headers.items())
[('Accept-Ranges', 'bytes'),
 ('Age', '398424'),
 ('Cache-Control', 'max-age=604800'),
 ('Content-Type', 'text/html; charset=UTF-8'),
 ('Date', 'Tue, 25 Jan 2022 12:18:53 GMT'),
 ('Etag', '"3147526947"'),
 ('Expires', 'Tue, 01 Feb 2022 12:18:53 GMT'),
 ('Last-Modified', 'Thu, 17 Oct 2019 07:18:26 GMT'),
 ('Server', 'ECS (nyb/1D16)'),
 ('Vary', 'Accept-Encoding'),
 ('X-Cache', 'HIT'),
 ('Content-Length', '1256'),
 ('Connection', 'close')]

现在您可以访问所有响应标头!您可能不需要大部分信息,但请放心,某些应用程序确实会使用它。例如,您的浏览器可能使用标头来读取响应、设置 cookie 并确定适当的缓存寿命。

有一些方便的方法可以从HTTPResponse对象,因为这是一个非常常见的操作。您可以致电.getheaders()直接在HTTPResponse对象,它将返回与上面完全相同的元组列表。如果您只对一个标题感兴趣,请说Serverheader,那么你可以使用单数.getheader("Server")HTTPResponse或使用方括号 ([]) 语法.headersHTTPMessage:

>>>
>>> response.getheader("Server")
'ECS (nyb/1D16)'
>>> response.headers["Server"]
'ECS (nyb/1D16)'

说实话,您可能不需要像这样直接与标题交互。您最可能需要的信息可能已经有一些内置的帮助方法,但现在您知道了,以防您需要更深入地挖掘!

关闭HTTPResponse

HTTPResponse对象有很多共同点文件对象。这HTTPResponse类继承自IO基类,就像文件对象一样,这意味着您必须注意打开和关闭。

在简单的程序中,如果忘记关闭,您不太可能注意到任何问题HTTPResponse对象。然而,对于更复杂的项目,这可能会显着减慢执行速度并导致难以查明的错误。

问题的出现是因为输入输出(I/O) 流是有限的。每个HTTPResponse要求流在读取时保持畅通。如果您从不关闭流,这最终将阻止打开任何其他流,并且可能会干扰其他程序甚至您的操作系统。

所以,请确保关闭您的HTTPResponse物体!为了方便起见,您可以使用上下文管理器,如示例中所示。您还可以通过显式调用来获得相同的结果.close()在响应对象上:

>>>
>>> from urllib.request import urlopen
>>> response = urlopen("https://www.example.com")
>>> body = response.read()
>>> response.close()

在此示例中,您不使用上下文管理器,而是显式关闭响应流。不过,上面的示例仍然存在问题,因为在调用之前可能会引发异常.close(),防止正确拆卸。要使此调用无条件(正如它应该的那样),您可以使用尝试……除了块与两个else和一个finally条款:

>>>
>>> from urllib.request import urlopen
>>> response = None
>>> try:
...     response = urlopen("https://www.example.com")
... except Exception as ex:
...     print(ex)
... else:
...     body = response.read()
... finally:
...     if response is not None:
...         response.close()

在此示例中,您实现了对.close()通过使用finally块,无论是否引发异常,该块都将始终运行。中的代码finally块首先检查是否response对象存在于is not None,然后关闭它。

也就是说,这正是上下文管理器所做的事情,并且with通常首选语法。不仅是with语法更简洁,更具可读性,但它也可以保护您免受讨厌的遗漏错误的影响。换句话说,它可以更好地防止意外忘记关闭对象:

>>>
>>> from urllib.request import urlopen
>>> with urlopen("https://www.example.com") as response:
...     response.read(50)
...     response.read(50)
...
b'<!doctype html>\n<html>\n<head>\n    <title>Example D'
b'omain</title>\n\n    <meta charset="utf-8" />\n    <m'

在此示例中,您导入urlopen()来自urllib.request模块。您使用with关键字与.urlopen()来分配HTTPResponse变量的对象response。然后,您读取响应的前 50 个字节,然后读取接下来的 50 个字节,所有这些都在with堵塞。最后,您关闭with块,执行请求并运行其块内的代码行。

使用此代码,您可以显示两组,每组五十个字节。这HTTPResponse一旦退出,对象就会关闭with块作用域,这意味着.read()方法只会返回空字节对象:

>>>
>>> import urllib.request
>>> with urllib.request.urlopen("https://www.example.com") as response:
...     response.read(50)
...
b'<!doctype html>\n<html>\n<head>\n    <title>Example D'
>>> response.read(50)
b''

在此示例中,第二个读取 50 个字节的调用超出了with范围。处于外部with块意味着HTTPResponse已关闭,即使您仍然可以访问该变量。如果您尝试读取HTTPResponse当它关闭时,它将返回一个空的字节对象。

另一点需要注意的是,一旦读完回复,就无法重新阅读回复:

>>>
>>> import urllib.request
>>> with urllib.request.urlopen("https://www.example.com") as response:
...     first_read = response.read()
...     second_read = response.read()
...
>>> len(first_read)
1256
>>> len(second_read)
0

此示例表明,一旦您阅读了回复,就无法再次阅读。如果您已完全读取响应,则即使响应未关闭,后续尝试也只会返回一个空字节对象。您必须再次提出请求。

在这方面,响应与文件对象不同,因为对于文件对象,您可以使用以下命令多次读取它:。寻找()方法,其中HTTPResponse不支持。即使关闭响应后,您仍然可以访问标头和其他元数据。

探索文本、八位字节和位

在到目前为止的大多数示例中,您从以下位置读取响应正文HTTPResponse,立即显示结果数据,并注意到它显示为字节对象。这是因为计算机中的文本信息不是以字母的形式存储或传输的,而是以字节的形式存储或传输的!

通过线路发送的原始 HTTP 消息被分解为一系列字节,有时称为八位位组。字节为 8-bit大块。例如,01010101是一个字节。要了解有关二进制、位和字节的更多信息,请查看Python 中的按位运算符.

那么如何用字节来表示字母呢?一个字节有 256 种可能的组合,您可以为每个组合分配一个字母。您可以分配00000001A, 00000010B, 等等。ASCII码字符编码很常见,使用这种类型的系统编码 128 个字符,这对于像英语这样的语言来说已经足够了。这是特别方便的,因为只需一个字节就可以表示所有字符,并且还有剩余空间。

所有标准英文字符(包括大写字母、标点符号和数字)都适合 ASCII。另一方面,日语被认为有大约 5 万个表意字符,所以 128 个字符根本不够用!即使理论上 1 个字节内可用的 256 个字符对于日语来说也不够。因此,为了适应世界上所有的语言,有许多不同的系统来编码字符。

尽管有很多系统,但您可以信赖的一件事是,它们总是会被分解为字节。大多数服务器,如果无法解析MIME类型和字符编码,默认为application/octet-stream,字面意思是字节流。然后谁收到消息就可以计算出字符编码。

处理字符编码

正如您可能已经猜到的那样,问题经常出现,因为存在许许多多不同的潜在字符编码。当今占主导地位的字符编码是UTF-8,这是一个实现统一码。幸运的是,百分之九十八的网页今天都是用UTF-8编码的!

UTF-8 占据主导地位是因为它可以有效地处理数量惊人的字符。它处理 Unicode 定义的所有 1,112,064 个潜在字符,包括中文、日语、阿拉伯语(从右到左的脚本)、俄语和更多字符集,包括表情符号!

UTF-8 仍然高效,因为它使用可变数量的字节来编码字符,这意味着对于许多字符,它只需要一个字节,而对于其他字符,它可能需要最多四个字节。

笔记:要了解有关 Python 编码的更多信息,请查看.

虽然 UTF-8 占主导地位,并且假设 UTF-8 编码通常不会出错,但您仍然会一直遇到不同的编码。好消息是,在使用时您不需要成为编码专家来处理它们urllib.request.

从字节到字符串

当你使用urllib.request.urlopen(),响应的主体是一个字节对象。您可能要做的第一件事是将字节对象转换为字符串。也许你想做一些网页抓取。为此,您需要解码字节。要使用 Python 解码字节,您需要找出的是字符编码用过的。编码,尤其是在涉及字符编码时,通常被称为字符集.

如前所述,98% 的情况下,默认使用 UTF-8 可能是安全的:

>>>
>>> from urllib.request import urlopen
>>> with urlopen("https://www.example.com") as response:
...     body = response.read()
...
>>> decoded_body = body.decode("utf-8")
>>> print(decoded_body[:30])
<!doctype html>
<html>
<head>

在此示例中,您获取从返回的字节对象response.read()并使用 bytes 对象对其进行解码.decode()方法,传入utf-8作为一个论点。当你打印 decoded_body,你可以看到它现在是一个字符串。

也就是说,听天由命并不是一个好的策略。幸运的是,标头是获取字符集信息的好地方:

>>>
>>> from urllib.request import urlopen
>>> with urlopen("https://www.example.com") as response:
...     body = response.read()
...
>>> character_set = response.headers.get_content_charset()
>>> character_set
'utf-8'
>>> decoded_body = body.decode(character_set)
>>> print(decoded_body[:30])
<!doctype html>
<html>
<head>

在此示例中,您调用.get_content_charset().headers的对象response并用它来解码。这是一种解析的便捷方法Content-Type头,以便您可以轻松地将字节解码为文本。

从字节到文件

如果您想将字节解码为文本,现在就可以开始了。但是如果您想将响应正文写入文件怎么办?好吧,你有两个选择:

  1. 将字节直接写入文件
  2. 将字节解码为 Python 字符串,然后将该字符串编码回文件中

第一种方法是最简单的,但第二种方法允许您根据需要更改编码。要更详细地了解文件操作,请查看 Real Python在 Python 中读写文件(指南).

要将字节直接写入文件而无需解码,您需要内置打开()函数,并且您需要确保使用写入二进制模式:

>>>
>>> from urllib.request import urlopen
>>> with urlopen("https://www.example.com") as response:
...     body = response.read()
...
>>> with open("example.html", mode="wb") as html_file:
...     html_file.write(body)
...
1256

使用open()wb模式绕过解码或编码的需要,并将 HTTP 消息正文的字节转储到example.html文件。写入操作后输出的数字表示已写入的字节数。就是这样!您已将字节直接写入文件,而无需进行任何编码或解码。

现在假设您有一个不使用 UTF-8 的 URL,但您想将内容写入使用 UTF-8 的文件。为此,您首先将字节解码为字符串,然后将字符串编码到文件中,并指定字符编码。

谷歌的主页似乎根据您所在的位置使用不同的编码。在欧洲和美国的大部分地区,它使用ISO-8859-1编码:

>>>
>>> from urllib.request import urlopen
>>> with urlopen("https://www.google.com") as response:
...     body = response.read()
...
>>> character_set = response.headers.get_content_charset()
>>> character_set
'ISO-8859-1'
>>> content = body.decode(character_set)
>>> with open("google.html", encoding="utf-8", mode="w") as file:
...     file.write(content)
...
14066

在此代码中,您获取了响应字符集并使用它将字节对象解码为字符串。然后将字符串写入文件,并使用 UTF-8 对其进行编码。

笔记:有趣的是,Google 似乎有多层检查,用于确定提供网页的语言和编码。这意味着您可以指定接受语言标头,这似乎覆盖了您的 IP 位置。用不同的方式尝试一下区域设置标识符看看你能得到什么编码!

写入文件后,您应该能够在浏览器或文本编辑器中打开生成的文件。大多数现代文本处理器可以自动检测字符编码。

如果存在编码错误并且您使用 Python 读取文件,那么您可能会收到错误消息:

>>>
>>> with open("encoding-error.html", mode="r", encoding="utf-8") as file:
...     lines = file.readlines()
...
UnicodeDecodeError:
    'utf-8' codec can't decode byte

Python 显式停止进程并引发异常,但在显示文本的程序中,例如您正在查看此页面的浏览器,您可能会发现臭名昭著的替换字符:

Unicode Replacement Character
A Replacement Character

带白色问号 (�)、正方形 (□) 和矩形 (▯) 的黑色菱形通常用作无法解码的字符的替换。

有时,解码似乎有效,但会导致难以理解的序列,例如æ–‡å—化‘。,这也表明使用了错误的字符集。在日本,他们甚至有一个词来形容由于字符编码问题而出现乱码的文本,莫吉巴克,因为这些问题在互联网时代之初就困扰着他们。

这样,您现在应该能够使用从返回的原始字节写入文件了urlopen()。在下一节中,您将学习如何使用以下命令将字节解析为 Python 字典:json模块。

从字节到字典

为了application/json响应,您经常会发现它们不包含任何编码信息:

>>>
>>> from urllib.request import urlopen
>>> with urlopen("https://httpbin.org/json") as response:
...     body = response.read()
...
>>> character_set = response.headers.get_content_charset()
>>> print(character_set)
None

在此示例中,您使用json的终点httpbin,一项允许您尝试不同类型的请求和响应的服务。这json端点模拟返回 JSON 数据的典型 API。请注意,.get_content_charset()方法在其响应中不返回任何内容。

即使没有字符编码信息,一切也不会丢失。根据RFC 4627,UTF-8 的默认编码是绝对要求application/json规格。这并不是说每个服务器都遵守规则,但一般来说,您可以假设如果传输 JSON,它几乎总是使用 UTF-8 进行编码。

幸运的是,json.loads()在底层解码字节对象,甚至在不同的方面有一些余地编码它可以处理。所以,json.loads()应该能够处理您向其抛出的大多数字节对象,只要它们是有效的 JSON:

>>>
>>> import json
>>> json.loads(body)
{'slideshow': {'author': 'Yours Truly', 'date': 'date of publication', 'slides'
: [{'title': 'Wake up to WonderWidgets!', 'type': 'all'}, {'items': ['Why <em>W
onderWidgets</em> are great', 'Who <em>buys</em> WonderWidgets'], 'title': 'Ove
rview', 'type': 'all'}], 'title': 'Sample Slide Show'}}

如您所见,json模块自动处理解码并生成 Python 字典。几乎所有 API 都以 JSON 形式返回键值信息,尽管您可能会遇到一些与XML。为此,您可能需要研究一下Python 中的 XML 解析器路线图.

这样,您应该对字节和编码有足够的了解,否则会很危险!在下一节中,您将学习如何排除故障并修复使用时可能遇到的一些常见错误urllib.request.

常见的urllib.request烦恼

在这个世界上你可能会遇到各种各样的问题荒野网络,无论您是否使用urllib.request或不。在本节中,您将学习如何处理入门时的一些最常见错误:403错误TLS/SSL 证书错误。不过,在查看这些特定错误之前,您将首先学习如何实现错误处理更一般地使用时urllib.request.

实施错误处理

在将注意力转向特定错误之前,提高代码优雅地处理各种错误的能力将会得到回报。 Web 开发经常受到错误的困扰,您可能会投入大量时间来明智地处理错误。在这里,您将学习如何在使用时处理 HTTP、URL 和超时错误urllib.request.

HTTP 状态代码伴随状态行中的每个响应。如果您可以读取响应中的状态代码,则请求已达到目标。虽然这很好,但只有当响应代码以2。例如,200201代表请求成功。如果状态码是404或者500,例如,出了点问题,并且urllib.request将提出HTTP错误.

有时会发生错误,提供的 URL 不正确,或者由于其他原因无法建立连接。在这些情况下,urllib.request将提出一个网址错误.

最后,有时服务器根本不响应。也许您的网络连接速度很慢,服务器已关闭,或者服务器被编程为忽略特定请求。为了解决这个问题,你可以通过timeout论证urlopen()提出一个超时错误经过一定时间后。

处理这些异常的第一步是捕获它们。您可以捕获其中产生的错误urlopen()与一个tryexcept块,利用HTTPError, URLError, 和TimeoutError课程:

# request.py

from urllib.error import HTTPError, URLError
from urllib.request import urlopen

def make_request(url):
    try:
        with urlopen(url, timeout=10) as response:
            print(response.status)
            return response.read(), response
    except HTTPError as error:
        print(error.status, error.reason)
    except URLError as error:
        print(error.reason)
    except TimeoutError:
        print("Request timed out")

功能make_request()接受一个 URL 字符串作为参数,尝试从该 URL 获取响应urllib.request,并捕获HTTPError发生错误时引发的对象。如果 URL 错误,它会捕获URLError。如果没有任何错误,它只会打印状态并返回包含主体和响应的元组。响应将在之后关闭return.

该函数还调用urlopen()与一个timeout争论,这将导致TimeoutError在指定的秒数后引发。十秒通常是等待响应的良好时间,但与往常一样,这在很大程度上取决于您需要向其发出请求的服务器。

现在您已准备好优雅地处理各种错误,包括但不限于接下来将介绍的错误。

处理403错误

您现在将使用make_request()函数来提出一些请求httpstat.us,这是一个用于测试的模拟服务器。该模拟服务器将返回具有您请求的状态代码的响应。如果您提出请求https://httpstat.us/200,例如,您应该期望200回复。

httpstat.us 等 API 用于确保您的应用程序可以处理可能遇到的所有不同状态代码。 httpbin 也有这个功能,但是 httpstat.us 有更全面的状态代码选择。它甚至还拥有臭名昭著和半官方的 418返回消息的状态码我是一个茶壶!

make_request()您在上一节中编写的函数,以交互模式运行脚本:

$ python3 -i request.py

随着-i标志,该命令将运行脚本交互模式。这意味着它将执行脚本然后打开Python REPL之后,您现在可以调用刚刚定义的函数:

>>>
>>> make_request("https://httpstat.us/200")
200
(b'200 OK', <http.client.HTTPResponse object at 0x0000023D612660B0>)
>>> make_request("https://httpstat.us/403")
403 Forbidden

在这里你尝试了200403httpstat.us 的端点。这200端点按预期执行并返回响应正文和响应对象。这403端点只是打印了错误消息并且没有返回任何内容,这也符合预期。

403状态表示服务器理解该请求但不会满足它。这是您可能遇到的常见错误,尤其是在网页抓取时。在许多情况下,您可以通过传递一个来解决它用户代理标头。

笔记:有两个密切相关的 4xx 代码有时会引起混淆:

  1. 401 未经授权
  2. 403 禁忌

服务器应该返回401如果用户未被识别或登录,并且必须执行某些操作才能获得访问权限,例如登录或注册。

403如果用户已被充分识别但无权访问资源,则应返回状态。例如,如果您登录到社交媒体帐户并尝试查看某人的私人个人资料页面,那么您可能会得到一个403地位。

也就是说,不要完全信任状态代码。错误在复杂的分布式服务中存在并且很常见。有些服务器根本就不是模范公民!

服务器识别发出请求的人或内容的主要方法之一是检查User-Agent标头。发送的原始默认请求urllib.request如下:

GET https://httpstat.us/403 HTTP/1.1
Accept-Encoding: identity
Host: httpstat.us
User-Agent: Python-urllib/3.10
Connection: close

请注意User-Agent被列为Python-urllib/3.10。您可能会发现某些网站会尝试阻止网络抓取工具,这User-Agent是一个致命的赠品。话虽如此,您可以设置自己的User-Agenturllib.request,尽管您需要稍微修改一下您的函数:

 # request.py

 from urllib.error import HTTPError, URLError
-from urllib.request import urlopen
+from urllib.request import urlopen, Request

-def make_request(url):
+def make_request(url, headers=None):
+    request = Request(url, headers=headers or {})
     try:
-        with urlopen(url, timeout=10) as response:
+        with urlopen(request, timeout=10) as response:
             print(response.status)
             return response.read(), response
     except HTTPError as error:
         print(error.status, error.reason)
     except URLError as error:
         print(error.reason)
     except TimeoutError:
         print("Request timed out")

要自定义随请求发送的标头,您首先必须实例化一个要求带有 URL 的对象。此外,您还可以传入关键字参数headers,它接受代表您希望包含的任何标头的标准字典。因此,不要将 URL 字符串直接传递到urlopen(),你通过了这个Request已使用 URL 和标头实例化的对象。

笔记: 在上面的例子中,当Request被实例化后,您需要向其传递标头(如果已定义)。否则,传递一个空白对象,例如{}。你无法通过None,因为这会导致错误。

要使用此改进的功能,请重新启动交互式会话,然后调用make_request()使用表示标题的字典作为参数:

>>>
>>> body, response = make_request(
...     "https://www.httpbin.org/user-agent",
...     {"User-Agent": "Real Python"}
... )
200
>>> body
b'{\n  "user-agent": "Real Python"\n}\n'

在此示例中,您向 httpbin 发出请求。在这里你使用user-agent返回请求的端点User-Agent价值。因为您使用自定义用户代理发出请求Real Python,这就是返回的内容。

不过,有些服务器很严格,只接受来自特定浏览器的请求。幸运的是,可以找到标准User-Agent网络上的字符串,包括通过用户代理数据库。它们只是字符串,因此您需要做的就是复制要模拟的浏览器的用户代理字符串并将其用作User-Agent标头。

修复 SSLCERTIFICATE_VERIFY_FAILED错误

另一个常见错误是由于 Python 无法访问所需的安全证书。要模拟此错误,您可以使用一些已知有错误 SSL 证书的模拟站点,这些站点由badssl.com。您可以向其中之一提出请求,例如superfish.badssl.com,并亲身体验错误:

>>>
>>> from urllib.request import urlopen
>>> urlopen("https://superfish.badssl.com/")
Traceback (most recent call last):
  (...)
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED]
certificate verify failed: unable to get local issuer certificate (_ssl.c:997)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  (...)
urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED]
certificate verify failed: unable to get local issuer certificate (_ssl.c:997)>

在这里,向具有已知错误 SSL 证书的地址发出请求将导致CERTIFICATE_VERIFY_FAILED这是一种URLError.

SSL 代表安全套接字层。这是一种用词不当,因为 SSL 已被弃用,取而代之的是 TLS,传输层安全。有时旧术语仍然存在!这是一种加密网络流量的方法,这样假设的监听者就无法窃听通过线路传输的信息。

如今,大多数网站地址前面都没有http://但是通过https://,与s代表安全的. HTTPS连接必须通过 TLS 加密。urllib.request可以处理 HTTP 和 HTTPS 连接。

HTTPS 的详细信息远远超出了本教程的范围,但您可以将 HTTPS 连接视为涉及两个阶段:握手和信息的传递。握手确保连接安全。有关 Python 和 HTTPS 的更多信息,请查看使用 Python 探索 HTTPS.

为了确保特定服务器是安全的,发出请求的程序依赖于受信任的证书存储。服务器的证书在握手阶段进行验证。 Python 使用操作系统的证书存储。如果 Python 找不到系统的证书存储,或者该存储已过期,那么您将遇到此错误。

笔记:在以前版本的 Python 中,默认行为urllib.request曾是not验证证书,这导致公众号 476默认启用证书验证。默认值更改为Python 3.4.3.

有时,Python 可以访问的证书存储已过期,或者无论出于何种原因,Python 都无法访问它。这很令人沮丧,因为有时您可以从浏览器访问该 URL,浏览器认为该 URL 是安全的,但实际上urllib.request仍然会引发此错误。

您可能会想选择不验证证书,但这会导致您的连接失败不安全感并且绝对是不建议:

>>>
>>> import ssl
>>> from urllib.request import urlopen
>>> unverified_context = ssl._create_unverified_context()
>>> urlopen("https://superfish.badssl.com/", context=unverified_context)
<http.client.HTTPResponse object at 0x00000209CBE8F220>

在这里您导入ssl模块,它允许您创建一个未经验证的上下文。然后您可以将此上下文传递给urlopen()并访问已知的错误 SSL 证书。由于未检查 SSL 证书,因此连接成功。

在采取这些绝望的措施之前,请尝试更新您的操作系统或更新您的 Python 版本。如果失败,那么您可以从requests库并安装certifi:

PS> python -m venv venv
PS> .\venv\Scripts\activate
(venv) PS> python -m pip install certifi
$ python3 -m venv venv
$ source venv/bin/activate.sh
(venv) $ python3 -m pip install certifi

证明书是您可以使用的证书集合,而不是系统的集合。您可以通过使用以下命令创建 SSL 上下文来完成此操作certifi证书包而不是操作系统的证书包:

>>>
>>> import ssl
>>> from urllib.request import urlopen
>>> import certifi
>>> certifi_context = ssl.create_default_context(cafile=certifi.where())
>>> urlopen("https://sha384.badssl.com/", context=certifi_context)
<http.client.HTTPResponse object at 0x000001C7407C3490>

在此示例中,您使用了certifi充当您的 SSL 证书存储,并且您使用它成功连接到具有已知良好 SSL 证书的站点。请注意,而不是._create_unverified_context(), 你用.create_default_context().

这样,您就可以保持安全,而不会遇到太多麻烦!在下一部分中,您将涉足身份验证的世界。

经过身份验证的请求

身份验证是一个广泛的主题,如果您处理的身份验证比此处介绍的内容复杂得多,那么这可能是一个很好的起点requests包裹。

在本教程中,您将仅介绍一种身份验证方法,该方法作为您为验证请求而必须进行的调整类型的示例。urllib.request确实有许多其他有助于身份验证的功能,但本教程不会介绍这些功能。

最常见的身份验证工具之一是不记名令牌,由RFC 6750。它经常被用作开放认证,但也可以单独使用。它也是最常见的一种标题,您可以将其与当前的标题一起使用make_request()功能:

>>>
>>> token = "abcdefghijklmnopqrstuvwxyz"
>>> headers = {
...     "Authorization": f"Bearer {token}"
... }
>>> make_request("https://httpbin.org/bearer", headers)
200
(b'{\n  "authenticated": true, \n  "token": "abcdefghijklmnopqrstuvwxyz"\n}\n',
<http.client.HTTPResponse object at 0x0000023D612642E0>)

在此示例中,您向 httpbin 发出请求/bearer端点,模拟承载身份验证。它会接受任何字符串作为令牌。它只需要 RFC 6750 指定的正确格式。名称has成为Authorization,或有时小写authorization,和值has成为Bearer,其与标记之间有一个空格。

笔记:如果您使用任何形式的令牌或秘密信息,请务必适当保护这些令牌。例如,不要将它们提交到 GitHub 存储库,而是将它们存储为临时文件环境变量.

恭喜,您已使用不记名令牌成功完成身份验证!

另一种形式的身份验证称为基本访问认证,这是一种非常简单的身份验证方法,仅比在标头中发送用户名和密码稍好一些。是非常没有安全感的!

当今最常用的协议之一是OAuth(开放授权)。如果您曾经使用 Google、GitHub 或 Facebook 登录过另一个网站,那么您就使用过 OAuth。 OAuth 流程通常涉及您想要交互的服务和身份服务器之间的一些请求,从而产生短暂的不记名令牌。然后,可以通过持有者身份验证使用该持有者令牌一段时间。

身份验证的大部分内容都归结为了解目标服务器使用的特定协议并仔细阅读文档以使其正常工作。

POST 请求urllib.request

您已经发出了很多 GET 请求,但有时您想要发送信息。这就是 POST 请求的用武之地。使用以下命令发出 POST 请求urllib.request,您不必显式更改该方法。你可以只通过一个data反对新的Request反对或直接反对urlopen()。这data不过,对象必须采用特殊格式。你会调整你的make_request()通过添加稍微功能来支持 POST 请求data范围:

 # request.py

 from urllib.error import HTTPError, URLError
 from urllib.request import urlopen, Request

-def make_request(url, headers=None):
+def make_request(url, headers=None, data=None):

-    request = Request(url, headers=headers or {})
+    request = Request(url, headers=headers or {}, data=data)
     try:
         with urlopen(request, timeout=10) as response:
             print(response.status)
             return response.read(), response
     except HTTPError as error:
         print(error.status, error.reason)
     except URLError as error:
         print(error.reason)
     except TimeoutError:
         print("Request timed out")

这里您刚刚修改了函数以接受data参数的默认值为None,然后你将其传递到Request实例化。但这并不是所有需要做的事情。您可以使用两种不同格式之一来执行 POST 请求:

  1. 表格数据: application/x-www-form-urlencoded
  2. JSON: application/json

第一种格式是 POST 请求最古老的格式,涉及对数据进行编码百分比编码,也称为 URL 编码。您可能已经注意到键值对 URL 编码为请求参数。键与值之间用等号 (=),键值对用 & 符号分隔 (&),空格通常会被抑制,但可以用加号 (+).

如果您从 Python 字典开始,请在您的字典中使用表单数据格式make_request()函数,你需要编码两次:

  1. 一次对字典进行 URL 编码
  2. 然后再次将结果字符串编码为字节

对于 URL 编码的第一阶段,您将使用另一个urllib模块,urllib.parse。请记住以交互模式启动脚本,以便您可以使用make_request()函数并在 REPL 上使用它:

>>>
>>> from urllib.parse import urlencode
>>> post_dict = {"Title": "Hello World", "Name": "Real Python"}
>>> url_encoded_data = urlencode(post_dict)
>>> url_encoded_data
'Title=Hello+World&Name=Real+Python'
>>> post_data = url_encoded_data.encode("utf-8")
>>> body, response = make_request(
...     "https://httpbin.org/anything", data=post_data
... )
200
>>> print(body.decode("utf-8"))
{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "Name": "Real Python",
    "Title": "Hello World"
  },
  "headers": {
    "Accept-Encoding": "identity",
    "Content-Length": "34",
    "Content-Type": "application/x-www-form-urlencoded",
    "Host": "httpbin.org",
    "User-Agent": "Python-urllib/3.10",
    "X-Amzn-Trace-Id": "Root=1-61f25a81-03d2d4377f0abae95ff34096"
  },
  "json": null,
  "method": "POST",
  "origin": "86.159.145.119",
  "url": "https://httpbin.org/anything"
}

在此示例中,您:

  1. 进口urlencode()来自urllib.parse模块
  2. 从字典开始初始化您的 POST 数据
  3. 使用urlencode()对字典进行编码的函数
  4. 使用 UTF-8 编码将结果字符串编码为字节
  5. 向以下人员提出请求anything的终点httpbin.org
  6. 打印 UTF-8 解码的响应正文

UTF-8 编码是规格为了application/x-www-form-urlencoded类型。 UTF-8 被抢先使用来解码正文,因为您已经知道httpbin.org可靠地使用 UTF-8。

anything来自 httpbin 的端点充当一种回显,返回它收到的所有信息,以便您可以检查所发出请求的详细信息。在这种情况下,您可以确认method确实是POST,您可以看到您发送的数据列在下面form.

要使用 JSON 发出相同的请求,您需要将 Python 字典转换为 JSON 字符串:json.dumps(),用 UTF-8 编码,将其作为data参数,最后添加一个特殊的头来表明数据类型是JSON:

>>>
>>> post_dict = {"Title": "Hello World", "Name": "Real Python"}
>>> import json
>>> json_string = json.dumps(post_dict)
>>> json_string
'{"Title": "Hello World", "Name": "Real Python"}'
>>> post_data = json_string.encode("utf-8")
>>> body, response = make_request(
...     "https://httpbin.org/anything",
...     data=post_data,
...     headers={"Content-Type": "application/json"},
... )
200
>>> print(body.decode("utf-8"))
{
  "args": {},
  "data": "{\"Title\": \"Hello World\", \"Name\": \"Real Python\"}",
  "files": {},
  "form": {},
  "headers": {
    "Accept-Encoding": "identity",
    "Content-Length": "47",
    "Content-Type": "application/json",
    "Host": "httpbin.org",
    "User-Agent": "Python-urllib/3.10",
    "X-Amzn-Trace-Id": "Root=1-61f25a81-3e35d1c219c6b5944e2d8a52"
  },
  "json": {
    "Name": "Real Python",
    "Title": "Hello World"
  },
  "method": "POST",
  "origin": "86.159.145.119",
  "url": "https://httpbin.org/anything"
}

To 连载这次你使用的字典json.dumps()代替urlencode()。您还明确添加内容类型标头值为application/json。有了这些信息,httpbin 服务器就可以在接收端反序列化 JSON。在其响应中,您可以看到下面列出的数据json钥匙。

笔记:有时需要以纯文本形式发送 JSON 数据,这种情况下的步骤如上,只是您设置Content-Type作为text/plain; charset=UTF-8。其中许多必需品取决于您要向其发送数据的服务器或 API,因此请务必阅读文档并进行实验!

这样,您现在就可以开始发出 POST 请求了。本教程不会详细介绍其他请求方法,例如PUT。可以说,您还可以通过传递一个来显式设置该方法method实例化的关键字参数请求对象.

请求包生态系统

总而言之,本教程的最后一部分致力于阐明 Python 的 HTTP 请求的包生态系统。由于软件包众多,没有明确的标准,可能会造成混乱。也就是说,每个包都有用例,这意味着您有更多选择!

什么是urllib2urllib3?

要回答这个问题,你需要回到早期的Python,一直回到1.2版本,当时最原始的Python网址库被介绍了。 1.6版本左右,进行了改版urllib2被添加,它与原来的一起生活urllib。当 Python 3 出现时,最初的urllib已被弃用,并且urllib2放弃了2,取原来的urllib姓名。它也分为几部分:

  • urllib.错误
  • urllib.parse
  • urllib.请求
  • urllib.响应
  • urllib.robotparser

那么呢urllib3?这是一个第三方库开发的urllib2还在附近。它与标准库无关,因为它是一个独立维护的库。有趣的是,requests库实际使用urllib3在引擎盖下,也是如此pip!

我应该什么时候使用requests超过urllib.request?

主要的答案是易用性和安全性。urllib.request被认为是一个低级库,它公开了有关 HTTP 请求工作原理的大量细节。蟒蛇文档为了urllib.request毫不犹豫地推荐requests作为更高级别的 HTTP 客户端接口。

如果您日复一日地与许多不同的 REST API 交互,那么requests强烈推荐。这requests该库标榜自己是“为人类而构建”,并已成功围绕 HTTP 创建了直观、安全且简单的 API。它通常被认为是首选图书馆!如果您想了解更多关于requests库,查看 Real Python请求指南.

举例说明如何requests当涉及到字符编码时,事情就变得更容易了。你会记得urllib.request,您必须了解编码并采取一些步骤来确保无错误的体验。这requests包将其抽象出来,并将通过使用解析编码沙代,一个通用字符编码检测器,以防万一有什么有趣的事情。

如果您的目标是了解有关标准 Python 的更多信息以及它如何处理 HTTP 请求的详细信息,那么urllib.request是进入这个领域的好方法。您甚至可以更进一步,使用非常低级的http模块。另一方面,您可能只想将依赖关系保持在最低限度,这urllib.request是有能力的。

为什么是requests不是标准库的一部分?

也许你想知道为什么requests目前还不是 Python 核心的一部分。

这是一个复杂的问题,没有硬性且快速的答案。关于原因有很多猜测,但有两个原因似乎很突出:

  1. requests还有其他第三方依赖项也需要集成。
  2. requests需要保持敏捷性,并且可以在标准库之外更好地做到这一点。

requests库具有第三方依赖项。整合requests进入标准库意味着还集成chardet, certifi, 和urllib3等。另一种选择是从根本上改变requests仅使用 Python 现有的标准库。这不是一件简单的任务!

整合requests也意味着开发这个库的现有团队将不得不放弃对设计和实现的完全控制,让位于PEP做决定的过程。

HTTP 规范和建议一直在变化,高级库必须足够敏捷才能跟上。如果需要修补安全漏洞或添加新的工作流程,requests团队的构建和发布速度比 Python 发布过程的一部分要快得多。据称,有时他们会在发现漏洞十二小时后发布安全修复程序!

有关这些问题及更多问题的有趣概述,请查看将请求添加到标准库,总结了 Python 语言峰会上的讨论肯尼思·赖茨,Requests 的创建者和维护者。

因为这种敏捷性对于requests及其底层urllib3,自相矛盾的陈述requests对于经常使用的标准库来说太重要了。这是因为 Python 社区很大程度上依赖于requests将其集成到 Python 核心中的敏捷性可能会损害它和 Python 社区。

在 GitHub 存储库问题板上requests,发布了一个问题,要求包含标准库中的请求。的开发商requestsurllib3插话,主要是说他们可能会失去自己维护它的兴趣。有些人甚至表示他们将分叉存储库并继续为自己的用例开发它们。

话虽如此,请注意requests库 GitHub 存储库托管在 Python 软件基金会的帐户下。仅仅因为某些东西不是 Python 标准库的一部分,并不意味着它不是生态系统不可或缺的一部分!

看来目前的情况对Python核心团队和维护者来说都是有利的。requests。虽然对于新手来说可能会有点困惑,但现有的结构为 HTTP 请求提供了最稳定的体验。

还需要注意的是,HTTP 请求本质上是复杂的。urllib.request不会试图粉饰太多。它公开了 HTTP 请求的许多内部工作原理,这就是它被称为低级模块的原因。您的选择requests相对urllib.request实际上取决于您的特定用例、安全问题和偏好。

结论

您现在可以使用urllib.request发出 HTTP 请求。现在,您可以在项目中使用这个内置模块,使它们在更长时间内保持无依赖性。您还通过使用较低级别的模块对 HTTP 有了深入的了解,例如urllib.request.

在本教程中,您已经:

  • 学会了基本的制作方法HTTP 请求urllib.request
  • 探索了一个的具体细节HTTP消息并研究了它是如何表示的urllib.request
  • 弄清楚如何处理角色编码HTTP 消息数
  • 探索了一些常见错误使用时urllib.request并学会了如何解决它们
  • 把你的脚趾浸入这个世界经过身份验证的请求urllib.request
  • 明白为什么两者urllibrequests图书馆存在并且何时使用其中之一

您现在可以使用以下命令发出基本的 HTTP 请求urllib.request,并且您还拥有使用标准库更深入研究低级 HTTP 领域的工具。最后你可以选择是否使用requests或者urllib.request,取决于您想要或需要什么。享受探索网络的乐趣!

了解更多: 单击此处加入 Real Python Newsletter 上 290,000 多名 Python 开发人员的行列并获取新的 Python 教程和新闻,让您成为更高效的 Pythonista。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python 的 urllib.request 用于 HTTP 请求 的相关文章

  • 通过 Office API 将多个 Word 文档保存为 HTML

    我有大量的Word文档需要解析 由于它们都是从同一个模板创建的 我认为最好的方法是将它们保存为 HTML 文件并解析 HTML 本身 虽然将单个 Word 文档保存为 HTML 相当容易 但我还没有找到从 Word 内部执行批量过程的方法
  • .NET Web API - 添加日志记录

    我正在寻找有关处理 API 日志记录的最佳方法的帮助 我想将所有请求和响应记录到 sql 或文本文件 如果这是最好的方法 目前我已经在 SQL Server 的日志表中插入一行 我使用名为 LogAction 的静态方法来执行此操作 并在
  • 如何按城市过滤 WikiVoyage API 结果?

    我目前正在尝试使用 wikivoyage API 我当前的 API 调用如下所示 en wikivoyage org w api php action query list search srwhat text srsearch Pari
  • R 中的 Tabulizer 包:如何在特定标题后抓取表格

    如何从 PDF 中抓取一些标题文本前面的表格 我正在尝试 tabulizer 包 这是从特定页面获取表格的示例 波兰语 公共卫生需求地图 library tabulizer library tidyverse options java pa
  • C++标准API

    我是一名学生 也是 C 新手 我正在寻找与 Java API 一样全面的标准 C API 到目前为止我一直在使用cplusplus com http www cplusplus com and cppreference com https
  • 从我自己的博客获取帖子[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 是否有任何 API 通过它我可以从 wordpress com 上我自己的博客获取帖子并将它们放在我的
  • 如何使用 Twitter Api 在单个请求中获取 20 多个列表成员?

    我想让超过 20 个用户在单个请求中使用 twitter api 有什么参数可以指定吗 我正在使用这个APIhttp api twitter com 1 Barelyme Politics members xml cursor 1 http
  • Swift:转义闭包捕获非转义参数“onCompletion”

    我的 swift 有问题 我正在尝试发送 API 请求 然后检索数据 但收到以下错误消息 Swift 转义闭包捕获非转义参数 onCompletion 有谁知道我该如何解决这个问题 提前致谢 Code class RestApiManage
  • 使用查询选择器从 VBA 中抓取

    我使用了该网站的代码来提取数据site https bazashifer ru proflist profnastil Option Explicit Public Sub GetInfo Dim sResponse As String i
  • Web Api - 不允许捕获 405 方法

    截至目前 Web api 应用程序针对 405 方法不允许错误返回以下响应正文 我正在尝试更改响应正文 但我不知道如何使用委托处理程序 ApiControllerActionSelector 或过滤器 谁能帮我捕获服务器端的 405 错误
  • 使用 python 更新 Google 搜索方法

    我试图使用xgoogle https github com pkrumins xgoogle但我已经 3 年没有更新了 即使我设置每页 100 个结果 我也只能得到不超过 5 个结果 如果有人使用 xgoogle 没有任何问题 请告诉我 现
  • 如何在 NodeJS 中允许表单数据

    我最近创建了一个接受文件的 API 我正在尝试使用 Postman 测试 API 如果我使用发出帖子请求x wwww form urlencoded身体类型 一切正常 我得到了所有预期的数据 唯一的问题是它不允许发送文件 如果我使用form
  • 如何将 Live API for Contacts 的响应中的哈希值转换为文本

    我集成了 Live JS api 来获取用户的实时联系人 它以哈希格式 email hash 返回电子邮件 我如何使用 javascript 或 c net 转换为可读文本 非常感谢 我遇到了同样的问题 并且找到了解决方案 您所需要做的就是
  • Puppeteer 的行为与开发者控制台不同

    我正在尝试使用 Puppeteer 提取此页面的标题 https www nordstrom com s zella high waist studio pocket 7 8 leggings 5460106 https www nords
  • Google Sheets API v4 和 valueInputOption

    我的电子表格中有三列 第一个是日期 第二个和第三个是简单字符串 当我批量上传数据时valueInputOption RAW 我的日期列得到错误的结果 所有日期前面都有一个看不见的撇号 字符串列没问题 当我使用valueInputOption
  • Yammer API 限制

    Yammer Rest api 文档表示 每个应用程序每个用户在 10 秒内最多可以发出 10 个请求 问题 什么是user这里 是为当前经过身份验证的 yammer 用户发出请求的 IP 地址还是承载令牌 如果我们所有的用户都使用相同的外
  • 用 Beautiful Soup 进行抓取:为什么 get_text 方法不返回该元素的文本?

    最近我一直在用 python 开发一个项目 其中涉及抓取一些网站的一些代理 我遇到的问题是 当我尝试抓取某个知名代理站点时 当我要求 Beautiful Soup 查找 IP 在代理表中的位置时 它并没有按照我的预期执行操作 我将尝试查找每
  • Booking.com酒店管理API

    我拥有一家酒店 并在 booking com 上查看了 API 因为我想创建自己的前端界面来更新我的酒店房价 房间数 以及通过该 API 上传图片 更新酒店描述 然而 我唯一能找到的是一个 API 供联营公司以一定的价格获取特定位置的酒店等
  • webm视频转换API

    有谁知道用于将视频转换为谷歌新的 WebM 视频格式的 原型 c API 谷歌快速搜索显示 不 但是编码器示例 http www webmproject org tools vp8 sdk example simple encoder ht
  • 无法摆脱脚本中的硬编码延迟

    我用 vba 结合 selenium 编写了一个脚本来解析网页中可用的所有公司名称 该网页启用了延迟加载方法 因此每个滚动中只有 20 个链接可见 如果我滚动 2 次 则可见链接数为 40 个 依此类推 该网页中有 1000 个可用链接 我

随机推荐

  • 用于近似重复检测的指纹图像

    目录 我们会做什么 什么是图像指纹 哈希 为什么不能使用md5 sha 1等 图像指纹可以用在哪里 我们需要什么库 第 1 步 对数据集进行指纹识别 第 2 步 搜索数据集 结果 改进我们的算法 概括 这是 Adrian Rosebrock
  • Django 和 AJAX 表单提交 – 更多练习

    目录 设置事件处理程序 创建 AJAX 请求 更新 Django 视图 处理回调 更新 DOM 下一步是什么 这是 Real Python 和 Mr 的合作作品 内森 尼科尔斯 于 2014 年 9 月 5 日更新 使应用程序更加 REST
  • 设置您的工作环境

    要下载本课程的数据集 您可以访问真正的 Python GitHub 存储库 有关本课程所涵盖概念的更多信息 您可以查看 Python 虚拟环境 入门 Visual Studio Code 中的 Python 开发 Jupyter Noteb
  • Python 中的字典

    目录 定义字典 访问字典值 字典键与列表索引 增量构建字典 字典键的限制 对字典值的限制 运算符和内置函数 Built in Dictionary Methods d clear d get d items d keys d values
  • 纯Python直方图

    当您准备绘制直方图时 最简单的方法是不要考虑箱 而是报告每个值出现的次数 频率表 一条蟒蛇字典非常适合这项任务 gt gt gt gt gt gt Need not be sorted necessarily gt gt gt a 0 1
  • 了解日期和时间是混乱的

    日期和时间并不是简单的事情 尤其是现在大多数计算都是远程完成的 无法保证计算机和用户位于同一个地方 由于管理夏令时和时区的规则不是静态的 这一事实使情况变得更加复杂 在本课程中 您将探索所有奇怪的边缘情况 并了解程序员通常如何处理它们
  • 掌握Python的内置时间模块

    蟒蛇time模块提供了多种方式代表时间代码中 例如对象 数字和字符串 它还提供除表示时间之外的功能 例如在代码执行期间等待和测量代码的效率 本课程将引导您了解最常用的函数和对象time 完成本课程后 您将能够 理解处理日期和时间的核心概念
  • 关于奥尔德伦·桑托斯

    关于奥尔德伦 桑托斯 个人网站 大家好 我是 Aldren Santos 担任自由平面设计师 插画师已有 3 年了 我的任务是尽我所能 让这个网站变得更加精彩 我真心希望我的插图能够吸引您通过我们团队辛勤工作的这些教程学习 Python 的
  • 编写和测试 Python 函数:面试练习(概述)

    无论您是想在编码面试中取得好成绩 还是只是想提高您的开发技能 解决编码挑战可以帮助您成长为一名程序员 在这个真实的 Python 代码对话中 Philipp 向 Martin 提出挑战 要求他编写一个函数 将字符串中的每个字符加倍 通过他们
  • 引导 Django 项目

    有关本课程所涵盖概念的更多信息 您可以查看 如何设置 Django 项目 真正的Python文章 使用 Django 和 Python 构建个人日记 真正的Python文章 以下是本课程中使用的命令行片段 mkdir portfolio p
  • Python 石头剪刀布:命令行游戏(概述)

    游戏编程是学习如何编程的好方法 您可以使用许多在现实世界中看到的工具 此外您还可以玩游戏来测试您的结果 开始 Python 游戏编程之旅的理想游戏是剪刀石头布 在本课程中 您将学习如何 自己编写代码剪刀石头布游戏 接受用户输入input 使
  • 2021 年 4 月 21 日

    主持人大卫 阿莫斯回答会员的问题 在这次会议上 我们讨论了 Real Python 的新功能 在哪里可以找到要阅读的代码以提高您的 Python 技能 为什么 0xfor x in 1 2 3 回报 15 数据科学 Django 和 Fla
  • Python 中的 K 均值聚类:实用指南

    目录 What Is Clustering 聚类技术概述 分区聚类 层次聚类 基于密度的聚类 How to Perform K Means Clustering in Python 了解 K 均值算法 使用 Python 编写您的第一个 K
  • 在 Python 中使用 lru_cache 进行缓存

    有很多方法可以实现快速响应的应用程序 缓存是一种方法 如果使用得当 可以使事情变得更快 同时减少计算资源的负载 蟒蛇的功能工具模块附带 lru cache 装饰器 这使您能够使用以下命令缓存函数的结果最近最少使用 LRU 策略 这是一种简单
  • 拼写错误、缺失或误用 Python 关键字

    以下是有关 Python 关键字的更多信息的资源 Python 关键字 简介 真正的 Python 文章 Python 3 8 关键字 Python 文档
  • Python 标准 REPL:快速尝试代码和想法

    目录 Getting to Know the Python Standard REPL 什么是 Python 的交互式 Shell 或 REPL 为什么使用 Python REPL Starting and Ending REPL Inte
  • 使用 Fabric 和 Ansible 自动化 Django 部署

    目录 设置和配置 Fabric Setup 设置 SSH 密钥 强化用户密码 安装 Ansible 依赖项 将 SELinux 设置为宽容模式 升级服务器 完整性检查 Ansible Primer 剧本 示例手册 Playbook Setu
  • 第 27 集:准备面试 Python 练习题

    第 27 集 准备面试 Python 练习题 真正的 Python 播客 2020 年 9 月 18 日47m RSS Apple Podcasts Google Podcasts Spotify More 播客瘾君子 灰蒙蒙 袖珍铸件 投
  • Python 基础知识:函数和循环(摘要)

    在本视频课程中 您了解了两个最基本的概念 在编程中 函数和循环 首先 您学习了如何定义自己的自定义函数 你看到了 该函数由两部分组成 这函数签名 这开始于def关键字并包括函数名称和函数参数 这函数体 其中包含每当调用该函数时运行的代码 函
  • Python 的 urllib.request 用于 HTTP 请求

    目录 使用 urllib request 的基本 HTTP GET 请求 The Nuts and Bolts of HTTP Messages 了解什么是 HTTP 消息 了解 urllib request 如何表示 HTTP 消息 关闭