☰
Python新手如何从零开始调用API:requests与access_token实战指南
2026/9/27 3:15:48 网站建设 项目流程

1. 为什么我建议每个Python新手都从API调用练起

刚学Python那会儿,我盯着教程里的循环和函数看了两周,合上电脑还是不知道能拿它干什么。直到第一次用requests库调通了一个天气接口,把返回的JSON数据打印在终端里——那种"我的代码真的连上了外面的世界"的感觉,比写一百个九九乘法表都来得实在。API调用就是Python新手最好的练手场:它逼着你同时理解HTTP协议、数据格式、异常处理、认证机制,而这些恰好是真实工作中每天都在用的东西。

这篇内容写给三类人:完全没接触过API的Python初学者、调接口总是报错但不知道从哪查起的人、以及想把API调用流程系统化梳理一遍的开发者。我会从最基础的HTTP概念讲起,一路走到access_token认证、requests库实战、429限流处理、502网关错误排查,把我在实际项目里踩过的坑和总结的经验都摊开来说。你不需要事先懂网络协议,只要会写基本的Python语法,跟着走一遍就能自己调通第一个接口。

核心关键词先摆出来:python、API、requests、access_token、HTTP。这五个词基本覆盖了从零开始调接口的全部关键环节。下面我按"理解原理→动手实操→排查问题"的顺序展开,每一段都尽量给出可以直接复制运行的代码和具体的参数说明。

2. 动手之前先把HTTP和API的关系理清楚

2.1 API到底是什么,用生活场景打个比方

API(Application Programming Interface,应用程序编程接口)这个词听起来很唬人,其实本质就是"别人给你留的一扇门"。你去餐厅吃饭,不会直接冲进厨房自己炒菜,而是看菜单点菜,服务员把需求传给厨房,再把做好的菜端给你。API就是那个服务员加菜单:你按照约定的格式发送请求,服务器按照约定的格式返回数据。

在Python里调用API,你做的事情就是:构造一个符合要求的HTTP请求,发送给对方的服务器地址,然后解析服务器返回的数据。整个过程不需要你关心对方服务器内部怎么实现的,只要遵守它公开的接口文档就行。

这里有个新手常犯的认知错误:以为API调用是什么高深技术。实际上你打开浏览器访问任何一个网页,浏览器就在帮你发HTTP请求。用Python调API,只是把这个过程用代码自动化了而已。

2.2 HTTP协议:请求和响应的四个核心要素

HTTP是API通信的底层协议,一次完整的交互包含四个关键部分:

请求方法(Method):最常见的是GET和POST。GET用于获取数据,参数直接拼在URL里;POST用于提交数据,参数放在请求体里。还有PUT、DELETE、PATCH等,分别对应更新、删除、部分更新。新手最容易搞混的是:有些接口文档写着用POST,你用了GET,服务器直接返回405错误("the specified http method is not allowed for the requested resource"),这个报错我在热搜词里也看到了,就是方法用错了。

请求URL:接口的地址,比如https://api.example.com/v1/weather。注意http和https的区别——https是加密传输,现在绝大多数API都要求用https,你用http去请求很可能会被拒绝或者重定向。

请求头(Headers):携带元信息的地方,比如Content-Type: application/json告诉服务器我发的是JSON格式,Authorization: Bearer xxx用来传递认证令牌。很多新手调接口返回401未授权,十有八九是请求头没写对。

请求体(Body):POST请求时携带的具体数据,通常是JSON格式。

服务器返回的响应同样包含状态码、响应头和响应体。状态码是最重要的判断依据:200表示成功,400表示请求参数有问题,401表示未认证,403表示无权限,404表示地址不对,429表示请求太频繁,500和502表示服务器端出问题了。

2.3 requests库:Python调API的事实标准

Python自带的urllib库也能发HTTP请求,但写起来极其繁琐。requests库把复杂的HTTP操作封装成了几个直观的方法,是目前Python社区调API的绝对主流选择。安装只需要一行命令:

pip install requests

如果你用的是国内网络环境,安装时可能会慢,可以加个镜像源:

pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple

装完之后验证一下:

import requests print(requests.__version__)

