【Python】Requests:请求发送

requests 是一个简洁易用的 Python 库,用于发送 HTTP 请求。它支持多种 HTTP 方法,并且在处理响应、会话保持、超时和重试等方面提供了强大的功能。本文将带你逐步了解如何使用 requests 库,并通过实例掌握其基本用法。

发送 HTTP 请求

常见的 HTTP 请求方法

requests 库支持六种主要的 HTTP 请求方法,以下是每种请求方法的基本用法示例:

python 复制代码
import requests

# GET 请求: 从服务器获取数据
response = requests.get('https://example.com')

# POST 请求: 向服务器提交数据
response = requests.post('https://example.com', data={'key': 'value'})

# PUT 请求: 更新服务器上的资源
response = requests.put('https://example.com/resource/1', data={'key': 'new_value'})

# DELETE 请求: 删除服务器上的资源
response = requests.delete('https://example.com/resource/1')

# HEAD 请求: 获取响应的头部信息,不返回响应体
response = requests.head('https://example.com')

# OPTIONS 请求: 获取目标资源的通信选项
response = requests.options('https://example.com')

在每个请求方法中,唯一的必选参数是 url,即请求的目标地址。你还可以通过 paramsdatajson 等参数向服务器发送数据:

  • params: 将参数添加到 URL 查询字符串中,适用于 GET 请求。
  • data: 用于发送表单数据,适用于 POST、PUT 请求。
  • json: 用于发送 JSON 格式的数据。

通用的 HTTP 请求方法

requests.request 是一个通用接口,用于发送任意类型的 HTTP 请求。你可以通过它灵活地控制请求的各个方面。

python 复制代码
requests.request(method, url, **kwargs)
  • method : 请求方法,必须是字符串,如 'GET''POST' 等。
  • url: 请求的目标 URL。
  • params: 用于添加到 URL 的查询参数(字典或字节流)。
  • data: 要发送的数据(字典、字节流或文件对象)。
  • json: JSON 格式的数据,会自动序列化为 JSON。
  • headers: 自定义请求头(字典形式)。
  • cookies: 要发送的 cookies(字典形式)。
  • files : 文件上传(字典形式),例如 files={'file': open('test.txt', 'rb')}
  • auth : 处理认证(元组形式),例如 auth=('user', 'pass')
  • timeout: 设置超时时间(秒)。
  • allow_redirects : 是否允许重定向,默认 True
  • stream : 是否立即下载响应内容,默认 False

示例:

python 复制代码
import requests

response = requests.request(
    method='GET',
    url='https://api.github.com/repos/psf/requests',
    headers={'Accept': 'application/vnd.github.v3+json'},
    params={'state': 'open'}
)

print(response.json())

查看实际请求的 URL

requests 库中,response.request.urlrequests 响应对象的一个属性,返回了实际请求的 URL。这在调试和日志记录中非常有用,尤其是在处理重定向或多次请求时。

以下是一个示例,展示如何使用 response.request.url 来获取请求的 URL:

python 复制代码
import requests

response = requests.get('https://example.com')
print(f"请求的 URL: {response.request.url}")
redirect_response = requests.get('https://httpbin.org/redirect/1')

# 打印实际请求的 URL
print(f"原始请求的 URL: {redirect_response.request.url}")
print(f"响应的最终 URL: {redirect_response.url}")
  • response.request.url:

    • 作用: 提供发出请求时使用的 URL。
    • 用途: 有助于调试和验证实际请求的 URL,尤其在处理重定向和日志记录时非常有用。
  • response.url:

    • 作用: 提供响应的最终 URL。这在处理重定向请求时尤为重要,因为它显示了最终被访问的 URL。

处理服务器响应

每次发送请求后,都会收到一个 Response 对象。这个对象包含了服务器返回的各种信息:

python 复制代码
import requests

response = requests.get('https://api.github.com')

# 获取响应状态码
print(response.status_code)

# 获取响应内容(字符串形式)
print(response.text)

# 获取响应内容(JSON 形式)
print(response.json())

# 获取响应头
print(response.headers)

# 获取响应体的二进制内容
print(response.content)

响应内容解析

  • 状态码 : response.status_code 表示请求的结果,如 200 成功,404 未找到资源。
  • 响应体 : response.text 返回响应的文本内容,response.json() 将 JSON 内容解析为字典。
  • 响应头 : response.headers 包含服务器返回的头部信息。

请求错误处理

