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))
我的小习惯:每次请求都带上
headers 和 timeout。不带 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 知识体系:数据采集的核心逻辑
下面这张图,是我自己总结的数据采集流程。你看一眼,心里就有谱了:
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()方法解析,省心省力
好了,数据采集这块儿,说白了就是「发请求→收响应→解析数据」这三板斧。你多练几次,自然就熟了。