Python爬虫(二)- Requests 高级使用教程
文章目录
- 前言
- 一、Session 对象
- 1. 简介
- 2. 跨请求保持 Cookie
- 3. 设置缺省数据
- 4. 方法级别参数不被跨请求保持
- 5. 会话作为上下文管理器
- 6. 移除字典参数中的值
- 二、请求与响应
- 1. 请求与响应对象
- 1.1 获取响应头信息
- 1.2 获取发送到服务器的请求头信息
- 三、SSL 证书验证
- 1. 忽略 SSL 证书验证
- 四、流式上传和请求
- 1. 流式上传
- 2. 流式请求
- 2.1 流式请求和普通请求的区别
- 2.2 示例
- 2.2.1 iter_lines() 使用
- 2.2.2 iter_content() 使用
- 五、代理
- 1. 获取免费代理地址
- 2. 使用代理配置单个请求
- 3. 使用代理池
- 4. 使用 HTTP Basic Auth 的代理
- 5. 针对特定主机或连接方式设置代理
- 六、编码方式
- 1. 编码方式处理
- 1.1 自动检测编码
- 1.2 手动设置编码
- 1.3 使用原始字节数据
- 七、身份认证
- 1. 基本身份认证 (HTTP Basic Auth)
前言
在现代网络应用中,HTTP 请求和响应的处理是至关重要的。Python 的 requests 库以其简洁易用的接口,成为了开发者进行网络请求的首选工具。无论是简单的 GET 请求,还是复杂的身份认证、会话管理、代理设置,requests 都能轻松应对。本文将深入探讨 requests 库的各个方面,包括 Session 对象的使用、请求与响应的处理、SSL 证书验证、流式上传和请求、代理配置、编码方式处理以及身份认证等。
一、Session 对象
1. 简介
Session 对象允许跨多个请求保持某些参数不变。它在同一个 Session 实例发出的所有请求之间保持 cookie,并利用 urllib3 的连接池功能来重用底层的 TCP 连接,从而为向同一主机发送的多个请求提供显著的性能提升。
主要特性:
- 所有 Requests API 方法:Session 对象拥有主要的 Requests API 的所有方法。
- 持久性 Cookie 和 Headers:可以跨请求保持某些数据,如认证信息和自定义头部。
- 连接池复用:对于同一主机的多次请求,能够复用TCP连接,提高效率。
2. 跨请求保持 Cookie
import requests
# 创建一个会话对象
session = requests.session()
# 设置 Cookie:通过会话发送 GET 请求,设置一个名为 sessioncookie 的 cookie,值为 123456789。
session.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
# 获取 Cookie:再次使用同一个会话发送 GET 请求,获取当前会话中的所有 cookies。
response = session.get("http://httpbin.org/cookies")
# 打印服务器返回的 cookie 信息
print(response.text)
打印的结果为:
{
"cookies": {
"sessioncookie": "123456789"
}
}
3. 设置缺省数据
通过为 Session 对象的属性提供数据,可以为请求方法提供缺省数据,例如认证信息或头部信息。
import requests
# 创建一个会话对象
session = requests.session()
# 设置会话的认证信息,用户名为 'user',密码为 'pass'
session.auth = ('user', 'pass')
# 更新会话头部信息,添加自定义头 'x-test',其值为 'true'
session.headers.update({'x-test': 'true'})
# 发送 GET 请求到 http://httpbin.org/headers,同时在此请求中添加另一个自定义头 'x-test2'
response = session.get('http://httpbin.org/headers', headers={'x-test2': 'true'})
# 打印服务器返回的响应内容,以查看请求头和其他信息
print(response.text)
打印的结果为:
任何传递给请求方法的字典都会与已设置会话层数据合并,方法层的参数覆盖会话的参数。
4. 方法级别参数不被跨请求保持
就算使用了 Session,方法级别的参数也不会被跨请求保持。
import requests
# 创建一个会话对象
session = requests.session()
# 只有第一个请求发送 cookie
response = session.get('http://httpbin.org/cookies', cookies={'from-my': 'browser'})
print(response.text)
# 第二个请求没有 cookie
response = session.get('http://httpbin.org/cookies')
print(response.text)
打印的结果为:
{
"cookies": {
"from-my": "browser"
}
}
{
"cookies": {}
}
从打印的结果可以看出,重复对同一个地址发起访问,第一个请求设置的 cookie 并没有被保存,所以第二个请求中没有 cookie,也就是说就算使用了 Session,方法级别的参数也不会被跨请求保持。
5. 会话作为上下文管理器
为了确保会话在完成后能被正确关闭,可以使用它作为上下文管理器。这样就能确保 with 区块退出后会话能被关闭,即使发生了异常也一样。
import requests
with requests.session() as session:
response = session.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
print(response.text)
打印的结果为:
{
"cookies": {
"sessioncookie": "123456789"
}
}
6. 移除字典参数中的值
可以通过在方法级别的参数中将键的值设置为 None
来移除会话层字典参数中的某个键,从而让该键不会被发送。
二、请求与响应
1. 请求与响应对象
任何时候当你调用 requests.get()
或其他类似方法时,实际上你在执行两个主要操作:构建一个 Request
对象以向服务器请求或查询资源;以及接收一个从服务器返回的 Response
对象。这个 Response
对象包含了来自服务器的所有信息,并且也包含了你最初创建的 Request
对象。
1.1 获取响应头信息
import requests
url = 'http://www.baidu.com'
response = requests.get(url)
print(response.headers)
打印的结果为:
1.2 获取发送到服务器的请求头信息
import requests
url = 'http://www.baidu.com'
response = requests.get(url)
print(response.request.headers)
打印的结果为:
{'User-Agent': 'python-requests/2.31.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
三、SSL 证书验证
Requests 可以为 HTTPS 请求验证 SSL 证书,就像 web 浏览器一样。SSL 验证默认是开启的,如果证书验证失败,Requests 会抛出 SSLError
。
1. 忽略 SSL 证书验证
在某些特殊情况下,比如在测试环境中或者你信任但证书不受信任的情况下,你可能想要忽略 SSL 证书验证。
import requests
from urllib3.exceptions import InsecureRequestWarning
url = 'https://example.com'
# 禁用不安全请求警告
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
# 忽略 SSL 证书验证
response = requests.get(url, verify=False)
print(response.status_code)
四、流式上传和请求
1. 流式上传
Requests支持流式上传,这允许你发送大的数据流或文件而无需先把它们读入内存。要使用流式上传,仅需为你的请求体提供一个类文件对象即可。
with open('massive-body', 'rb') as f:
requests.post('http://some.url/streamed', data=f)
data=f
:这里将文件对象 f
直接赋值给了 data
参数。这意味着 requests
将会逐块读取文件内容,并通过 HTTP 请求进行流式传输。这样做的好处是,即使文件非常大,也不会占用过多的内存,因为数据是一部分一部分地发送出去的。
2. 流式请求
2.1 流式请求和普通请求的区别
流式请求(Streaming Requests)和普通请求(Non-streaming Requests)是使用 requests
库进行 HTTP 请求的两种不同方式,它们在处理响应内容时有着显著的区别。下面我们将详细介绍这两种请求类型,并说明其适用场景。
普通请求
-
一次性读取:当发起一个普通的 GET 或 POST 请求时,
requests
会立即将整个响应体下载到内存中。这意味着如果响应体很大,可能会占用大量的内存。 -
简单易用:对于小文件或轻量级的数据交换,这种方式非常方便,因为它不需要额外的配置就可以直接获取完整的响应内容。
流式请求
-
逐块读取:通过设置参数
stream=True
,你可以告诉requests
不要立即下载整个响应体。相反,它会在需要时逐步从网络流中读取数据。这允许你处理超大文件或长时间运行的数据流,而不会一次性占用大量内存。 -
节省资源:流式请求非常适合于处理大型文件、长连接 API 响应或者实时数据流,因为你可以一边接收数据一边处理,从而有效管理资源。
-
更复杂的逻辑:由于数据是分块到达的,你需要编写额外的代码来处理这些数据块,例如将它们写入文件或进行增量处理。
流式请求迭代器方法
-
iter_content()
:以字节为单位返回响应内容的迭代器。可以指定chunk_size
参数来控制每次读取的大小。 -
iter_lines()
:以行为单位返回响应内容的迭代器,适用于按行处理的文本响应。
2.2 示例
2.2.1 iter_lines() 使用
import json
import requests
# 发送 GET 请求到指定的 URL,使用 stream=True 以便逐行读取响应内容
response = requests.get('http://httpbin.org/stream/20', stream=True)
# 检查响应的编码,如果未设置,则将其设置为 'utf-8'
if response.encoding is None:
response.encoding = 'utf-8'
# 逐行迭代响应内容,使用 decode_unicode=True 将内容解码为字符串
for line in response.iter_lines(decode_unicode=True):
# 检查当前行是否非空
if line:
# 将 JSON 格式的字符串解析为 Python 对象并打印
print(json.loads(line))
iter_lines
不保证重进入时的安全性。多次调用该方法 会导致部分收到的数据丢失。如果要在多处调用它,应该使用生成的迭代器对象。
import json
import requests
# 发送 GET 请求到指定的 URL,使用 stream=True 以便逐行读取响应内容
response = requests.get('http://httpbin.org/stream/20', stream=True)
# 检查响应的编码,如果未设置,则将其设置为 'utf-8'
if response.encoding is None:
response.encoding = 'utf-8'
# 使用 iter_lines() 方法创建一个可迭代的行生成器
lines = response.iter_lines()
# 使用 next() 函数获取并跳过第一行,first_line 存储的是第一行的内容
first_line = next(lines)
# 逐行处理剩余的响应内容
for line in lines:
# 打印当前行的内容
print(json.loads(line))
2.2.2 iter_content() 使用
import requests
# 定义要请求的 URL
url = 'https://api.github.com/events'
# 发送 GET 请求到指定的 URL,使用 stream=True 以便逐块下载内容
response = requests.get(url=url, stream=True)
# 打开一个文件以写入二进制数据,路径为 'D:\\demo.txt'
with open('D:\\demo.txt', 'wb') as fd:
# 遍历响应内容,按块读取数据,块大小为 8192 字节
for chunk in response.iter_content(chunk_size=8192):
# 将读取的块写入文件
fd.write(chunk)
五、代理
1. 获取免费代理地址
点击连接访问快代理网站:https://www.kuaidaili.com/free/inha/
如下图所示,可以获取免费的代理。
2. 使用代理配置单个请求
如果需要使用代理,可以通过为任意请求方法提供 proxies
参数来配置单个请求。
import requests
url = 'http://www.baidu.com'
proxies = {
"http": "47.119.22.92:8008"
}
# 发送 GET 请求,并使用指定的代理
response = requests.get(url=url, proxies=proxies)
print(response.status_code)
打印的结果为:
200
3. 使用代理池
在进行网络请求时,特别是当你需要频繁地访问某个网站或希望隐藏自己的真实 IP 地址时,使用代理池可以提高请求的成功率和匿名性。通过每次发送请求时从多个代理服务器中随机选择一个来发送,可以分散请求源,减少被目标网站封禁的风险。
import random
import requests
url = 'http://www.baidu.com'
# 使用代理池
proxies_pool = [
{'http': '47.122.65.254:8080'},
{'http': '8.130.34.44:8800'},
{'http': '47.121.183.107:8443'},
{'http': '111.1.61.50:3128'},
{'http': '47.119.164.33:3128'}
]
proxies = random.choice(proxies_pool)
# 发送 GET 请求,并使用指定的代理
response = requests.get(url=url, proxies=proxies)
print(response.status_code)
打印的结果为:
200
4. 使用 HTTP Basic Auth 的代理
如果代理需要使用 HTTP Basic Auth,可以使用 username:password@host
的语法来配置代理。
# 定义代理字典,包含用户名和密码
proxies = {
"http": "username:password@10.10.1.10:3128",
}
5. 针对特定主机或连接方式设置代理
要为某个特定的连接方式或主机设置代理,可以使用 scheme://hostname
作为键。这种方式将针对指定的主机和连接方式进行匹配。
import requests
url = 'http://www.baidu.com'
# 针对特定主机设置代理
proxies = {
"http://127.0.0.1": "http://47.121.183.107:8443"
}
# 发送 GET 请求,并使用指定的代理
response = requests.get(url=url, proxies=proxies)
print(response.status_code)
打印的结果为:
200
六、编码方式
1. 编码方式处理
当你收到一个 HTTP 响应时,requests
库会猜测响应内容的编码方式,以便在你调用 Response.text
方法时能够正确解码响应内容。以下是 requests
处理编码方式的具体机制。
1.1 自动检测编码
-
优先检查 HTTP 头部:
requests
首先会在 HTTP 响应头部查找Content-Type
字段中的字符集声明。如果存在明确指定的字符集(例如charset=UTF-8
),则使用该字符集进行解码。 -
使用 charade 猜测编码:如果 HTTP 头部没有明确指定字符集,并且
Content-Type
包含text
类型,则requests
会使用内置的chardet
库(原名为charade
)来推测最可能的编码方式。 -
默认编码:根据 RFC 2616 的规定,当
Content-Type
是text
类型但没有指定字符集时,默认字符集应该是ISO-8859-1
。requests
遵循这一规范并默认采用ISO-8859-1
进行解码。
1.2 手动设置编码
如果你知道正确的编码方式并且希望覆盖 requests
的自动检测结果,可以通过直接设置 Response.encoding
属性来实现。
import requests
url = 'https://api.github.com/events'
response = requests.get(url=url)
# 查看推测的文本编码
print("自动推测的编码方式:" + response.encoding)
# 手动指定编码为 ISO-8859-1
response.encoding = 'ISO-8859-1'
# 打印编码方式
print("手动指定的编码方式:" + response.encoding)
打印的结果为:
自动推测的编码方式:utf-8
手动指定的编码方式:ISO-8859-1
1.3 使用原始字节数据
如果你不确定编码或者不想依赖于 requests
的自动解码功能,可以直接访问 Response.content
属性以获取未经解码的原始字节数据。然后根据需要自行解码。
import requests
url = 'https://api.github.com/events'
response = requests.get(url=url)
# 获取未经解码的原始字节数据,返回的是服务器返回的二进制内容
raw_data = response.content
# 使用 UTF-8 编码方式对原始字节数据进行解码,转换为字符串
decoded_text = raw_data.decode('utf-8')
print(decoded_text)
七、身份认证
1. 基本身份认证 (HTTP Basic Auth)
HTTP Basic Authentication 是一种简单的认证机制,广泛用于需要认证的 Web 服务中。requests
库对这种认证方式提供了直接的支持。
可以通过导入 HTTPBasicAuth
类来使用 HTTP Basic Auth。
import requests
from requests.auth import HTTPBasicAuth
url = 'https://api.github.com/user'
auth = HTTPBasicAuth('user', 'pass')
# 发送带有基本身份认证的 GET 请求
response = requests.get(url=url, auth=auth)
print(response.status_code)
简写形式:由于 HTTP Basic Auth 非常常见,requests
提供了一种更为简洁的方式来传递认证信息——直接在 auth
参数中提供一个元组。
import requests
url = 'https://api.github.com/user'
# 发送带有基本身份认证的 GET 请求
response = requests.get(url=url, auth=('user', 'pass'))
print(response.status_code)
参考链接:
- requests 官方文档:http://cn.python-requests.org/zh_CN/latest/