在使用 requests 库处理 HTTP 请求时,进行返回异常检查是确保程序稳健性的关键步骤。requests 库提供了一些内置的异常处理机制,你可以通过捕获这些异常来处理各种错误场景,例如网络问题、无效的 HTTP 响应等。

  • requests.exceptions.RequestException: 所有异常的基类。你可以捕获这个异常来处理所有与请求相关的错误。
  • requests.exceptions.HTTPError: 捕获 HTTP 错误,通常与响应的状态码相关(4xx 或 5xx)。
  • requests.exceptions.ConnectionError: 捕获网络连接相关的错误,例如 DNS 失败、拒绝连接等。
  • requests.exceptions.Timeout: 捕获请求超时错误,当请求超过设定的超时时间还没有响应时抛出。
  • requests.exceptions.TooManyRedirects: 捕获重定向次数过多的错误。

以下是一个基本的示例,演示如何在请求过程中捕获和处理异常:

python 复制代码
import requests

url = 'https://example.com'

try:
    response = requests.get(url, timeout=5)
    
    # 如果服务器返回的状态码不是 200 (OK),手动引发 HTTPError 异常
    response.raise_for_status()
    
    # 处理响应内容
    print(response.text)

except requests.exceptions.HTTPError as http_err:
    print(f'HTTP 错误发生: {http_err}')
except requests.exceptions.ConnectionError as conn_err:
    print(f'连接错误发生: {conn_err}')
except requests.exceptions.Timeout as timeout_err:
    print(f'请求超时: {timeout_err}')
except requests.exceptions.RequestException as req_err:
    print(f'发生错误: {req_err}')
  • response.raise_for_status() : 这个方法在 requests 库中非常有用。如果响应的状态码是 4xx 或 5xx,raise_for_status() 会引发 requests.exceptions.HTTPError 异常。因此,你可以通过这个方法自动捕获不成功的请求,并根据需要进行处理。

  • 捕获特定异常 : 通过分别捕获 HTTPErrorConnectionErrorTimeout 等特定异常,你可以根据错误类型执行不同的逻辑处理。例如,遇到连接错误时可能需要记录日志,而遇到超时错误时可以尝试重试请求。

  • RequestException : 作为所有 requests 异常的基类,RequestException 可以用来捕获任何与请求相关的错误。这在你不需要区分错误类型时非常有用。

响应的编码问题

当我们获取到 HTTP 响应时,服务器返回的文本内容可能使用了不同的编码格式。如果没有正确处理编码,可能会导致乱码或解析错误。

python 复制代码
import requests

response = requests.get('https://example.com')
print(response.text)  # 可能会出现乱码

requests 库会尝试根据响应头中的 Content-Type 自动检测编码,但有时服务器没有正确设置编码,导致检测失败。在这种情况下,可以手动设置编码。

python 复制代码
import requests

response = requests.get('https://example.com')

# 手动设置编码为 'utf-8'
response.encoding = 'utf-8'

# 现在输出的文本应该没有乱码
print(response.text)

apparent_encodingrequests.Response 对象的一个属性,使用 chardet 库来检测响应内容的编码。它与 encoding 属性不同,encoding 是基于 HTTP 响应头的 Content-Type 自动设置的编码,而 apparent_encoding 是通过实际的响应内容来推测的编码。

python 复制代码
import requests

url = 'https://example.com'
response = requests.get(url)

# 打印自动推测的编码和服务器提供的编码
print(f"服务器提供的编码: {response.encoding}")
print(f"推测的编码: {response.apparent_encoding}")

# 使用推测的编码
response.encoding = response.apparent_encoding

# 处理响应内容
print(response.text)

二进制资源的爬取和存储

发送 HTTP GET 请求

使用 requests 库发送 GET 请求以获取图片的内容。通过设置 stream=True,可以分块下载内容,这样适合处理大文件。

python 复制代码
import requests

# 图片的 URL
image_url = 'https://example.com/image.jpg'

# 发送 GET 请求获取图片内容
response = requests.get(image_url, stream=True)
  • requests.get(image_url, stream=True): 发送 HTTP GET 请求获取图片,stream=True 表示以流式方式下载响应内容。

检查请求是否成功

使用 raise_for_status() 方法检查请求是否成功,如果请求失败会抛出异常。

python 复制代码
# 检查请求是否成功
response.raise_for_status()
  • response.raise_for_status(): 如果响应状态码表示请求失败(例如 404 或 500),抛出异常,以便你可以处理错误情况。

保存资源到本地文件

将下载的图片内容写入到本地文件中,使用二进制模式打开文件,并逐块写入数据。

python 复制代码
# 本地文件路径
file_path = 'local_image.jpg'

