跳转到内容

User:Xyy23330121/Python/HTTP 请求

来自维基学院


本章节,我们将主要讲解如何使用 Python 自动访问互联网内容。本章节默认读者对 HTML 有基本的了解,知道如何使用 正则表达式 等方式,从 HTML 代码中提取信息。

Python 内置的模块 urllib 提供了对互联网内容进行请求的方法。而第三方模块 requests 提供了比 urllib 更好用的方法。本章讲解的内容将基于 requests,而非 urllib。

对于使用从 python.org 下载的 Windows 版本的 Python,我们可以从命令行程序中,输入以下代码以安装 request。

python -m pip install requests

如果链接不畅,或者下载失败。可以用以下代码,改为从清华大学提供的托管服务器中下载 requests。

python -m pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple

基本请求

[编辑 | 编辑源代码]

访问互联网的基本方式是进行 HTTP 请求。如果读者仅是访问网页,无需登录网站账号等,只要看本章节内容即可。

import requests
r = requests.get("https://zh.wikiversity.org")

requests.get() 等请求函数会进行一次请求,并返回请求得到的结果。

响应结果

[编辑 | 编辑源代码]

对于结果对象 rr.content会包含所得到的响应内容。该响应内容是二进制的 bytes 对象。在实际使用时,并不会直接使用二进制的内容,而是通过 HTTP 头中的信息,将二进制内容按指定的编码解码后、再进行处理。

requests 会按照 HTTP 头中的信息自动设置编码方式。读者也可以自行设置编码方式。

r = requests.get("https://www.baidu.com/s?wd=URL")
print(r.encoding) #输出自动的编码方式
r.encoding = "ascii" #将编码方式改为 ascii

响应结果文本

[编辑 | 编辑源代码]

类似 r.content,r.text会包含所得到的结果的文本形式。它等同于

r.content.decode(encoding = r.encoding, errors = "ignore")

如果请求的是一个 HTML 页面,则返回的会是该 HTML 页面的源代码,比如:

r = requests.get("https://www.baidu.com/s?wd=URL")
print(r.text)

输出为:

<html>
<head>
        <script>
                location.replace(location.href.replace("https://","http://"));
        </script>
</head>
<body>
        <noscript><meta http-equiv="refresh" content="0;url=http://www.baidu.com/"></noscript>
</body>
</html>

如果请求的并非 HTML 页面,此方法也会原样返回得到的内容。比如以下方式请求得到的是一个 json 文本序列。

s = requests.get("https://api.bilibili.com/x/web-show/page/header")
print(s.text)

输出为:

{"code":-400,"message":"Key: 'ResourceID' Error:Field validation for 'ResourceID' failed on the 'min' tag","ttl":1}

json 响应结果

[编辑 | 编辑源代码]

使用 r.json() 即可将获得的 json 结果转换为对应的 Python 对象。该方法等同于:

import json
json.loads(r.text)

原始响应结果

[编辑 | 编辑源代码]

在极少数情况,读者可能需要使用原始的响应结果。r.raw 会输出原始的响应结果对象。如果要使用原始响应结果,在请求时,需要添加 stream 参数,比如:

r = requests.get("https://www.baidu.com/s?wd=URL", stream=True)

原始的响应结果对象是第三方库 urllib3 中的一个 urllib3.response.HTTPResponse 对象。这里不多赘述。

高级请求

[编辑 | 编辑源代码]

除了基本方式之外,HTTP 能传输更多信息。如果要实现更高级的功能,就要了解 HTTP 能传输的信息内容。本章将讲解 HTTP 请求中包含的内容,以及如何用 requests 模块中的方法来传输这些信息。

URL参数

[编辑 | 编辑源代码]

在 HTTP 标准中, URL 是可以包含参数的。比如:

r = requests.get("https://www.baidu.com/s?wd=URL")

除上述由读者在 URL 中添加参数之外,requests 还可以将参数自动加入到 URL 里面并进行请求:

payload = {"wd" : "URL"}
r = requests.get("https://www.baidu.com/s", params = payload)
print(r.url) #输出:https://www.baidu.com/s?wd=URL

请求的标头

[编辑 | 编辑源代码]

HTTP 请求中,HTTP 标头(HTTP header)会包含比如“发送请求的客户端名称”、“传输的编码方式”等等。具体参见 HTTP 标头

以“客户端名称”为例,想要自定义发送的客户端名称,可以用如下方式:

