通过 OAuth 2.0 认证,集成 Google Docs API,实现文档的创建、读取、更新、格式化和管理。
Google Docs API 集成是一项面向实际任务的技能,主要用于Direct 访问 Google Docs API 使用 OAuth 2. 0. 创建文档,插入和格式文本,并管理文档内容.;Prerequisites.;
从功能定位来看,该技能强调把分散的操作要求整理成清晰、可复用的处理流程,使用户能够围绕既定目标快速准备输入、选择执行方式并获得结构化结果。实际使用前应先确认任务范围、数据来源、运行环境、必要权限和关键参数,再依据技能说明逐步执行;若输入条件不完整,应先补齐信息或采用保守配置,避免因错误假设导致结果偏离需求。
执行过程中需要关注工具调用是否成功、接口或依赖是否可用、输出格式是否符合预期,并对异常提示、缺失字段和边界情况进行处理;涉及批量任务时,还应保存进度,避免中断后重复操作。该技能适合用于一次性任务,也可以接入自动化工作流,与其他技能或上层代理配合完成更完整的业务链路;在组合使用时,应明确每一步的输入输出关系,并避免不同步骤之间出现参数冲突。
使用 OAuth 2.0 直接访问 Google Docs API。支持创建文档、插入与格式化文本,以及管理文档内容。
Google Cloud 项目配置
环境配置
export GOOGLE_CLIENT_ID="your-client-id"
export GOOGLE_CLIENT_SECRET="your-client-secret"
export GOOGLE_REFRESH_TOKEN="your-refresh-token"
使用以下 Python 脚本获取刷新令牌(一次性设置):
import urllib.request
import urllib.parse
import json
import webbrowser
from http.server import HTTPServer, BaseHTTPRequestHandler
import os
# OAuth 配置
CLIENT_ID = os.environ.get('GOOGLE_CLIENT_ID')
CLIENT_SECRET = os.environ.get('GOOGLE_CLIENT_SECRET')
REDIRECT_URI = 'http://localhost:8080'
SCOPES = 'https://www.googleapis.com/auth/documents'
# 步骤 1:获取授权码
auth_url = (
f"https://accounts.google.com/o/oauth2/v2/auth?"
f"client_id={CLIENT_ID}&"
f"redirect_uri={REDIRECT_URI}&"
f"response_type=code&"
f"scope={urllib.parse.quote(SCOPES)}&"
f"access_type=offline&"
f"prompt=consent"
)
print("正在打开浏览器进行授权...")
webbrowser.open(auth_url)
# 步骤 2:捕获授权码
auth_code = None
class OAuthHandler(BaseHTTPRequestHandler):
def do_GET(self):
global auth_code
query = urllib.parse.urlparse(self.path).query
params = urllib.parse.parse_qs(query)
auth_code = params.get('code', [None])[0]
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(b'授权成功!
您可以关闭此窗口。
')
server = HTTPServer(('localhost', 8080), OAuthHandler)
server.handle_request()
# 步骤 3:使用授权码换取 Token
if auth_code:
data = urllib.parse.urlencode({
'code': auth_code,
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'redirect_uri': REDIRECT_URI,
'grant_type': 'authorization_code'
}).encode()
req = urllib.request.Request('https://oauth2.googleapis.com/token', data=data)
response = json.load(urllib.request.urlopen(req))
print(f"\n刷新令牌(Refresh Token): {response['refresh_token']}")
print(f"访问令牌(Access Token): {response['access_token']}")
print(f"\n请设置您的刷新令牌:")
print(f"export GOOGLE_REFRESH_TOKEN=\"{response['refresh_token']}\"")
在调用 API 前,请先获取新的访问令牌:
import urllib.request
import urllib.parse
import json
import os
def get_access_token():
"""使用刷新令牌获取新的访问令牌"""
data = urllib.parse.urlencode({
'client_id': os.environ['GOOGLE_CLIENT_ID'],
'client_secret': os.environ['GOOGLE_CLIENT_SECRET'],
'refresh_token': os.environ['GOOGLE_REFRESH_TOKEN'],
'grant_type': 'refresh_token'
}).encode()
req = urllib.request.Request('https://oauth2.googleapis.com/token', data=data)
response = json.load(urllib.request.urlopen(req))
return response['access_token']
# 缓存供后续复用
access_token = get_access_token()
print(f"访问令牌(Access Token): {access_token}")
https://docs.googleapis.com/v1
所有请求均需在 Authorization 请求头中携带访问令牌:
Authorization: Bearer {access_token}
import urllib.request
import json
import os
# 首先获取访问令牌(使用上方定义的函数)
access_token = get_access_token()
# 获取文档
req = urllib.request.Request('https://docs.googleapis.com/v1/documents/{documentId}')
req.add_header('Authorization', f'Bearer {access_token}')
doc = json.load(urllib.request.urlopen(req))
print(json.dumps(doc, indent=2))
import urllib.request
import json
access_token = get_access_token()
data = json.dumps({'title': 'My New Document'}).encode()
req = urllib.request.Request(
'https://docs.googleapis.com/v1/documents',
data=data,
method='POST'
)
req.add_header('Authorization', f'Bearer {access_token}')
req.add_header('Content-Type', 'application/json')
response = json.load(urllib.request.urlopen(req))
doc_id = response['documentId']
print(f"已创建文档: {doc_id}")
print(f"URL: https://docs.google.com/document/d/{doc_id}/edit")
req = urllib.request.Request(
f'https://docs.googleapis.com/v1/documents/{doc_id}'
)
req.add_header('Authorization', f'Bearer {access_token}')
doc = json.load(urllib.request.urlopen(req))
# 在文档开头插入文本
updates = {
'requests': [
{
'insertText': {
'location': {'index': 1},
'text': 'Hello, World!\n\n'
}
}
]
}
data = json.dumps(updates).encode()
req = urllib.request.Request(
f'https://docs.googleapis.com/v1/documents/{doc_id}:batchUpdate',
data=data,
method='POST'
)
req.add_header('Authorization', f'Bearer {access_token}')
req.add_header('Content-Type', 'application/json')
response = json.load(urllib.request.urlopen(req))
{
'requests': [
{
'insertText': {
'location': {'index': 1},
'text': 'Your text here'
}
}
]
}
{
'requests': [
{
'updateTextStyle': {
'range': {
'startIndex': 1,
'endIndex': 10
},
'textStyle': {
'bold': True,
'italic': True,
'fontSize': {
'magnitude': 14,
'unit': 'PT'
}
},
'fields': 'bold,italic,fontSize'
}
}
]
}
{
'requests': [
{
'insertTable': {
'location': {'index': 1},
'rows': 3,
'columns': 4
}
}
]
}
{
'requests': [
{
'replaceAllText': {
'containsText': {
'text': '{{placeholder}}',
'matchCase': True
},
'replaceText': 'Actual value'
}
}
]
}
{
'requests': [
{
'deleteContentRange': {
'range': {
'startIndex': 1,
'endIndex': 50
}
}
}
]
}
{
'requests': [
{
'insertPageBreak': {
'location': {'index': 1}
}
}
]
}
{
'requests': [
{
'updateParagraphStyle': {
'range': {
'startIndex': 1,
'endIndex': 50
},
'paragraphStyle': {
'namedStyleType': 'HEADING_1',
'alignment': 'CENTER'
},
'fields': 'namedStyleType,alignment'
}
}
]
}
import urllib.request
import urllib.parse
import json
import os
def get_access_token():
"""获取新的访问令牌"""
data = urllib.parse.urlencode({
'client_id': os.environ['GOOGLE_CLIENT_ID'],
'client_secret': os.environ['GOOGLE_CLIENT_SECRET'],
'refresh_token': os.environ['GOOGLE_REFRESH_TOKEN'],
'grant_type': 'refresh_token'
}).encode()
req = urllib.request.Request('https://oauth2.googleapis.com/token', data=data)
response = json.load(urllib.request.urlopen(req))
return response['access_token']
def create_document(title, access_token):
"""创建新文档"""
data = json.dumps({'title': title}).encode()
req = urllib.request.Request(
'https://docs.googleapis.com/v1/documents',
data=data,
method='POST'
)
req.add_header('Authorization', f'Bearer {access_token}')
req.add_header('Content-Type', 'application/json')
response = json.load(urllib.request.urlopen(req))
return response['documentId']
def batch_update(doc_id, requests, access_token):
"""对文档执行批量更新"""
data = json.dumps({'requests': requests}).encode()
req = urllib.request.Request(
f'https://docs.googleapis.com/v1/documents/{doc_id}:batchUpdate',
data=data,
method='POST'
)
req.add_header('Authorization', f'Bearer {access_token}')
req.add_header('Content-Type', 'application/json')
return json.load(urllib.request.urlopen(req))
# 主流程
access_token = get_access_token()
# 创建文档
doc_id = create_document('Project Report', access_token)
print(f"已创建: https://docs.google.com/document/d/{doc_id}/edit")
# 添加带格式的内容
requests = [
# 插入标题
{
'insertText': {
'location': {'index': 1},
'text': 'Project Report\n\n'
}
},
# 将标题设为一级标题
{
'updateParagraphStyle': {
'range': {'startIndex': 1, 'endIndex': 15},
'paragraphStyle': {'namedStyleType': 'HEADING_1'},
'fields': 'namedStyleType'
}
},
# 添加正文
{
'insertText': {
'location': {'index': 16},
'text': 'Executive Summary\n\nThis report covers...\n\n'
}
},
# 格式化章节标题
{
'updateParagraphStyle': {
'range': {'startIndex': 16, 'endIndex': 34},
'paragraphStyle': {'namedStyleType': 'HEADING_2'},
'fields': 'namedStyleType'
}
}
]
batch_update(doc_id, requests, access_token)
print("文档更新成功!")
endOfSegmentLocation'fields': 'bold,fontSize,foregroundColor'| 状态码 | 含义 |
|---|---|
| 400 | 错误请求 — 请求格式无效 |
| 401 | 未授权 — 访问令牌无效或已过期 |
| 403 | 禁止访问 — 权限不足 |
| 404 | 未找到 — 文档不存在 |
| 429 | 请求频率超限 — 请求次数过多 |
访问令牌有效期为 1 小时。若收到 401 错误,请刷新令牌:
try:
# 发起 API 请求
req = urllib.request.Request(url)
req.add_header('Authorization', f'Bearer {access_token}')
response = urllib.request.urlopen(req)
except urllib.error.HTTPError as e:
if e.code == 401:
# 刷新令牌后重试
access_token = get_access_token()
req = urllib.request.Request(url)
req.add_header('Authorization', f'Bearer {access_token}')
response = urllib.request.urlopen(req)
令牌管理
批量操作
索引计算
endOfSegmentLocation速率限制