Requests库详解

网络爬虫专题 · 掌握爬虫核心HTTP请求库

专题:Python网络爬虫系统学习

关键词:Python, 网络爬虫, Requests, HTTP请求, Session, 请求头, GET, POST, 异常处理

一、Requests库概述

Requests是Python生态中最流行的HTTP请求库,由Kenneth Reitz开发,以其简洁优雅的API设计闻名。它的设计哲学是"HTTP for Humans"(为人类设计的HTTP库),旨在让HTTP请求变得直观自然,无需手动处理URL编码、连接管理、cookie持久化等底层细节。

安装方式非常简单,通过pip即可完成:

pip install requests

与Python标准库中的urllib相比,Requests具有显著优势。urllib虽然功能齐全,但API设计较为底层,发送一个简单的GET请求需要手动处理urlopen、read、decode等多个步骤,而POST请求的参数编码、异常处理等更是需要大量样板代码。Requests将这些复杂性封装在简洁的API背后,一行代码即可完成一个完整的HTTP请求。

"Requests is an elegant and simple HTTP library for Python, built for human beings." -- Kenneth Reitz

Requests基于urllib3开发,在其基础上提供了更高层次的抽象。它默认支持Keep-Alive连接池、自动解压gzip/deflate响应内容、自动处理Cookie持久化、支持文件上传、自动检测响应编码等特性。这些功能在爬虫开发中几乎是必备的,而Requests让它们开箱即用。

二、基本请求方法

Requests为每种HTTP方法都提供了对应的便捷函数,让发送请求变得异常简单。

GET请求

GET是最常用的HTTP方法,用于从服务器获取资源。使用Requests发送GET请求只需调用requests.get()方法:

import requests response = requests.get('https://api.github.com/events') print(response.status_code) # 200 print(response.text) # 响应文本内容

POST请求

POST用于向服务器提交数据,如表单提交、API调用等:

import requests payload = {'key1': 'value1', 'key2': 'value2'} response = requests.post('https://httpbin.org/post', data=payload) print(response.json()) # 解析JSON响应

其他HTTP方法

Requests同样支持其他HTTP方法,API风格保持一致:

import requests response = requests.put('https://httpbin.org/put', data={'key': 'value'}) response = requests.delete('https://httpbin.org/delete') response = requests.head('https://httpbin.org/get') response = requests.options('https://httpbin.org/get')

响应对象详解

每次请求都会返回一个Response对象,它包含了服务器返回的所有信息。以下是Response对象最常用的属性和方法:

提示:在处理中文网页时,如果text属性出现乱码,通常是因为Requests自动检测的编码不正确。此时可以通过设置response.encoding = 'utf-8' 或 response.encoding = 'gbk' 来手动修正编码。

状态码检查是请求处理中非常关键的一环。Requests提供了raise_for_status()方法,如果响应状态码是4xx或5xx,它会抛出一个HTTPError异常,方便进行统一的错误处理。

response = requests.get('https://httpbin.org/status/404') try: response.raise_for_status() except requests.exceptions.HTTPError as e: print(f"HTTP错误: {e}")

三、请求参数与配置

Requests提供了丰富的参数用于定制请求行为,覆盖了爬虫开发中的各种场景。

URL查询参数 (params)

使用params参数可以方便地拼接URL查询字符串,Requests会自动进行URL编码:

import requests params = { 'q': 'python requests', 'page': 1, 'per_page': 20 } response = requests.get('https://api.github.com/search/repositories', params=params) print(response.url) # https://api.github.com/search/repositories?q=python+requests&page=1&per_page=20

表单数据 (data) 与JSON数据 (json)

在发送POST请求时,data参数用于提交表单格式的数据,json参数用于提交JSON格式的数据:

import requests # Form表单数据(Content-Type: application/x-www-form-urlencoded) form_data = {'username': 'admin', 'password': '123456'} response = requests.post('https://httpbin.org/post', data=form_data) # JSON数据(Content-Type: application/json) json_data = {'name': 'John', 'age': 30} response = requests.post('https://httpbin.org/post', json=json_data)

自定义请求头 (headers)

很多网站会校验请求头来识别爬虫,自定义请求头是爬虫开发中的基本操作:

import requests headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', 'Referer': 'https://www.google.com/' } response = requests.get('https://httpbin.org/headers', headers=headers)

其他重要参数