能打印出版本号就说明装好了。这里提醒一句,如果你用VSCode写代码,记得在设置里选对Python解释器,不然会出现"明明装了库却提示ModuleNotFoundError"的情况——这是vscode python环境配置里最常见的问题,本质是你pip装到了A环境,VSCode用的是B环境。

3. 从第一个GET请求到access_token认证的完整实操

3.1 最简单的GET请求:三行代码跑通

先从一个不需要认证的公开接口开始,建立信心。我用一个返回IP信息的接口做演示:

import requests response = requests.get("https://httpbin.org/get") print(response.status_code) print(response.text)

运行之后你会看到状态码200和一段JSON文本。这里有个细节:response.text返回的是字符串,如果接口返回的是JSON,用response.json()可以直接得到Python字典,操作起来更方便:

data = response.json() print(data["origin"])

实操心得:调任何新接口之前,我都会先用浏览器或者Postman手动访问一次,确认接口是通的、返回格式是什么样的,然后再写代码。这样能把"接口本身有问题"和"我代码写错了"两种情况区分开,排查效率高很多。

带参数的GET请求,参数用字典传给params:

params = {"city": "beijing", "date": "2024-01-01"} response = requests.get("https://api.example.com/weather", params=params)

requests会自动帮你把参数拼接到URL后面,不用自己手动拼接字符串,也就避免了编码问题。

3.2 POST请求与JSON数据提交

POST请求用于提交数据,关键是要设置正确的Content-Type。现在绝大多数API都接受JSON格式:

import requests import json url = "https://api.example.com/v1/chat" headers = {"Content-Type": "application/json"} payload = { "model": "some-model", "messages": [{"role": "user", "content": "你好"}] } response = requests.post(url, headers=headers, json=payload) print(response.status_code) print(response.json())

注意这里用的是json=payload而不是data=payload。用json=参数时,requests会自动帮你做两件事:把字典序列化成JSON字符串,同时自动设置Content-Type: application/json。如果你用data=传字典,它会按表单格式编码,很多接口会因此返回400错误。

踩过的坑:有一次我调一个接口一直返回400,报错信息是"the supported api model names are xxx",我以为是模型名写错了,检查了半天才发现是Content-Type没设对,服务器根本没解析到我的JSON体。所以看到400错误,第一反应应该是检查请求体和请求头,而不是怀疑接口挂了。

3.3 access_token认证:拿令牌、带令牌、刷新令牌

大部分生产环境的API都需要认证,最常见的方式就是access_token。流程通常分两步:

第一步,用你的身份凭证换取token。比如很多平台要求你用API Key去换一个有时效的token:

import requests auth_url = "https://api.example.com/oauth/token" auth_data = { "grant_type": "client_credentials", "client_id": "your_client_id", "client_secret": "your_client_secret" } resp = requests.post(auth_url, data=auth_data) token = resp.json()["access_token"] print(token)

第二步,把token放进请求头调用业务接口:

headers = { "Authorization": f"Bearer {token}", "Content-Type": "application/json" } response = requests.get("https://api.example.com/v1/user/info", headers=headers)

这里的Bearer是OAuth 2.0的标准前缀,注意Bearer和token之间有一个空格。我见过有人写成Authorization: token或者漏掉空格,结果一直返回401。

关于token的几个关键经验:

  • token是有有效期的,通常几小时到几天不等。生产代码里必须处理token过期的情况,不能每次请求都重新获取(那样既慢又浪费配额),也不能获取一次就永久使用(会过期)。
  • 正确的做法是缓存token并记录获取时间,每次请求前检查是否临近过期,快过期了就刷新。
  • 不要把token硬编码在代码里提交到代码仓库。用环境变量或者配置文件管理,这是基本的安全习惯。
import os token = os.environ.get("API_ACCESS_TOKEN")

3.4 用Session复用连接:提升批量请求效率

如果你要连续调同一个接口很多次,用requests.Session()比每次requests.get()效率高得多。Session会自动复用底层的TCP连接(也就是http连接复用),省去了每次重新建立连接的开销:

import requests session = requests.Session() session.headers.update({ "Authorization": f"Bearer {token}", "Content-Type": "application/json" }) for i in range(100): resp = session.get(f"https://api.example.com/v1/items/{i}") print(resp.status_code)