head = {'user-agent': 'my-app/0.0.1'}
r = requests.get("https://www.baidu.com/", headers = head)

需要注意的是:

  • 标头字典中的所有值必须是字符串、bytes 或者 Unicode 信息。最好不要使用 Unicode 信息。
  • 自定义标头的优先级低于其它可确定的信息源,比如:
    • (如果有)netrc 配置文件的内容
    • (如果有)auth 参数的内容。
    • (如果有)在 URL 中提供的代理凭据。标头 Proxy-Authorization 将被 URL 中提供的代理凭据覆盖。
    • (可以确定信息长度时)标头 Content-Length 会被修改为确定的信息长度。
    • 其它类似的信息源。
关于 httpbin.org
httpbin.org 是一个公益性的网站。向 httpbin.org 域名下的对应 URL 发送请求时。请求中的所有信息会在返回信息的正文中出现。善用这一工具有助于读者了解如何发送请求。
详细使用方式参见 https://httpbin.org/#/

cookie 是 HTTP 标头中的一个字段。

网页常用 cookies 来记录用户的情况。浏览器在请求网页时,也会发送上次浏览网页时留下的 cookies,以实现自动登录等功能。一个 cookie 会包含以下的内容:

  • 域名
  • 路径
  • 过期时间

在使用时,我们可以用字典,只传递“键”和“值”,让其它内容自动填充。比如:

url = 'https://httpbin.org/cookies'
cookies = {'name': 'value'}
r = requests.get(url, cookies=cookies)
print(r.text)

也可以使用 requests.cookies.RequestsCookieJar 实例来传递更多内容。比如:

#创建存储 cookies 的对象。
jar = requests.cookies.RequestsCookieJar()

#添加两个 cookie
jar.set('name1', 'value1', domain='httpbin.org', path='/cookies')
jar.set('name2', 'value2', domain='httpbin.org', path='/')

#请求时传递 cookies。
url = 'https://httpbin.org/cookies'
r = requests.get(url, cookies=jar)
print(r.text)

对于需要登录的网页,读者可以使用比如 EditThisCookie 之类的浏览器扩展程序来查看并复制自己浏览网页产生的 cookies,将其详细信息复制,并用 requests 模块发送。此时请求的结果,等同于读者登录账号之后的请求结果。

身份验证信息

[编辑 | 编辑源代码]

HTTP 协议支持自定义的身份验证信息。该信息的内容应当由服务器和用户自行约定。并会覆盖位于标头的 Authorization 信息。

该内容应当为一个多个不含空格的ASCII字符串组成的元组,可以使用以下方式插入该信息。

auth = ("Basic","QWxhZGRpbjpvcGVuIHNlc2FtZQ==")

url = 'https://httpbin.org/get'
r = requests.get(url, auth=auth)

响应延时

[编辑 | 编辑源代码]

在请求时,有时会需要一些时间才能正确接收到服务器返回的信息;也可能请求丢失、服务器未响应。设定响应延时可以解决这些问题。

响应延时是以秒为单位的浮点数或形如 (连接延时, 读取延时) 的浮点数元组。使用方式为:

url = 'https://httpbin.org/get'
r = requests.get(url, timeout=0.1)

https 重定向

[编辑 | 编辑源代码]

许多网页都会把 http 的内容重定向到 https 来处理。虽然禁止重定向没有什么好处,我们是可以禁止这样的重定向的。比如:

r = requests.get('http://github.com/', allow_redirects=False)

代理服务器

[编辑 | 编辑源代码]

可以设置经由代理服务器发送请求。方式如下:

proxies = {
  'http': 'http://10.10.1.10:3128',
  'https': 'http://10.10.1.10:1080',
}
requests.get('http://example.org', proxies=proxies)

如果在请求时设置了 stream 参数为 True,则返回的内容会是一个“二进制流”。

r = requests.get('https://github.com', stream=True)

if r.encoding is None:
    r.encoding = 'utf-8'

for line in r.iter_lines(): #像二进制流一样处理返回的内容
    line.decode(r.encoding)

在互相传递信息时,需要验证双方的身份。互联网通过公私钥加密建立了“证书”机制,通过证书可以验证沟通双方的身份。其具体内容可以参见 数字证书 。读者在进行交流时,也需要验证证书。

服务器验证

[编辑 | 编辑源代码]

在进行请求时,可以对服务器返回的 TLS 证书进行验证。

