使用requests调用Twitter API时出现Error 400 Bad Request问题排查
问题原因分析
你的400 Bad Request错误主要来自以下几个关键问题:
1. OAuth请求头格式完全错误
OAuth 1.0的授权头应该是单个Header字段,格式为OAuth oauth_consumer_key="xxx", oauth_nonce="xxx", oauth_signature="xxx", ...,而不是把每个OAuth参数拆分成独立的Header键。你的代码里把每个参数都作为单独的Header,这会被API识别为无效格式。
2. OAuth参数赋值错误
- 你把
oauth_token_secret错误赋值给了oauth_token字段,两者是完全不同的参数:oauth_token是第一个程序生成的令牌值,oauth_token_secret是对应的密钥。 - 你直接用
oauth_token作为oauth_signature,这完全不符合要求——签名需要通过HMAC-SHA1算法,结合请求方法、请求URL、所有请求参数、consumer_secret和oauth_token_secret生成,不能直接用令牌代替。
3. 缺少必要的签名生成逻辑
手动构造OAuth 1.0签名需要严格遵循算法步骤,包括参数排序、编码、拼接基字符串、用密钥签名等,你的代码完全跳过了这个关键步骤,导致签名无效。
改进方案
方案一:继续使用requests_oauthlib(推荐,先验证功能再移植)
既然你已经用OAuth1Session生成了合法的令牌,直接用这个库来发送请求会自动处理OAuth签名和头格式,避免手动构造的错误:
import os import json from requests_oauthlib import OAuth1Session consumer_key = os.environ.get("CONSUMER_KEY") consumer_secret = os.environ.get("CONSUMER_SECRET") # 替换成第一个程序生成的令牌 oauth_token = "<your_oauth_token>" oauth_token_secret = "<your_oauth_token_secret>" payload = {"text": "Hello world1!"} # 初始化OAuth会话 oauth = OAuth1Session( consumer_key, client_secret=consumer_secret, resource_owner_key=oauth_token, resource_owner_secret=oauth_token_secret ) response = oauth.post("https://api.twitter.com/2/tweets", json=payload) if response.status_code != 201: raise Exception(f"Request error: {response.status_code} {response.text}") print(f"Response code: {response.status_code}") print(json.dumps(response.json(), indent=4, sort_keys=True))
方案二:手动构造请求(适合嵌入式移植场景)
如果必须手动构造请求,需要修正以下几点:
- 合并OAuth参数到单个Header:
正确的Header格式示例:import string import random import time import requests import hmac import hashlib import base64 from urllib.parse import quote, urlencode def generate_oauth_signature(method, url, oauth_params, payload, consumer_secret, token_secret): # 合并所有参数(OAuth参数和请求体参数) all_params = oauth_params.copy() all_params.update(payload) # 按参数名排序并编码 sorted_params = sorted(all_params.items()) encoded_params = urlencode(sorted_params, quote_via=quote) # 构造基字符串 base_string = f"{method.upper()}&{quote(url)}&{quote(encoded_params)}" # 构造签名密钥 signing_key = f"{quote(consumer_secret)}&{quote(token_secret)}" # 生成HMAC-SHA1签名并Base64编码 signature = hmac.new(signing_key.encode(), base_string.encode(), hashlib.sha1).digest() return base64.b64encode(signature).decode() # 生成OAuth参数 oauth_params = { "oauth_consumer_key": consumer_key, "oauth_nonce": "".join(random.choice(string.ascii_letters + string.digits) for _ in range(32)), "oauth_signature_method": "HMAC-SHA1", "oauth_timestamp": str(int(time.time())), "oauth_token": oauth_token, "oauth_version": "1.0" } # 生成合法签名 oauth_params["oauth_signature"] = generate_oauth_signature( "POST", "https://api.twitter.com/2/tweets", oauth_params, payload, consumer_secret, oauth_token_secret ) # 拼接成单个Authorization头 auth_header = "OAuth " + ", ".join([f'{k}="{v}"' for k, v in oauth_params.items()]) headers = {"Authorization": auth_header, "Content-Type": "application/json"} # 发送请求 response = requests.post("https://api.twitter.com/2/tweets", headers=headers, json=payload) - 修正参数对应关系:确保
oauth_token和oauth_token_secret赋值正确,不要搞混。
内容的提问来源于stack exchange,提问作者Kampi
相关产品推荐
相关产品推荐

