requests 库 - HTTP for Humans
# requests 库 - HTTP for Humans
在爬虫的世界里,第一步就是模拟浏览器,向目标网站的服务器发送一个“请求”(Request),然后接收服务器返回的“响应”(Response)。Python 的 requests
库就是为此而生的神器。它极大地简化了发送 HTTP 请求的过程,让复杂的网络交互变得像调用一个普通函数一样简单。
# 1. 安装 requests 库
requests
是一个第三方库,需要通过 pip
进行安装。打开你的终端或命令行,输入以下命令:
pip install requests
如果你使用的是 conda
环境,也可以使用:
conda install requests
安装成功后,你就可以在 Python 脚本中通过 import requests
来使用它了。
# 2. 发送你的第一个 GET 请求
GET 是最常见的 HTTP 请求方法,通常用于向服务器索取数据。当你访问一个网页时,浏览器就是在发送 GET 请求。
import requests
# 1. 定义你要抓取的网页 URL
url = "https://www.baidu.com"
# 2. 使用 requests.get() 方法发送一个 GET 请求
# 这个调用会返回一个 Response 对象,我们通常用变量 response 来接收
response = requests.get(url)
# 3. response 对象包含了服务器返回的所有信息
# 我们来检查一下它的几个核心属性
# 3.1 状态码 (Status Code)
# 状态码是服务器对请求的响应结果的数字代码。
# 200 表示请求成功。404 表示未找到。500 表示服务器内部错误。
print(f"状态码: {response.status_code}")
# 3.2 响应内容的编码格式
# requests 会猜测响应内容的编码格式,但有时会猜错,导致乱码。
print(f"推测的编码: {response.encoding}")
# 3.3 手动设置正确的编码 (防止乱码的关键)
# 如果发现 response.text 乱码,可以手动将其设置为服务器实际使用的编码,
# 通常是 'utf-8'。
response.encoding = 'utf-8'
print(f"手动设置编码后: {response.encoding}")
# 3.4 响应头 (Headers)
# 响应头是服务器返回的一些元数据,例如内容类型、服务器软件等。
# 它是一个类似字典的对象。
print(f"响应头: {response.headers}")
print(f"响应头中的内容类型: {response.headers['Content-Type']}")
# 3.5 响应体 (Response Body) - 网页的实际内容
# .text 属性会以文本形式返回响应内容,requests 会根据 encoding 属性解码。
# 这通常就是我们看到的网页 HTML 源代码。
print(response.text)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 什么是 Response 对象?
当你执行 requests.get()
或其他请求方法时,你得到的不是一个简单的字符串,而是一个功能强大的 Response
对象。这个对象就像一个包裹,里面装满了服务器返回的所有信息:
status_code
: 数字状态码。headers
: 响应头,一个不区分大小写的字典。encoding
: 响应的编码格式。text
: 解码后的响应体文本。content
: 原始的、未解码的响应体(字节流)。json()
: 如果响应是 JSON 格式,可以用这个方法直接将其解码为 Python 字典。
# 3. 在 URL 中传递参数
很多时候,你需要向 URL 传递一些参数,例如搜索关键词、页码等。比如 https://www.baidu.com/s?wd=python
。
虽然你可以手动拼接字符串,但 requests
提供了更优雅、更安全的方式:使用 params
参数。
import requests
# 基础 URL
base_url = "https://www.baidu.com/s"
# 1. 将所有 URL 参数组织成一个字典
# requests 会自动帮你进行 URL 编码,无需担心特殊字符
params = {
'wd': 'Python 爬虫',
'ie': 'utf-8'
}
# 2. 在 get 请求中传入 params 字典
response = requests.get(base_url, params=params)
# 3. .url 属性可以查看 requests 最终拼接生成的完整 URL
print(f"最终请求的 URL: {response.url}")
# 4. 打印响应内容,这里会是百度搜索“Python 爬虫”的结果页面
# response.encoding = 'utf-8' # 百度页面可能需要设置
# print(response.text)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
使用 params
的好处:
- 代码更清晰: 将 URL 和参数分离,易于阅读和修改。
- 自动编码:
requests
会妥善处理各种特殊字符(如空格、中文等),避免了手动 URL 编码的麻烦和错误。
# 4. 获取不同的响应内容
根据服务器返回的数据类型,我们可以用不同的方式来获取响应体。
response.text
: 获取文本内容。这是最常用的方式,适用于 HTML、XML、普通文本等。requests
会根据response.encoding
来解码。response.content
: 获取字节内容。适用于需要下载图片、视频、PDF 等二进制文件。它返回的是原始的bytes
数据。response.json()
: 获取 JSON 内容。如果服务器返回的是 JSON 格式的数据(这在 API 接口中非常常见),这个方法会直接将其解析成 Python 的字典或列表。
# 4.1 下载图片(二进制内容)
import requests
# 图片的 URL
image_url = "https://www.python.org/static/community_logos/python-logo-master-v3-TM.png"
response = requests.get(image_url)
# 1. 检查请求是否成功
if response.status_code == 200:
# 2. 使用 .content 获取二进制数据
image_data = response.content
# 3. 以二进制写入('wb')模式打开一个文件,并将图片数据写入
with open("python_logo.png", "wb") as f:
f.write(image_data)
print("图片下载成功!")
else:
print(f"下载失败,状态码: {response.status_code}")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 4.2 解析 JSON 数据(API 交互)
import requests
# 一个返回 JSON 数据的测试 API
api_url = "http://httpbin.org/get"
response = requests.get(api_url, params={'name': 'scholar', 'age': 18})
# 1. 检查响应头,确认内容是 JSON
print(f"Content-Type: {response.headers['Content-Type']}")
# 2. 如果是 JSON,直接调用 .json() 方法
if 'application/json' in response.headers.get('Content-Type', ''):
# .json() 会自动解码并返回一个 Python 字典
data = response.json()
# 3. 像操作普通字典一样操作数据
print(type(data))
print(data)
print(f"请求来源 IP: {data['origin']}")
print(f"传递的参数: {data['args']}")
else:
print("响应不是 JSON 格式")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 5. 自定义请求头 (Headers)
当你向网站发送请求时,你的请求中会包含一个“请求头”(Request Headers),它告诉服务器关于你的一些信息,比如你使用的浏览器类型、你接受什么样的数据等。
有些网站会检查请求头中的 User-Agent
字段,如果发现它不是来自一个正常的浏览器,就可能会拒绝你的请求。因此,伪装 User-Agent
是爬虫的基本功。
import requests
url = "https://www.zhihu.com" # 知乎对 User-Agent 有较强的校验
# 1. 定义一个字典来存放请求头
# 最关键的是 'User-Agent'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
# 2. 在请求中通过 headers 参数传入
# 这样,requests 发送的请求就会带上你指定的 User-Agent
response = requests.get(url, headers=headers)
# 即使有 User-Agent,知乎也可能返回非 200 状态码,但至少请求是模仿浏览器发出的
print(f"状态码: {response.status_code}")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 6. 发送 POST 请求
与 GET 请求(索取数据)不同,POST 请求通常用于向服务器提交数据,例如用户登录、提交表单等。
# 6.1 提交表单数据 (data
参数)
当你在网页上填写一个表单并点击“提交”时,浏览器通常会发送一个 POST 请求,并将表单中的数据放在请求体中。requests
使用 data
参数来模拟这个过程。
import requests
# httpbin.org 是一个很好的测试网站
post_url = "http://httpbin.org/post"
# 1. 准备要提交的表单数据,格式为字典
form_data = {
'username': 'scholar',
'password': 'mysecretpassword'
}
# 2. 使用 requests.post() 方法,并通过 data 参数提交
response = requests.post(post_url, data=form_data)
# 3. 查看响应结果,httpbin 会将你提交的数据原样返回
json_response = response.json()
print(f"提交的表单数据: {json_response['form']}")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 6.2 提交 JSON 数据 (json
参数)
对于现代的 Web API,更常见的做法是直接在请求体中提交 JSON 格式的数据。requests
对此有专门的 json
参数支持。
import requests
post_url = "http://httpbin.org/post"
# 1. 准备要提交的 JSON 数据,也是一个 Python 字典
json_data = {
'user_id': 123,
'post_content': '这是我的第一篇帖子',
'tags': ['python', 'requests']
}
# 2. 使用 json 参数提交
# requests 会自动:
# a. 将 Python 字典序列化为 JSON 字符串。
# b. 设置请求头的 'Content-Type' 为 'application/json'。
response = requests.post(post_url, json=json_data)
# 3. 查看响应结果
json_response = response.json()
# 在响应的 'data' 字段可以看到序列化后的 JSON 字符串
print(f"提交的 JSON 原始数据: {json_response['data']}")
# 在响应的 'json' 字段可以看到被解析回来的 Python 字典
print(f"服务器解析后的 JSON 数据: {json_response['json']}")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
data
vs json
参数总结:
data
: 用于提交传统的application/x-www-form-urlencoded
表单数据。json
: 用于提交application/json
数据,更现代、更常用。requests
会帮你自动处理序列化和请求头。
# 7. 超时 (Timeout)
网络请求有时会因为服务器响应慢或网络问题而卡住。为了防止你的程序无限期地等待下去,设置一个超时时间非常重要。
import requests
from requests.exceptions import Timeout
url = "http://httpbin.org/delay/5" # 这个 URL 会延迟 5 秒后才响应
try:
# 设置超时为 3 秒。如果 3 秒内服务器没有响应,就会抛出 Timeout 异常
response = requests.get(url, timeout=3)
print("请求成功!")
except Timeout:
print("请求超时!程序不会被卡住。")
2
3
4
5
6
7
8
9
10
11
12
timeout
参数可以是一个数字(连接和读取的总超时),也可以是一个元组 (connect_timeout, read_timeout)
分别设置。
# 8. 异常处理
健壮的爬虫必须能处理各种网络异常。requests
库的常见异常都继承自 requests.exceptions.RequestException
。
import requests
from requests.exceptions import RequestException, ConnectionError, HTTPError, Timeout
urls_to_test = [
'http://httpbin.org/status/404', # 一个会返回 404 的 URL
'http://invalid.domain.that.does.not.exist', # 一个无效的域名
'http://httpbin.org/delay/5' # 一个会超时的 URL
]
for url in urls_to_test:
try:
response = requests.get(url, timeout=3)
# 检查 HTTP 错误状态码 (如 4xx 或 5xx)
# 如果状态码不是 2xx,.raise_for_status() 会抛出 HTTPError
response.raise_for_status()
except Timeout:
print(f"请求 {url} 超时了。")
except HTTPError as http_err:
print(f"请求 {url} 发生 HTTP 错误: {http_err}")
except ConnectionError as conn_err:
print(f"请求 {url} 发生连接错误: {conn_err}")
except RequestException as err:
print(f"请求 {url} 发生未知错误: {err}")
else:
print(f"请求 {url} 成功!状态码: {response.status_code}")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
核心实践:
- 总是将你的请求代码放在
try...except
块中。 - 至少捕获
requests.exceptions.RequestException
这个基类异常。 - 使用
response.raise_for_status()
是一个检查请求是否“业务上”成功的好习惯。
# 9. 会话对象 (Session)
如果你需要向同一个网站发送多次请求(例如,先登录,再访问个人主页),使用 requests.Session()
对象会带来两个巨大的好处:
- 自动保持 Cookies: Session 对象会自动管理 Cookies。如果你通过 Session 登录了一个网站,后续通过同一个 Session 发出的所有请求都会自动带上登录后的 Cookie,无需你手动处理。
- 性能提升: Session 会保持底层的 TCP 连接,当你对同一个主机进行多次请求时,它会重用这个连接,而不是每次都重新建立,从而大大提高了效率。
import requests
# 1. 创建一个 Session 对象
session = requests.Session()
# 2. 伪装请求头,可以在 Session 级别设置
# 之后这个 Session 发出的所有请求都会使用这个头
session.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
})
# 3. 使用 Session 对象发出第一个请求
# httpbin.org/cookies/set 会在响应中设置一个名为 'sample' 的 cookie
session.get('http://httpbin.org/cookies/set/sample/123456789')
# 4. 再次使用同一个 Session 发出请求
# 这次请求会自动带上之前服务器设置的 'sample' cookie
response = session.get('http://httpbin.org/cookies')
# 5. 查看响应,你会发现服务器收到了我们之前设置的 cookie
print(response.json()) # 输出: {'cookies': {'sample': '123456789'}}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
结论: 当你需要和网站进行多次交互时(特别是需要登录的场景),始终优先使用 requests.Session
对象。
# 10. 使用代理 (Proxies)
当你在短时间内对同一个网站进行大量请求时,你的 IP 地址很容易被服务器识别并封禁。为了避免这种情况,以及隐藏自己的真实 IP,使用代理服务器是一个非常常见的反爬策略。
requests
库可以非常方便地配置代理。
# 10.1 HTTP 和 HTTPS 代理
你需要准备一个代理服务器的地址和端口,然后将其组织成一个字典,通过 proxies
参数传递给请求方法。
import requests
# 目标网站,httpbin.org/ip 会返回你请求的来源 IP 地址
url = "http://httpbin.org/ip"
# 1. 代理服务器的地址和端口
# 格式: '协议': 'http://IP地址:端口号'
# 注意:即使代理服务器本身是 HTTP 的,它也可以代理 HTTPS 请求。
proxy_ip = "127.0.0.1:8888" # 这是一个示例,请替换成你自己的有效代理 IP
proxies = {
'http': f'http://{proxy_ip}',
'https': f'http://{proxy_ip}', # 也可以是 https://IP:PORT
}
try:
# 2. 在请求中传入 proxies 参数
response = requests.get(url, proxies=proxies, timeout=5)
# 3. 查看返回的 IP 地址,如果代理有效,这里应该是代理服务器的 IP
print("使用了代理的响应:")
print(response.json())
except requests.exceptions.ProxyError:
print("代理连接失败!请检查代理服务器是否可用。")
except Exception as e:
print(f"发生错误: {e}")
# 作为对比,我们再发一个不使用代理的请求
print("\n未使用代理的响应:")
response_no_proxy = requests.get(url)
print(response_no_proxy.json())
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 10.2 需要认证的代理
很多付费代理服务需要提供用户名和密码进行认证。你可以将认证信息直接包含在代理 URL 中。
import requests
# 格式: http://用户名:密码@IP地址:端口
proxy_with_auth = "http://user:password@127.0.0.1:8888"
proxies = {
'http': proxy_with_auth,
'https': proxy_with_auth,
}
# response = requests.get("http://example.com", proxies=proxies)
2
3
4
5
6
7
8
9
10
11
# 10.3 SOCKS 代理
requests
默认不支持 SOCKS 协议的代理。但你可以通过安装一个额外的库 PySocks
来轻松启用它。
首先,安装
PySocks
:pip install pysocks
1然后,在代理字典中使用
socks5
或socks5h
作为协议:socks5
: 代理服务器在本地解析域名。socks5h
: 域名解析在代理服务器端进行(h 代表 hostname)。
import requests
# SOCKS5 代理地址
socks_proxy = "127.0.0.1:1080" # 示例 SOCKS5 代理
proxies = {
'http': f'socks5h://{socks_proxy}',
'https': f'socks5h://{socks_proxy}',
}
try:
# 安装 PySocks 后,requests 会自动识别并使用 SOCKS 代理
response = requests.get('https://httpbin.org/ip', proxies=proxies)
print("通过 SOCKS5 代理的响应:")
print(response.json())
except Exception as e:
print(f"SOCKS 代理请求失败: {e}")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
通过合理使用代理,你可以大大提高爬虫的稳定性和成功率,是进阶爬虫工程师必备的技能。