不进行验证
[编辑 | 编辑源代码]

默认总会验证服务器返回的 TLS 证书。如果要不进行验证,可以增加以下参数。

requests.get('http://example.org', verify=False)
要求证书路径
[编辑 | 编辑源代码]

如果 verify 参数为一个字符串,则只有服务器使用路径包含 verify 的证书时,才会通过验证。比如:

requests.get('https://github.com', verify='/path/to/certfile')

客户端验证

[编辑 | 编辑源代码]

可以为自己的客户端设置证书。

如果给 cert 参数输入字符串,则应当输入指向 ssl 客户端认证文件(.pem 文件)的路径。如果输入元组,则应当传入形如 ("cert","key") 的元组。

更多请求

[编辑 | 编辑源代码]

上面章节提到的请求,均为“GET”请求,只用于读取资料。如果要进行其它操作,则需要使用其它的请求。要了解所有的请求方式,参见 HTTP 请求方法

requests 实现了以下几种请求方式:

  • GET,读取资料。
  • HEAD,获得资料的元信息,而不获取整个资料。
  • OPTIONS,使服务器传回该资源支持的所有HTTP请求方法。
  • POST,上传新数据。
  • PUT,上传并修改。
  • DELETE,删除数据。
  • PATCH,将局部更改应用到资源。

在 requests 模块中,这些请求方式支持 requests.get() 函数中支持的一切参数。以下将主要讲解它们在使用时与 GET 的区别。

requests.head

[编辑 | 编辑源代码]

requests.head() 函数向服务器请求资料,但服务器不会返回资料的正文部分。与 GET 不同,此函数默认禁用重定向。

r = requests.head('https://httpbin.org/get')

如果要启用重定向,则需要:

r = requests.head('http://github.com/', allow_redirects=True)

requests.options

[编辑 | 编辑源代码]

OPTIONS 请求的方法如下:

r = requests.options('https://httpbin.org/get')
print(r.headers['allow'])  #输出:OPTIONS, GET, HEAD

从以上输出可以知道,该 URL 支持三种请求:OPTIONS、GET 和 HEAD。

requests.post

[编辑 | 编辑源代码]

POST 请求用于上传信息。

form 信息的发送

[编辑 | 编辑源代码]

http 支持表单内容的发送。其基本使用方法如下:

payload = {'key': 'value'} #要上传的信息
r = requests.post('https://httpbin.org/post', data=payload)
print(r.text)

通过在 data 中传入字典,可以将字典的信息以 form 的形式发送。

在 data 中,可以给同一个键设置多个值,此时可以用以下两种方法:

payload_tuples = [('key1', 'value1'), ('key1', 'value2')]
r1 = requests.post('https://httpbin.org/post', data=payload_tuples)
payload_dict = {'key1': ['value1', 'value2']}
r2 = requests.post('https://httpbin.org/post', data=payload_dict)

文件发送

[编辑 | 编辑源代码]

如果要发送文件,则可以使用以下方式:

url = 'https://httpbin.org/post'
files = {'file': open('report.xls', 'rb')}

r = requests.post(url, files=files)
print(r.text)

其中,文件内容最好是二进制的,这样有助于自动获取请求中信息的大小。这样发送的文件不包含文件名,也不包含处理方式。仅包含文件的内容。

如果要包含文件名、文件类型等信息,可以使用元组:

url = 'https://httpbin.org/post'
files = {'file': (
  'report.xls',
  open('report.xls', 'rb'),
  'application/vnd.ms-excel',
  {'Expires': '0'}
)}

r = requests.post(url, files=files)
print(r.text)

如果读者希望,也可以直接将文本内容当成文件的内容并发送出去:

url = 'https://httpbin.org/post'
files = {'file': (
  'report.csv',
  'some,data,to,send\nanother,row,to,send\n'
)}

r = requests.post(url, files=files)
print(r.text)

流式发送

[编辑 | 编辑源代码]

对于较大的文件,可能无法在一个请求中完整发送。此时,需要使用流式发送:

with open('file_path', 'rb') as f:
    requests.post("http://some.url/streamed", data = f)

这种流式发送,需要所请求的服务器有对应的支持。

同时发送多个文件

[编辑 | 编辑源代码]

使用以下方法,可以同时发送多个文件:

url = 'https://httpbin.org/post'
multiple_files = [
    ('images', ('foo.png', open('foo.png', 'rb'), 'image/png')),
    ('images', ('bar.png', open('bar.png', 'rb'), 'image/png'))]