实测下来,批量请求场景下用Session能明显降低平均响应时间,尤其是请求同一个域名的时候。这是很多人忽略的优化点。

4. 接口调不通?这些报错我帮你翻译成人话

4.1 429 Too Many Requests:限流了怎么办

热搜词里反复出现"exceeded retry limit, last status: 429 too many requests",这个错误太典型了。429的意思是"你请求太频繁了,服务器让你歇会儿"。几乎所有公开API都有调用频率限制,比如每分钟60次、每小时1000次。

遇到429,正确的处理方式是指数退避重试:第一次等1秒,第二次等2秒,第三次等4秒,以此类推,而不是傻等着或者疯狂重试。requests本身不带重试功能,但可以配合urllib3的Retry机制:

import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry session = requests.Session() retry = Retry( total=5, backoff_factor=1, status_forcelist=[429, 500, 502, 503, 504], allowed_methods=["GET", "POST"] ) adapter = HTTPAdapter(max_retries=retry) session.mount("https://", adapter) session.mount("http://", adapter)

backoff_factor=1表示重试间隔按1、2、4、8、16秒递增。status_forcelist指定哪些状态码触发重试。这套配置我用了很久,对付偶发的429和5xx错误很稳。

注意事项:重试不是万能的。如果接口明确告诉你"5小时配额已用完"(热搜里那个"you have exceeded the 5-hour usage quot"),那重试再多次也没用,只能等配额重置或者升级套餐。所以重试逻辑里要区分"临时限流"和"配额耗尽",前者重试,后者直接报错给用户。

4.2 502 Bad Gateway:网关错误的排查思路

"unexpected status 502 bad gateway"这个错误,问题通常不在你这边,而在服务器端。502表示网关(比如Nginx)从上游服务器拿不到有效响应。你本地代码能做的就是:

  • 确认接口地址没写错,特别是端口号。热搜里那个http://127.0.0.1:15721/v1/responses就是典型的本地服务地址,如果本地服务没启动,就会报502或者连接失败。
  • 检查是不是请求体太大或者格式有问题导致上游崩溃。
  • 加重试逻辑,502往往是瞬时的,重试一次可能就好了。
  • 如果持续502,那就是对方服务的问题,只能等或者联系接口提供方。

4.3 400错误:参数问题的集中营

400错误的花样最多,我整理了一个速查表:

报错关键词大概率原因排查方向
maximum context length exceeded输入内容太长,超过模型token上限截断输入或分段处理
supported api model names are xxx模型名写错或该模型不可用对照文档核对模型名
reasoning_content must be passed back多轮对话时没把思考内容回传检查消息历史是否完整
method is not allowed请求方法用错(GET/POST搞混)核对文档要求的method

400错误的排查核心就一句话:逐字对照接口文档,检查你发的每一个字段。字段名大小写、数据类型(字符串还是数字)、必填项是否都传了,这些细节最容易出错。

4.4 连接类错误:从本地环境查起

"failed to connect to the docker api at npipe"这类错误,本质是客户端连不上目标服务。排查顺序建议是:

  1. 目标服务是否启动?用curl或者浏览器直接访问一下地址。
  2. 地址和端口是否正确?本地服务常见端口有冲突。
  3. 防火墙或代理是否拦截?公司网络环境下这个问题很常见。
  4. 如果是Docker相关,检查Docker Desktop是否运行、管道配置是否正确。

独家避坑技巧:我习惯在代码里加一个统一的请求日志,把每次请求的URL、状态码、耗时、响应前200字符都记下来。出问题的时候翻日志,比在代码里到处打print高效得多。

import logging logging.basicConfig(level=logging.INFO) def log_response(resp): logging.info(f"URL: {resp.url} | Status: {resp.status_code} | " f"Time: {resp.elapsed.total_seconds():.2f}s | " f"Body: {resp.text[:200]}")

5. 把API调用写得更专业:超时、异常、重试一个都不能少

5.1 超时设置:不加超时的代码都是耍流氓

requests默认没有超时限制,意味着如果服务器不响应,你的程序会一直卡在那里。生产代码必须设置超时:

