Python 从零开始 · 第 13 课:网络请求、API 与爬虫入门
到这里,你的程序还都是"单机版"——数据要么写死在代码里,要么读本地文件。但真正有用的程序,几乎都要联网:查天气、刷新闻、调 AI、发消息……
这一课我们学会三件事:
- 用
requests库,让 Python 像浏览器一样发请求、拿数据。 - 认识 JSON——网络上数据的通用"普通话"。
- 调一个真实 API,并学会限流与重试,写得像个老手。
学完这一课,你就明白"AI Agent 调工具"的底层逻辑了——它本质就是:程序联网 + 调 API + 决策。
一、什么是 HTTP 请求?
你在浏览器地址栏敲一个网址、按回车,浏览器就向服务器发了一个 HTTP 请求,服务器返回一个响应,浏览器把它渲染成网页。
请求的核心是一个 URL,外加一个方法(method)。最常用的两个:
| 方法 | 含义 | 对应 Python |
|---|---|---|
| GET | 拿数据(浏览网页、查天气) | requests.get() |
| POST | 提交数据(登录、发消息) | requests.post() |
我们这门课主要用 GET——把数据"要"回来。
二、requests:一行发请求
先装库:
pip install requests然后:
import requests
resp = requests.get("https://api.github.com") # 发一个 GET 请求
print(resp.status_code) # 200 表示成功(就像"HTTP 200 OK")
print(resp.text) # 返回的原始文本(字符串)status_code 是服务器给你的"状态码",常见的要记住:
| 状态码 | 含义 |
|---|---|
| 200 | 成功 |
| 404 | 找不到(网址错了) |
| 500 | 服务器自己崩了 |
| 429 | 你请求太快,被限流了 |
新手口诀:2 开头是成功,4 开头是你的错,5 开头是服务器的错。
🧊 冷笑话:"程序员最怕什么数字?""404——因为'找不到对象'。"(404 既是"网页不存在",也是"单身")
三、JSON:网络的"普通话"
服务器返回的 resp.text 往往长这样:
{"name": "jerry", "age": 30, "city": "上海"}这叫 JSON——一种用纯文本表示数据的格式,和 Python 的字典几乎一模一样。它是全世界的"数据普通话"。
用 requests 可以直接把它转成 Python 字典:
resp = requests.get("https://api.github.com/users/torvalds")
data = resp.json() # 把 JSON 文本转成 Python 字典
print(data["login"]) # torvalds
print(data["followers"]) # 粉丝数(数字会随时间变)resp.json() 一步到位,比手动解析 resp.text 省事得多。拿到字典后,剩下的就是第 5 课学的 dict 操作了。
想"看"一个 JSON 长什么样,可以 print(json.dumps(data, indent=2, ensure_ascii=False)),缩进打印,一目了然。四、带参数的请求:问天气、查搜索
很多 API 要你传参数,比如"查哪个城市"。GET 的参数直接拼在 URL 后面,或交给 params:
import requests
# 用 params 传参数,requests 会自动拼好 URL
resp = requests.get(
"https://api.github.com/search/repositories",
params={"q": "python", "sort": "stars"},
)
data = resp.json()
print("共", data["total_count"], "个结果")
for repo in data["items"][:3]: # 只看前 3 个
print(repo["full_name"], "⭐", repo["stargazers_count"])params 传一个字典,requests 会把它变成 ?q=python&sort=stars 这样的查询串——你不用手动拼,也就不会拼错。
五、响应头与超时:别让程序"卡死"
发请求时,网络可能很慢、甚至不通。如果程序一直傻等,就会"卡死"。所以一定要设超时(timeout):
resp = requests.get("https://api.github.com", timeout=5) # 最多等 5 秒超过 5 秒还没响应,requests 会抛出一个异常,而不是无限等下去。
响应头(headers)里也藏着信息,比如服务器是什么、数据格式:
print(resp.headers["Content-Type"]) # application/json; charset=utf-8六、限流与重试:写得像个老手
真实世界有个残酷现实:免费 API 通常限流——一分钟只能请求 N 次。你请求太快,它就回你 429。
老手的做法是:失败别硬刚,等一等再试。这就用到了第 9 课的异常处理 + 一个经典循环:
import requests
import time
def 带重试地请求(url, 最多试几次=3):
for 第几次 in range(1, 最多试几次 + 1):
try:
resp = requests.get(url, timeout=5)
if resp.status_code == 200:
return resp.json() # 成功,直接返回
if resp.status_code == 429: # 被限流了
print(f"限流了,第 {第几次} 次,等 2 秒再试…")
time.sleep(2) # 等 2 秒(第 10 课也见过 time)
else:
return None # 别的错误,不重试了
except requests.RequestException as e:
print(f"请求异常:{e},第 {第几次} 次重试")
time.sleep(1)
return None # 试完了还是不行
data = 带重试地请求("https://api.github.com/users/torvalds")
if data:
print("拿到数据:", data["login"])三个要点:
try/except包住请求,网络异常不会让程序崩。- 看状态码:
429就sleep一下再试;别的错误果断放弃。 - 设一个上限,不能无限重试。
七、爬虫入门:BeautifulSoup
前面我们用 requests 拿到的,大多是有现成 JSON 接口的数据。可现实里,大多数网站没有 JSON 接口,返回的是网页本身(HTML)。你想抓新闻标题、商品价格、文章正文,就得自己从 HTML 里"淘"出数据。
resp.text 是一大坨 HTML 标签,直接打印像天书:
<html><head><title>某新闻</title></head><body><h1>头条</h1>...</body></html>所以爬虫要分两步走:requests 抓下来 → BeautifulSoup 解析并提取。后者就是今天的主角。
安装
pip install beautifulsoup4
pip install lxml # 可选,更快的解析器基本用法:把 HTML 变成可查询对象
from bs4 import BeautifulSoup
html = """
<html>
<head><title>示例页面</title></head>
<body>
<h1>这是标题</h1>
<p class="intro">第一段</p>
<a href="https://example.com">点我</a>
</body>
</html>
"""
soup = BeautifulSoup(html, "html.parser") # 解析 HTML
print(soup.title) # <title>示例页面</title>
print(soup.title.string) # 示例页面(标签里的文字)
print(soup.h1.string) # 这是标题
print(soup.a["href"]) # https://example.com(取属性)BeautifulSoup(html, "html.parser") 把一大段 HTML 变成一个可查询的对象,这就是解析的核心。
找单个用 find、找多个用 find_all
print(soup.find("h1")) # 第一个 <h1>
print(soup.find_all("p")) # 所有 <p>,返回列表
for a in soup.find_all("a"):
print(a["href"]) # 打印所有链接find_all 还能按属性过滤:
soup.find_all("p", class_="intro") # class 是 Python 关键字,要写成 class_
soup.find_all(id="main") # 按 id 找CSS 选择器:select(推荐)
熟悉网页的人更爱用 CSS 选择器,select 一行搞定:
soup.select("p.intro") # 所有 class 含 intro 的 <p>
soup.select("#main a") # id 为 main 里所有的 <a>
soup.select("h1")[0].string # 第一个 h1 的文字实战:抓一个真实网页的标题
import requests
from bs4 import BeautifulSoup
resp = requests.get("https://example.com", timeout=5)
soup = BeautifulSoup(resp.text, "html.parser")
print(soup.title.string) # Example Domain这就是"爬虫"的最小闭环:发请求 → 解析 HTML → 提取你要的字段。前面学的 requests,加上这里的 BeautifulSoup,你就有了爬任何网页的基本功。
三条铁律(务必遵守)
- 看 robots.txt:网站根目录的
robots.txt会声明"哪些能爬、哪些不能",先尊重它。 - 别太频繁:加
time.sleep(1)之类,别把人家服务器打挂——否则就是你被封的 429(本课第六节刚学过)。 - 只用于学习与公开数据:抓公开信息练手没问题,但别扒隐私、别商用侵权内容。
八、动手时间 🎯
实验 1:查 GitHub 某个用户的信息
import requests
resp = requests.get("https://api.github.com/users/jerry", timeout=5)
if resp.status_code == 200:
d = resp.json()
print(d["login"], "粉丝数:", d["followers"])
else:
print("请求失败,状态码:", resp.status_code)实验 2:抓一页新闻标题(练 JSON 解析)
很多网站提供公开 JSON 接口。这里用一个稳定示例——Hacker News:
import requests
resp = requests.get(
"https://hacker-news.firebaseio.com/v0/topstories.json",
timeout=5,
)
ids = resp.json()[:5] # 前 5 条新闻的 id
for news_id in ids:
item = requests.get(
f"https://hacker-news.firebaseio.com/v0/item/{news_id}.json",
timeout=5,
).json()
print("·", item.get("title"))实验 3:给你的重试函数加个"最多试 5 次"
把上面的 带重试地请求 抄下来,改 最多试几次=5,故意请求一个会 404 的网址,看它怎么处理(应该直接返回 None,而不是死磕)。
九、课后练习
- 用
params搜 GitHub 上machine learning相关的仓库,打印前 5 个的名字和星数。 - 写一个函数
get_title(url),请求任意 JSON 接口并返回某个字段,网络失败时返回 None(不崩)。 - 故意把 timeout 设成
0.001,触发一次超时异常,用 try/except 接住它并打印一句友好的提示。 - 想想:如果 API 需要"登录"(比如要 token),你猜请求头上该加什么?(提示:headers 里的
Authorization)
最后一课,我们把前面 13 课全串起来,从零写一个能用的命令行小工具——检验你是不是真的"会写脚本"了。
十、小结
- 网络就是"发请求、收响应",
requests.get()一行搞定,JSON 是网络的普通话。 - 永远给请求设
timeout,用 try/except 接住网络异常,程序才不"卡死"。 - 带参数用
params,要登录就往 headers 里加Authorization——调 API 的套路就这些。