r = requests.post(url, files=multiple_files)
r.text

在发送文件时,建议用二进制模式打开文件。

requests.put

[编辑 | 编辑源代码]
r = requests.put('https://httpbin.org/put', data={'key': 'value'})

requests.delete

[编辑 | 编辑源代码]
r = requests.delete('https://httpbin.org/delete')

requests.patch

[编辑 | 编辑源代码]
r = requests.patch('https://httpbin.org/put', data={'key': 'value'})

请求的钩子

[编辑 | 编辑源代码]

requests 模块的请求支持使用钩子。目前支持的钩子仅有一个:"response"。这个钩子会在请求得到响应时触发,使用方法如下:

def print_url(r):
    print(f"向 {r.url} 发出了请求")

r = requests.get('https://httpbin.org/', hooks={'response': print_url})

程序输出为:

 https://httpbin.org/ 发出了请求

在得到响应后,钩子指定的函数会被传入一个响应结果对象。

特别的,如果 "response" 钩子所指定的函数返回值不为 None,则 requests.get() 函数会返回钩子函数所返回的内容,比如以下的钩子函数:

def record_hook(r, *args, **kwargs):
    r.hook_called = True
    return r

高级响应结果处理

[编辑 | 编辑源代码]

任何请求所得的回应,都是一个 request.Response 对象。本章节将讲解该对象的方法。

响应结果的标头

[编辑 | 编辑源代码]

读者可以通过以下方式,读取网站返回时使用的 标头

r = requests.get(url)
print(r.headers)

标头会以字典形式返回。

响应结果的 cookies

[编辑 | 编辑源代码]

读者可以通过以下方式,读取网站返回的 cookies。

r = requests.get(url)
print(r.cookies)

此方法返回的是一个 requests.cookies.RequestsCookieJar 实例,该实例有以下方法:

响应延时

[编辑 | 编辑源代码]
r = requests.get(url)
print(r.elapsed)

响应延时是发送请求到收到响应之间的时间间隔,为一个 datetime.timedelta 对象。

重定向历史记录

[编辑 | 编辑源代码]
r = requests.get(url)
print(r.history)

在进行请求时,requests 模块会自动进行重定向。通过这种方法可以获得重定向的历史记录列表,列表的元素为每次重定向时的 request.Response 对象。

HTTP 状态码

[编辑 | 编辑源代码]
r = requests.get(url)
print(r.status_code) #状态码,比如 404、200 等
print(r.reason)      #状态字符串,比如"Not Found"等

会话(Session)是一种高级对象。它会自动保存网站所发送的 cookie 并在下一次请求时向对应的网站提供。它同时还提供了连接池和连接配置。

创建 Session 和使用

[编辑 | 编辑源代码]

以下程序创建了一个 Session,并用该 Session 发送了一个 GET 请求:

import requests
s = requests.Session()
s.get('https://httpbin.org/get')

也可以用上下文管理器的方式来使用 Session:

import requests
with requests.Session() as s:
    s.get('https://httpbin.org/get')

Session 支持上面列出的一切请求方式。使用 Session 发送请求时所使用的“方法名称”和“方法参数”,和使用上面列出的函数来发送请求时的“函数名称”和“函数参数”是一样的。

Session 包含的设置

[编辑 | 编辑源代码]

对于经过以下代码所创建的 Session:

import requests
session = requests.Session()

该对象在进行请求时,会自动在请求中输入各种参数。以下是参数的列表:

属性 说明 备注
session.cookies cookie 一个 RequestsCookieJar 实例,和上面 #cookies 章节一致。
可以修改成其它的、cookielib.CookieJar 的子类型的实例。
session.auth 身份验证信息 和上面 #身份验证信息 章节一致。
session.verify 服务器验证 和上面 #服务器验证 章节一致。
session.cert 客户端验证 和上面 #客户端验证 章节一致。
session.headers 标头信息 和上面 #请求的标头 章节一致。
session.hooks 钩子 和上面 #请求的钩子 章节一致。
session.proxies 代理服务器 和上面 #代理服务器 章节一致。
session.stream 和上面 #流 章节一致。
session.max_redirects 最大重定向次数 可以设置重定向的最大次数,默认为 30。
session.params 请求参数 在请求时会被自动加入的,所有参数的字典。

除上述属性之外,requests.Sessions 还有一些其它的方法,详情参见文档。