try: response = requests.get(url, timeout=(3.05, 10)) except requests.exceptions.Timeout: print("请求超时")

timeout传元组时,第一个值是连接超时,第二个值是读取超时。连接超时设短一点(3秒左右),读取超时根据接口正常响应时间设(10秒到30秒)。这个参数怎么定?我的经验是:先不加超时跑几次,看正常响应耗时,然后把读取超时设成正常耗时的3到5倍。

5.2 异常处理:把可能出错的地方都包起来

调API的代码,异常处理不是可选项而是必选项。网络请求可能因为各种原因失败,完整的异常处理应该覆盖:

import requests from requests.exceptions import ( RequestException, Timeout, ConnectionError, HTTPError, TooManyRedirects ) def call_api(url, **kwargs): try: resp = requests.get(url, timeout=(3.05, 10), **kwargs) resp.raise_for_status() return resp.json() except Timeout: print("超时,稍后重试") except ConnectionError: print("连接失败,检查网络或地址") except HTTPError as e: print(f"HTTP错误:{e.response.status_code}") except RequestException as e: print(f"请求异常:{e}") return None

raise_for_status()这个方法很实用,它会在状态码是4xx或5xx时自动抛出HTTPError,省得你手动判断每一个状态码。

5.3 参数校验:在发请求之前就把错误拦住

很多400错误其实可以在发请求之前就避免。养成习惯:调接口前先校验必填参数是否齐全、类型是否正确。

def validate_params(params, required): missing = [k for k in required if k not in params or params[k] is None] if missing: raise ValueError(f"缺少必填参数:{missing}")

这个简单的校验函数帮我省了无数次调试时间。与其等服务器返回400再猜哪里错了,不如在本地就把明显的问题拦下来。

5.4 响应解析:JSON解析失败怎么办

有时候接口返回的不是标准JSON,可能是HTML错误页或者空响应。直接调resp.json()会抛异常:

def safe_json(resp): try: return resp.json() except ValueError: print(f"响应不是合法JSON,原始内容:{resp.text[:500]}") return None

这个safe_json函数我在每个项目里都会写一份,尤其是调第三方接口的时候,对方返回什么格式你控制不了,做好防御总没错。

6. 几个真实场景的完整调用示例

6.1 场景一:调用大模型对话接口

现在很多人调大模型API,流程和普通接口一样,但有几个特殊点。以OpenAI兼容格式的接口为例:

import requests import os API_KEY = os.environ.get("API_KEY") BASE_URL = "https://api.example.com/v1/chat/completions" headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" } payload = { "model": "your-model-name", "messages": [ {"role": "system", "content": "你是一个助手"}, {"role": "user", "content": "介绍一下Python"} ], "temperature": 0.7, "max_tokens": 1000 } resp = requests.post(BASE_URL, headers=headers, json=payload, timeout=60) data = resp.json() print(data["choices"][0]["message"]["content"])

调这类接口最容易踩的坑:模型名写错(返回400)、token超限(返回400)、并发太高被限流(返回429)。另外多轮对话时要把历史消息都带上,否则模型没有上下文。

6.2 场景二:带重试的批量数据抓取

假设你要抓取1000条数据,接口限制每分钟60次,怎么设计?

import time import requests session = requests.Session() session.headers.update({"Authorization": f"Bearer {token}"}) results = [] for i in range(1000): for attempt in range(3): try: resp = session.get(f"https://api.example.com/item/{i}", timeout=10) if resp.status_code == 429: wait = 2 ** attempt print(f"被限流,等待{wait}秒") time.sleep(wait) continue resp.raise_for_status() results.append(resp.json()) break except Exception as e: print(f"第{i}条失败:{e}") time.sleep(1) time.sleep(1) # 主动限速,每分钟约60次

这段代码的核心思路:主动限速(每次请求间隔1秒)+ 遇到429指数退避 + 单条失败不影响整体。实测下来,这种写法比不限速硬冲要稳定得多,总耗时反而更短,因为避免了大量失败重试。

6.3 场景三:token自动刷新的封装

把token管理封装成一个类,用起来最省心:

