4、数据采集基础:使用requests库抓取网页数据、处理API返回的JSON数据

数据采集,说白了就是让程序替你去网上「看」东西。

我刚开始做数据分析那会儿,还傻乎乎地手动复制粘贴网页表格。后来发现,这活儿交给 Python 的 requests 库,几行代码就搞定了。今天咱们就聊聊这个基本功。

4.1 为什么是 requests?

Python 里能发 HTTP 请求的库不少,但 requests 是我用得最顺手的。它简洁、直观,文档也友好。你想想看,一个 GET 请求就一行代码:

import requests
response = requests.get('https://api.example.com/data')
print(response.status_code)  # 看看服务器回没回话

嗯,就这么简单。但实际项目中,坑往往藏在细节里。

4.2 抓取网页数据:从静态页面开始

先拿静态网页练手。比如抓一个公开的新闻标题列表:

import requests
from bs4 import BeautifulSoup

url = 'https://news.example.com'
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}

resp = requests.get(url, headers=headers, timeout=10)
resp.encoding = 'utf-8'  # 我习惯显式指定编码,避免乱码

soup = BeautifulSoup(resp.text, 'html.parser')
titles = soup.select('h2.news-title a')

for t in titles:
    print(t.get_text(strip=True))
我的小习惯:每次请求都带上 headerstimeout。不带 User-Agent 容易被反爬,不设 timeout 程序可能卡死。

这里要注意,BeautifulSoup 不是必须的,但解析 HTML 时它确实好用。我个人习惯用 lxml 作为解析器,速度更快。

4.3 处理 API 返回的 JSON 数据

现在很多网站都提供 JSON 接口。处理起来比 HTML 省心多了——不用解析标签,直接当字典用。

import requests

api_url = 'https://api.example.com/users?page=1'
resp = requests.get(api_url, headers=headers)

if resp.status_code == 200:
    data = resp.json()  # 直接转成 Python 字典/列表
    for user in data['results']:
        print(f"用户名: {user['name']}, 邮箱: {user['email']}")
else:
    print(f"请求失败,状态码: {resp.status_code}")

为什么用 resp.json() 而不是 json.loads(resp.text)?因为前者会自动处理编码问题。我在项目中遇到过,后者在某些 API 返回带 BOM 的 JSON 时会报错。

4.4 避坑指南:我踩过的三个雷

  • SSL 证书验证:有些内部 API 用自签名证书,直接请求会报错。加个 verify=False 能绕过,但生产环境别这么干。
  • 重定向陷阱:默认情况下 requests 会自动跟随重定向。如果你不想跟,设置 allow_redirects=False
  • 大文件下载:别用 resp.content 一次性读入内存。用 stream=True 分块写入:
resp = requests.get(large_file_url, stream=True)
with open('file.zip', 'wb') as f:
    for chunk in resp.iter_content(chunk_size=8192):
        f.write(chunk)
我曾经犯过的错:有一次爬一个分页 API,没检查 next 字段是否存在,结果死循环了。后来我养成了习惯——每次循环都加一个最大页数限制。

4.5 知识体系:数据采集的核心逻辑

下面这张图,是我自己总结的数据采集流程。你看一眼,心里就有谱了:

数据采集核心流程 1. 构造请求 URL + Headers + 参数 2. 发送请求 GET / POST / timeout 3. 获取响应 状态码 + 内容 状态码 200? 解析数据 JSON / HTML / XML 重试/报错 存储/处理 CSV / 数据库 / 文件 核心原则:先检查状态码,再解析内容,最后处理异常

4.6 实战:处理分页 API

实际工作中,API 数据往往不止一页。我常用的分页处理模式是这样的:

def fetch_all_pages(base_url, params=None, max_pages=10):
    all_data = []
    page = 1
    
    while page <= max_pages:
        params = params or {}
        params['page'] = page
        
        resp = requests.get(base_url, params=params, headers=headers)
        if resp.status_code != 200:
            print(f"第{page}页请求失败")
            break
            
        data = resp.json()
        if not data['results']:  # 没数据了,退出
            break
            
        all_data.extend(data['results'])
        page += 1
        
        # 礼貌性延迟,别把人家服务器打崩了
        time.sleep(0.5)
    
    return all_data

关键点总结:

  • 始终检查 status_code,别假设请求一定成功
  • params 参数传查询参数,别自己拼 URL 字符串
  • 加延迟、设超时、限制最大页数——这是对服务器的尊重
  • JSON 数据用 .json() 方法解析,省心省力

好了,数据采集这块儿,说白了就是「发请求→收响应→解析数据」这三板斧。你多练几次,自然就熟了。


专注资料整理