2、Python数据采集基础:使用requests库获取HTTP数据、解析JSON/XML响应、异常处理与重试机制

数据采集,说白了就是让程序替你去“读网页”。

我刚开始做爬虫时,总觉得这事挺玄乎。后来发现,核心就三步:发请求、收响应、解析数据。Python 里最顺手的工具就是 requests 库。今天咱们就把这三步掰开揉碎,讲清楚。

2.1 准备工作:安装与导入

先装库。这步没啥技术含量,但容易忘。

pip install requests

装好之后,在代码里导入:

import requests

嗯,就这一行。我见过有人把 requests 拼成 request(少了个 s),结果报错半天找不出原因。你注意一下。

2.2 发送 GET 请求:最基础的操作

获取网页数据,最常用的是 GET 请求。代码极其简单:

url = "https://api.example.com/data"
response = requests.get(url)

print(response.status_code)   # 看看服务器回没回话
print(response.text)          # 看看返回的原始内容

response 对象里藏着所有信息。我个人习惯先看 status_code,200 代表一切正常,4xx 或 5xx 就说明出问题了。

小技巧: 用 response.encoding 可以查看或设置编码。中文网站经常返回 utf-8,但有些老站点是 gbk。我遇到过几次乱码,都是因为没手动设编码。

2.3 解析 JSON 响应

现在大部分 API 都返回 JSON 格式。解析它,requests 已经帮你封装好了:

import requests

url = "https://api.example.com/users"
response = requests.get(url)

if response.status_code == 200:
    data = response.json()   # 直接变成 Python 字典或列表
    print(data["name"])
else:
    print("请求失败:", response.status_code)

.json() 方法会自动把 JSON 字符串转成 Python 对象。你想想看,省了多少手动 json.loads() 的功夫。

但要注意——如果返回的内容不是合法 JSON,.json() 会抛异常。所以最好放在 try 里:

try:
    data = response.json()
except ValueError:
    print("返回的不是 JSON 格式")

2.4 解析 XML 响应

JSON 虽好,但有些老系统还在用 XML。比如一些政府数据接口、SOAP 服务。解析 XML 需要额外库,我推荐 xml.etree.ElementTree,Python 自带,不用装。

import requests
import xml.etree.ElementTree as ET

url = "https://api.example.com/data.xml"
response = requests.get(url)

if response.status_code == 200:
    root = ET.fromstring(response.content)
    # 遍历所有 <item> 标签
    for item in root.findall("item"):
        title = item.find("title").text
        print(title)

这里用 response.content 而不是 response.text,因为 ET.fromstring 需要字节数据。我曾经在这上面栽过跟头——用 text 传进去,解析出来全是乱码。

响应属性 返回类型 适用场景
response.text 字符串 HTML、普通文本
response.content 字节 XML、图片、二进制文件
response.json() 字典/列表 JSON 数据

2.5 异常处理:别让程序说崩就崩

网络请求是最容易出问题的环节。服务器挂了、网络断了、超时了……任何一个都能让你的脚本直接崩溃。

我早期写爬虫时,脚本跑了一夜,第二天一看,第一条请求就挂了,后面全没执行。从那以后,我养成了“每条请求必加异常处理”的习惯。

import requests
from requests.exceptions import RequestException

url = "https://api.example.com/data"

try:
    response = requests.get(url, timeout=5)
    response.raise_for_status()  # 状态码不是 200 就抛异常
    data = response.json()
    print(data)
except RequestException as e:
    print(f"请求出错: {e}")

raise_for_status() 是个好东西。它会检查状态码,如果是 4xx 或 5xx,直接抛异常。省得你每次都手动 if status_code != 200。

注意: 永远要设 timeout 参数。不设的话,请求可能卡住几分钟甚至更久。我一般设 5-10 秒,具体看接口响应速度。

2.6 重试机制:让程序更健壮

网络波动是常态。一次失败就放弃,太可惜了。合理的做法是:失败了,等一会儿,再试一次。

Python 里有个好用的库叫 tenacity,专门干这事:

pip install tenacity

用法很简单:

from tenacity import retry, stop_after_attempt, wait_fixed
import requests

@retry(stop=stop_after_attempt(3), wait=wait_fixed(2))
def fetch_data(url):
    response = requests.get(url, timeout=5)
    response.raise_for_status()
    return response.json()

data = fetch_data("https://api.example.com/data")

这段代码的意思是:最多重试 3 次,每次间隔 2 秒。如果 3 次都失败,异常会继续往上抛,你可以在外层再处理。

如果你不想装第三方库,自己写个循环也行:

import requests
import time

def fetch_with_retry(url, max_retries=3, delay=2):
    for attempt in range(max_retries):
        try:
            response = requests.get(url, timeout=5)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            print(f"第 {attempt+1} 次尝试失败: {e}")
            if attempt < max_retries - 1:
                time.sleep(delay)
    raise Exception("所有重试都失败了")
核心原则: 重试不是万能药。如果是 4xx 错误(比如 404、403),重试也没用,那是请求本身的问题。只有 5xx 或网络超时,才值得重试。

2.7 本章知识体系

下面这张图,把今天讲的内容串起来了。你可以把它当成“数据采集请求”的标准流程:

数据采集请求流程 数据采集请求核心流程 准备 URL 发送 GET 请求 检查状态码 200 非 200 解析 JSON/XML 异常处理 + 重试 返回处理结果 重试机制只对 5xx 和网络超时有效,4xx 错误请检查请求本身

2.8 避坑指南

  • 忘记设 User-Agent: 有些服务器会拦截没有 User-Agent 的请求。加一个常见的浏览器标识就行:headers = {"User-Agent": "Mozilla/5.0"}
  • 忽略编码问题: 中文乱码多半是编码没设对。先看 response.encoding,不对就手动设成 utf-8 或 gbk。
  • 重试次数太多: 我曾经设过 10 次重试,结果服务器挂了,脚本在那傻等 30 秒。3 次足够了,不行就放弃。
  • 不设 timeout: 默认是永久等待。网络一卡,脚本就卡死。每次请求都带上 timeout,这是好习惯。