使用Python服务账号调用Blogger/Google API时出现OSError错误
解决Blogger API服务账号认证时的OSError: Errno 9 Bad file descriptor错误
问题分析
在Debian系统使用google-api-python-client 2.65.0,通过服务账号JSON密钥访问Blogger API时,调用googleapiclient.discovery.build()触发OSError: [Errno 9] Bad file descriptor错误。已确认凭证读取正常,浏览器可访问API发现文档,但代码执行失败,报错根源在底层socket发送数据时出现无效文件描述符。
核心原因及解决方案
1. 混用新旧OAuth2凭证库导致冲突
代码同时导入了已弃用的oauth2client库和官方推荐的google-oauth2库,两者在HTTP请求处理的底层逻辑存在冲突,可能导致socket资源异常。
修复操作:
完全替换oauth2client的使用,改用google.oauth2.service_account生成凭证:
# 删除旧的oauth2client导入 # from oauth2client import client # from oauth2client.service_account import ServiceAccountCredentials # 替换为google官方新库 from google.oauth2 import service_account
生成凭证的代码修改为:
credentials = service_account.Credentials.from_json_keyfile_dict( key_dict, scopes=SCOPES ) # 如果需要模拟博客所有者访问(部分Blogger接口要求),添加subject参数 # credentials = credentials.with_subject("your-blog-owner-email@example.com")
2. httplib2版本过旧
报错中使用的httplib2版本为0.10.3,该版本存在与新版google-api-python-client不兼容的网络请求bug,会导致socket描述符异常。
修复操作:
升级httplib2到最新兼容版本:
pip install --upgrade httplib2
3. 网络/代理配置问题
虽然浏览器能访问API地址,但Python进程可能使用了不同的代理配置,或者系统socket资源被异常占用。
排查操作:
- 在终端测试Python能否访问API发现文档:
import urllib.request response = urllib.request.urlopen("https://www.googleapis.com/discovery/v1/apis/blogger/v3/rest") print(response.status)
- 如果使用代理,确保Python进程的环境变量(
HTTP_PROXY/HTTPS_PROXY)配置正确,或者在代码中显式设置代理:
from googleapiclient.discovery import build import httplib2 http = httplib2.Http(proxy_info=httplib2.ProxyInfo( httplib2.socks.PROXY_TYPE_HTTP, "proxy-host", 8080 )) service = build('blogger', 'v3', credentials=credentials, http=http, static_discovery=False)
修改后的完整代码示例
import json from google.oauth2 import service_account import googleapiclient.discovery SCOPES = ['https://www.googleapis.com/auth/blogger'] def get_key_dict(): SERVICE_ACCOUNT_FILE = '/path/to/myproject/apis/blogger/private_secrets.json' with open(SERVICE_ACCOUNT_FILE, 'r') as keyfile: return json.load(keyfile) key_dict = get_key_dict() credentials = service_account.Credentials.from_json_keyfile_dict(key_dict, scopes=SCOPES) # 如果需要模拟特定用户访问,取消注释并替换邮箱 # credentials = credentials.with_subject("blog-owner@example.com") service = googleapiclient.discovery.build('blogger', 'v3', credentials=credentials, static_discovery=False) try: users = service.users() thisuser = users.get(userId="self").execute() print(f"This user's display name is: {thisuser['displayName']}") blogs = service.blogs() thisusersblogs = blogs.listByUser(userId="self").execute() for blog in thisusersblogs["items"]: print(f"The blog named '{blog['name']}' is at: {blog['url']}") posts = service.posts() for blog in thisusersblogs["items"]: print(f"The posts for {blog['name']}:") request = posts.list(blogId=blog["id"]) while request is not None: posts_doc = request.execute() if "items" in posts_doc and posts_doc["items"]: for post in posts_doc["items"]: print(f" {post['title']} ({post['url']})") request = posts.list_next(request, posts_doc) except Exception as e: print(f"Error occurred: {str(e)}")
内容的提问来源于stack exchange,提问作者madtyn
相关产品推荐
相关产品推荐