# 超时设置 response = requests.get('https://httpbin.org/delay/5', timeout=(3, 10)) # 代理设置 proxies = { 'http': 'http://127.0.0.1:7890', 'https': 'http://127.0.0.1:7890' } response = requests.get('https://httpbin.org/ip', proxies=proxies)

四、Session会话管理

在爬虫开发中,经常需要保持登录状态或共享配置,这时就应该使用requests.Session()。Session对象在同一个会话实例发起的多次请求之间会自动保持Cookie,并且可以设置会话级别的默认参数。

import requests # 创建Session对象 session = requests.Session() # 设置会话级别的请求头和Cookie session.headers.update({ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' }) session.cookies.update({'session_id': 'abc123'}) # 后续所有请求都会自动携带上述headers和cookies response1 = session.get('https://httpbin.org/cookies') response2 = session.get('https://httpbin.org/headers')

Session最强大的特性是自动管理Cookie。当你先登录然后访问需要认证的页面时,Session会自动携带登录后设置的Cookie:

import requests session = requests.Session() # 第一步:登录 login_data = {'username': 'user', 'password': 'pass'} session.post('https://example.com/login', data=login_data) # 第二步:访问需要登录的页面(cookie自动携带) response = session.get('https://example.com/dashboard') # 此时已自动携带登录成功后服务器设置的Cookie

Session还支持作为上下文管理器使用,确保请求完成后正确释放资源:

import requests with requests.Session() as session: session.headers.update({'User-Agent': 'Custom'}) response = session.get('https://httpbin.org/headers') print(response.text) # 会话自动关闭

使用Session的另一个重要优势是连接池复用。Session内部维护了一个urllib3连接池,在向同一主机发送多次请求时,可以复用底层的TCP连接,显著提高请求效率。对于需要大量请求的爬虫任务,使用Session比每次新建连接能提升数倍的性能。

五、请求头伪装

网站通常通过分析请求头来识别爬虫流量,因此合理设置请求头是爬虫开发的核心技能之一。

关键请求头字段

User-Agent随机切换

单一User-Agent容易被网站识别和封禁,使用fake_useragent库可以方便地随机切换User-Agent:

from fake_useragent import UserAgent import requests ua = UserAgent() headers = {'User-Agent': ua.random} # 每次随机生成一个User-Agent response = requests.get('https://httpbin.org/headers', headers=headers) print(response.text)

从浏览器复制请求头

最可靠的伪裝方法是直接从浏览器开发者工具中复制真实的请求头。在Chrome中按F12打开开发者工具,切换到Network标签页,找到目标请求,右键选择"Copy as cURL"或手动复制关键请求头字段。然后将这些请求头原样设置在Requests中,这样服务器几乎无法区分请求是来自爬虫还是真实浏览器。

import requests # 从浏览器完整复制的请求头 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', 'Accept-Encoding': 'gzip, deflate, br', 'Referer': 'https://www.example.com/', 'Connection': 'keep-alive', 'Upgrade-Insecure-Requests': '1', 'Sec-Fetch-Dest': 'document', 'Sec-Fetch-Mode': 'navigate', 'Sec-Fetch-Site': 'same-origin', 'Sec-Fetch-User': '?1', } response = requests.get('https://example.com/target-page', headers=headers)

六、异常处理

网络请求充满了不确定性,完善的异常处理机制是稳健爬虫的基石。Requests定义了一套层次分明的异常体系,所有异常都继承自requests.RequestException

异常类型

import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry import time def fetch_with_retry(url, max_retries=3, backoff_factor=0.5): session = requests.Session() # 配置重试策略 retry_strategy = Retry( total=max_retries, # 总重试次数 backoff_factor=backoff_factor, # 退避因子(等待时间递增) status_forcelist=[500, 502, 503, 504], # 需要重试的状态码 allowed_methods=['GET', 'POST'] # 允许重试的HTTP方法 ) adapter = HTTPAdapter(max_retries=retry_strategy) session.mount('http://', adapter) session.mount('https://', adapter) try: response = session.get(url, timeout=10) response.raise_for_status() return response except requests.exceptions.ConnectionError as e: print(f"连接错误: {e}") except requests.exceptions.Timeout as e: print(f"请求超时: {e}") except requests.exceptions.HTTPError as e: print(f"HTTP错误: {e}") except requests.exceptions.RequestException as e: print(f"请求失败: {e}") finally: session.close()

