到这里,你的程序还都是"单机版"——数据要么写死在代码里,要么读本地文件。但真正有用的程序,几乎都要联网:查天气、刷新闻、调 AI、发消息……

这一课我们学会三件事:

  1. requests 库,让 Python 像浏览器一样发请求、拿数据。
  2. 认识 JSON——网络上数据的通用"普通话"。
  3. 调一个真实 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"])

三个要点:

  1. try/except 包住请求,网络异常不会让程序崩。
  2. 看状态码429sleep 一下再试;别的错误果断放弃。
  3. 设一个上限,不能无限重试。

七、爬虫入门: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,你就有了爬任何网页的基本功。

三条铁律(务必遵守)

  1. 看 robots.txt:网站根目录的 robots.txt 会声明"哪些能爬、哪些不能",先尊重它。
  2. 别太频繁:加 time.sleep(1) 之类,别把人家服务器打挂——否则就是你被封的 429(本课第六节刚学过)。
  3. 只用于学习与公开数据:抓公开信息练手没问题,但别扒隐私、别商用侵权内容。

八、动手时间 🎯

实验 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,而不是死磕)。


九、课后练习

  1. params 搜 GitHub 上 machine learning 相关的仓库,打印前 5 个的名字和星数。
  2. 写一个函数 get_title(url),请求任意 JSON 接口并返回某个字段,网络失败时返回 None(不崩)。
  3. 故意把 timeout 设成 0.001,触发一次超时异常,用 try/except 接住它并打印一句友好的提示。
  4. 想想:如果 API 需要"登录"(比如要 token),你猜请求头上该加什么?(提示:headers 里的 Authorization
最后一课,我们把前面 13 课全串起来,从零写一个能用的命令行小工具——检验你是不是真的"会写脚本"了。

十、小结

  1. 网络就是"发请求、收响应",requests.get() 一行搞定,JSON 是网络的普通话。
  2. 永远给请求设 timeout,用 try/except 接住网络异常,程序才不"卡死"。
  3. 带参数用 params,要登录就往 headers 里加 Authorization——调 API 的套路就这些。

标签: none

添加新评论