# 以二进制模式打开文件并写入图片内容
with open(file_path, 'wb') as file:
    for chunk in response.iter_content(chunk_size=8192):
        file.write(chunk)

print(f"图片成功下载并保存到 {file_path}")
  • with open(file_path, 'wb') as file: 以二进制写入模式('wb')打开文件,确保在操作完成后文件被正确关闭。
  • response.iter_content(chunk_size=8192): 分块读取响应内容,每块大小为 8192 字节,以避免一次性加载整个文件。
  • file.write(chunk): 将每个数据块写入到本地文件中。

保持会话状态

通过 requests.Session,可以在多个请求之间共享参数、cookies 和 HTTP 连接,适用于需要保持登录状态的操作:

python 复制代码
import requests

# 创建会话对象
session = requests.Session()

# 设置公共的请求头
session.headers.update({'User-Agent': 'my-app/0.0.1'})

# 登录后发送请求
session.post('https://example.com/login', data={'username': 'user', 'password': 'pass'})
response = session.get('https://example.com/profile')

print(response.text)

处理超时和重试

在网络请求中,设置超时和实现自动重试机制可以提高程序的稳定性:

设置超时时间

通过 timeout 参数控制请求的最大等待时间:

python 复制代码
import requests

try:
    response = requests.get('https://httpbin.org/delay/5', timeout=3)
except requests.exceptions.Timeout:
    print('请求超时')

实现重试机制

可以使用 HTTPAdapterRetry 类为请求配置自动重试策略:

python 复制代码
import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry

session = requests.Session()

# 定义重试策略
retry = Retry(
    total=3,  # 总重试次数
    backoff_factor=0.3,  # 重试间隔时间
    status_forcelist=[500, 502, 503, 504],  # 需要重试的 HTTP 状态码
)

# 安装适配器
adapter = HTTPAdapter(max_retries=retry)
session.mount('http://', adapter)
session.mount('https://', adapter)

try:
    response = session.get('https://httpbin.org/status/500')
except requests.exceptions.RetryError:
    print('多次重试失败')

伪装浏览器访问

为了使服务器更难察觉你在使用爬虫抓取内容,你可以通过修改和配置以下参数和方法,使得你的爬虫行为更加类似于普通用户的浏览器访问。需要注意的是,尽管这些方法可以帮助你降低被识别的风险,但你仍然应该遵守网站的 robots.txt 规则和服务条款,以避免法律和道德问题。

修改 User-Agent

User-Agent 是 HTTP 请求头的一部分,用于标识发出请求的客户端(如浏览器)。大多数网站会根据 User-Agent 来识别请求是否来自爬虫。因此,设置一个常见的浏览器 User-Agent 可以降低被识别的风险。

python 复制代码
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36'
}

response = requests.get('https://www.amazon.cn/gp/product/B01M8L5Z3Y', headers=headers)

设置请求间隔和随机延迟

爬虫通常会快速发送多个请求,而普通用户的浏览行为相对较慢。你可以通过在请求之间设置延迟,并随机化这个延迟时间,来模仿人类用户的行为。

python 复制代码
import time
import random

delay = random.uniform(1, 5)  # 随机延迟1到5秒之间
time.sleep(delay)

使用 Session 保持会话

使用 requests.Session 可以在多个请求之间保持会话,这包括了自动处理 cookies。这使得你的请求行为更接近于一个持续浏览的用户,而不是单个独立的请求。

python 复制代码
session = requests.Session()
session.headers.update({'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'})

response = session.get('https://example.com')

添加 Referer 和 Accept-Language 等请求头

除了 User-Agent 外,添加其他常见的 HTTP 头也可以帮助模拟真实的浏览器请求。这些头部字段包括 Referer(指示从哪里来),Accept-Language(指示用户语言偏好),Accept-Encoding(指示可接受的压缩格式)等。

python 复制代码
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
    'Referer': 'https://google.com',
    'Accept-Language': 'en-US,en;q=0.9',
    'Accept-Encoding': 'gzip, deflate, br'
}

response = requests.get('https://example.com', headers=headers)

使用代理服务器

通过代理服务器,你可以改变请求的来源 IP 地址,从而避免被服务器检测到大量请求来自同一 IP 地址。许多网站会限制来自单一 IP 的请求数量,通过使用代理,你可以在不同 IP 之间轮换请求。

python 复制代码
proxies = {
    'http': 'http://10.10.1.10:3128',
    'https': 'http://10.10.1.10:1080',
}

response = requests.get('https://example.com', proxies=proxies)

处理 Cookies