最佳实践:在爬虫中使用指数退避重试策略。每次重试的等待时间按照backoff_factor * (2 ** (重试次数-1))递增,避免在服务器压力大时集中重试造成更大的负担。推荐将重试配置为3次,退避因子0.5,这样重试间隔分别为0.5秒、1秒、2秒。

七、高级用法

除了基础功能,Requests还提供了一些高级特性,可以应对更复杂的爬虫需求。

流式请求与大文件下载

当需要下载大文件时,使用stream=True可以避免将整个响应内容加载到内存中:

import requests url = 'https://example.com/large-file.zip' response = requests.get(url, stream=True) response.raise_for_status() with open('large-file.zip', 'wb') as f: for chunk in response.iter_content(chunk_size=8192): if chunk: # 过滤keep-alive的空chunk f.write(chunk)

流式请求的核心优势在于:无论文件多大,内存占用始终保持稳定。每次只处理一个chunk的数据,非常适合下载大文件或处理大型响应流。

自定义SSL证书验证

某些网站使用自签名证书或证书配置有误,导致SSL验证失败。可以通过设置verify参数来控制证书验证行为:

import requests # 跳过SSL验证(不推荐,存在安全风险) response = requests.get('https://self-signed.badssl.com', verify=False) # 使用自定义CA证书 response = requests.get('https://example.com', verify='/path/to/custom/cert.pem') # 抑制SSL警告 import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

hooks钩子函数

Requests支持钩子机制,可以在请求处理的特定阶段执行自定义函数:

import requests def print_url(response, *args, **kwargs): print(f"请求URL: {response.url}") def print_elapsed(response, *args, **kwargs): print(f"请求耗时: {response.elapsed.total_seconds():.2f}秒") # 注册钩子,可以在请求的各个阶段调用 hooks = {'response': [print_url, print_elapsed]} response = requests.get('https://httpbin.org/get', hooks=hooks)

钩子函数在处理批量请求时非常实用,可以用来统一记录日志、统计请求耗时、自动处理重定向、或者在收到特定状态码时触发告警。

连接池管理

Session对象维护了一个连接池,通过HTTPAdapter可以调整其行为:

from requests.adapters import HTTPAdapter session = requests.Session() # pool_connections:缓存连接数,pool_maxsize:每个主机的最大连接数 adapter = HTTPAdapter(pool_connections=100, pool_maxsize=100) session.mount('http://', adapter) session.mount('https://', adapter)

合理配置连接池参数可以显著提升爬虫的并发请求能力。pool_connections决定缓存多少个不同的主机连接,pool_maxsize决定单个主机最多允许的并发连接数。对于需要高并发抓取的场景,适当增大这两个参数可以有效减少TCP握手开销。

八、核心要点总结

  • 简洁优先:Requests的设计哲学是"HTTP for Humans",让HTTP请求代码简洁直观,一行代码完成一个完整请求
  • Session复用:使用Session对象保持Cookie和连接池,避免重复登录和TCP握手,显著提升爬虫效率
  • 请求头伪裝:合理设置User-Agent、Referer等请求头是反爬虫对抗的基础,配合fake_useragent实现随机切换
  • 异常处理:全面捕获ConnectionError、Timeout、HTTPError等异常,结合指数退避重试机制确保爬虫稳健运行
  • 流式下载:大文件下载使用stream=True和iter_content()分块写入,避免内存溢出
  • 时间设置:始终设置timeout参数,避免爬虫因某个请求卡死而无限等待
  • 代理支持:proxies参数支持HTTP/HTTPS/SOCKS代理,是实现IP轮转绕过封禁的基础
  • 连接池优化:通过HTTPAdapter调整pool_connections和pool_maxsize参数,适应高并发爬虫场景

九、进一步思考

Requests库虽然强大,但它提供的是同步阻塞式的HTTP请求。在面对大规模爬虫任务时,IO等待时间占据了整个爬虫运行时间的大部分。可以考虑以下优化方向:

首先,可以使用grequests库,它结合了Requests和gevent,通过协程实现异步并发请求,在等待IO时可以切换执行其他任务。其次,aiohttp是基于asyncio的异步HTTP库,提供了更底层的控制和更高的并发性能。对于企业级爬虫框架,Scrapy内置了Twisted异步引擎和中间件系统,支持自动限速、去重、Pipeline等高级功能。

在实际项目中,可以将Requests用于快速原型开发和中小规模爬虫,将Scrapy用于大规模分布式爬虫系统。理解Requests的核心原理,也是深入学习其他HTTP客户端库和爬虫框架的基础。