import time import requests class APIClient: def __init__(self, client_id, client_secret): self.client_id = client_id self.client_secret = client_secret self.token = None self.expire_at = 0 def _refresh_token(self): resp = requests.post("https://api.example.com/oauth/token", data={ "grant_type": "client_credentials", "client_id": self.client_id, "client_secret": self.client_secret }) data = resp.json() self.token = data["access_token"] self.expire_at = time.time() + data.get("expires_in", 3600) - 60 def _ensure_token(self): if not self.token or time.time() >= self.expire_at: self._refresh_token() def get(self, url, **kwargs): self._ensure_token() headers = kwargs.pop("headers", {}) headers["Authorization"] = f"Bearer {self.token}" return requests.get(url, headers=headers, timeout=10, **kwargs)

提前60秒刷新token,避免临界点过期。这个封装模式我在多个项目里复用,基本不用再操心token的事。

7. 新手最容易忽略的几个细节

7.1 编码问题:中文乱码的根源

有些接口返回的中文是乱码,原因是requests猜测的编码不对。解决办法是手动指定:

resp.encoding = "utf-8" print(resp.text)

或者直接用resp.content.decode("utf-8")。判断编码是否正确,看resp.encoding的值,如果是ISO-8859-1而实际内容是中文,那基本就是猜错了。

7.2 代理与证书:公司网络环境的特殊处理

公司网络环境下,请求可能走代理,或者遇到自签名证书报SSL错误。代理配置:

proxies = {"http": "http://proxy.company.com:8080", "https": "http://proxy.company.com:8080"} requests.get(url, proxies=proxies)

证书问题(仅限内部可信服务):

requests.get(url, verify=False)

verify=False会关闭证书校验,只在你完全信任目标服务时使用,公网接口千万别这么干。

7.3 请求频率的自我约束

不要因为接口没报429就无限加速。很多接口的限流是滑动窗口,你短时间内冲太多,后面会被惩罚性限流。我的习惯是:即使接口没限制,批量请求也保持每秒不超过5次,给服务器也给自己留余地。

7.4 日志与监控

生产环境调API,一定要有日志。记录每次请求的关键信息,出问题能快速定位。如果调用量大,还要监控成功率、平均耗时、错误分布,这些数据能帮你提前发现接口方的变化。

8. 常见问题速查表

现象可能原因解决方向
401 Unauthorizedtoken缺失、过期或格式错误检查Authorization头,确认Bearer后有空格
403 Forbidden权限不足确认账号是否有该接口权限
404 Not FoundURL写错逐字核对接口地址和路径
429 Too Many Requests请求频率超限指数退避重试,降低请求频率
400 Bad Request参数错误对照文档检查字段名、类型、必填项
502 Bad Gateway服务端网关问题重试,持续失败则联系接口方
连接超时网络问题或服务未启动检查地址、端口、服务状态
JSON解析失败返回非JSON格式打印原始响应排查
中文乱码编码识别错误手动设置encoding为utf-8
SSL证书错误证书校验失败内部服务可verify=False,公网需查证书链

这张表我建议存下来,遇到报错先查表,能省不少搜索时间。

9. 我个人的一些实战体会

调API这件事,说到底就是"理解协议、遵守约定、做好防御"。协议是HTTP,约定是接口文档,防御是超时、重试、异常处理。这三样做好了,90%的问题都能自己解决。

我刚开始学的时候,最大的误区是遇到报错就慌,到处搜"xxx错误怎么解决",而不是静下心来看报错信息本身。其实大部分报错信息已经把原因说得很清楚了,比如"the supported api model names are xxx"直接告诉你模型名不对,"maximum context length exceeded"直接告诉你输入太长。学会读报错,比记住一百个解决方案都有用。

还有一个体会是:不要怕写"啰嗦"的代码。新手总想用最少的行数实现功能,结果异常没处理、超时没设置、日志没打,出了问题两眼一抹黑。宁可多写二十行防御性代码,也不要为了简洁埋下隐患。等你熟练了,自然知道哪些地方可以精简,哪些地方必须保留。

最后分享一个习惯:每调通一个新接口,我都会把请求示例、认证方式、常见报错整理成一个markdown笔记存起来。下次再调类似的接口,直接翻笔记,效率翻倍。这个习惯坚持了几年,现在我的笔记库已经成了自己最值钱的资产之一。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询