浏览器通常会在会话期间使用 cookies 来维持状态,你可以在请求中处理和发送 cookies,使你的爬虫更像是一个普通用户。

python 复制代码
session = requests.Session()
session.get('https://example.com')  # 获取初始 cookies
response = session.get('https://example.com/another-page')

规避反爬虫技术

一些网站使用 JavaScript 生成内容或检测浏览行为,以防止爬虫抓取数据。你可以使用浏览器自动化工具如 Selenium 或 Playwright 结合 requests,以规避这些检测。

python 复制代码
from selenium import webdriver

driver = webdriver.Chrome()
driver.get('https://example.com')

# 获取渲染后的页面内容
html = driver.page_source

控制并发请求数量

大多数爬虫会发出大量并发请求以加快抓取速度,但这会引起服务器的注意。限制并发请求数量或设置请求频率可以帮助模仿正常用户行为。

python 复制代码
import requests
from concurrent.futures import ThreadPoolExecutor

urls = ['https://example.com/page1', 'https://example.com/page2']
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'}

def fetch(url):
    response = requests.get(url, headers=headers)
    return response.text

with ThreadPoolExecutor(max_workers=2) as executor:
    results = executor.map(fetch, urls)

*遵守robots.txt 协议

协议格式

robots.txt 是一个用于向网络爬虫(如搜索引擎爬虫)指示哪些页面或文件可以被抓取的文本文件。它通常放置在网站的根目录下,通过简单的指令告诉爬虫哪些部分的网站是允许或禁止访问的。

一个典型的 robots.txt 文件可能如下所示:

plaintext 复制代码
User-agent: *
Disallow: /private/
Allow: /public/
  • User-agent : 指定适用的爬虫类型。* 表示适用于所有爬虫。
  • Disallow : 指定不允许访问的路径。/private/ 表示禁止访问该目录及其子目录下的所有内容。
  • Allow : 指定允许访问的路径。/public/ 表示允许访问该目录下的内容。

获取文件

首先,你需要从网站上获取 robots.txt 文件。这通常可以通过访问 https://example.com/robots.txt 来实现。

python 复制代码
import requests

robots_url = 'https://example.com/robots.txt'
response = requests.get(robots_url)
response.raise_for_status()

# 获取 robots.txt 内容
robots_txt = response.text

解析文件

requests 库本身并没有类似于 urllib.robotparser 的功能来解析 robots.txt 文件。但是你可以结合 requestsurllib.robotparser 来实现对 robots.txt 的解析和遵守。urllib.robotparser 是 Python 标准库中用于解析 robots.txt 文件的工具,可以用来检查某个 URL 是否被允许访问。

python 复制代码
from urllib.robotparser import RobotFileParser
from io import StringIO

# 创建 RobotFileParser 实例
rp = RobotFileParser()

# 使用 StringIO 来读取 robots.txt 内容
rp.parse(StringIO(robots_txt))

# 检查某个 URL 是否被允许访问
user_agent = '*'
url_to_check = 'https://example.com/somepage'

if rp.can_fetch(user_agent, url_to_check):
    print(f"可以访问 {url_to_check}")
else:
    print(f"不能访问 {url_to_check}")

发起请求

如果 URL 被允许访问,则可以使用 requests 库发起请求。

python 复制代码
if rp.can_fetch(user_agent, url_to_check):
    response = requests.get(url_to_check)
    response.raise_for_status()
    print(f"请求成功: {response.status_code}")
else:
    print(f"访问被拒绝: {url_to_check}")
相关推荐
Justinc.4 分钟前
CSS3新增边框属性(五)
前端·css·css3
deephub10 分钟前
Tokenformer:基于参数标记化的高效可扩展Transformer架构
人工智能·python·深度学习·架构·transformer
xiaoxiongip66613 分钟前
HTTP 和 HTTPS
网络·爬虫·网络协议·tcp/ip·http·https·ip
neter.asia20 分钟前
vue中如何关闭eslint检测?
前端·javascript·vue.js
~甲壳虫21 分钟前
说说webpack中常见的Plugin?解决了什么问题?
前端·webpack·node.js
Open-AI28 分钟前
Python如何判断一个数是几位数
python
极客代码31 分钟前
【Python TensorFlow】入门到精通
开发语言·人工智能·python·深度学习·tensorflow
义小深33 分钟前
TensorFlow|咖啡豆识别
人工智能·python·tensorflow
疯一样的码农37 分钟前
Python 正则表达式(RegEx)
开发语言·python·正则表达式
光影少年40 分钟前
vue2与vue3的全局通信插件,如何实现自定义的插件
前端·javascript·vue.js