仅拥有ClientEmail、Key和CalendarEmail,如何用Google Apps Script访问谷歌日历?
问题解答
一、CalendarEmail就是日历ID
谷歌日历的ID本身就是对应邮箱地址——不管是个人主日历(比如xxx@gmail.com)还是共享日历(比如xxx@group.calendar.google.com),这个邮箱地址就是合法的日历ID。你之前用CalendarApp.getCalendarById(CalendarEmail)返回null,根本原因是当前脚本用的是执行者个人账号的权限,而不是你手里的服务账号权限,所以没权限访问目标日历。
二、用现有信息完全能连接日历,OAuth2报错是私钥格式问题
你手里的ClientEmail(服务账号邮箱)、Key(服务账号私钥)、CalendarEmail(目标日历ID)足够完成访问,不需要额外信息。OAuth2报Exception: Invalid argument: key是因为私钥格式不对,按下面步骤修正:
1. 修正私钥格式
服务账号的私钥必须是完整的PEM格式,要包含首尾标记和正确换行,比如:
const privateKey = `-----BEGIN PRIVATE KEY----- 你的2200字符密钥内容(要保留原有的换行,别把所有内容挤成一行) -----END PRIVATE KEY-----`;
如果你的key是从JSON密钥文件里复制的,注意把转义的\n换成实际换行,或者直接用模板字符串包裹原内容。
2. 正确配置OAuth2服务
调整后的OAuth2代码示例:
function getCalendarService() { const CLIENT_EMAIL = "你的服务账号邮箱"; const PRIVATE_KEY = `-----BEGIN PRIVATE KEY----- 你的完整私钥内容 -----END PRIVATE KEY-----`; const CALENDAR_EMAIL = "目标日历邮箱(也就是日历ID)"; const SCOPES = ["https://www.googleapis.com/auth/calendar.readonly"]; // 只读权限够的话用这个,更安全 return OAuth2.createService("CalendarService") .setTokenUrl("https://oauth2.googleapis.com/token") // 新版API用这个URL更可靠 .setPrivateKey(PRIVATE_KEY) .setIssuer(CLIENT_EMAIL) .setSubject(CALENDAR_EMAIL) // 模拟访问该日历的身份 .setScope(SCOPES); }
3. 给服务账号开日历访问权限
- 打开目标日历的设置页面,找到「共享权限」,添加你的服务账号邮箱(ClientEmail),给它「查看所有事件」或更高权限。
- 这步必须做,不然服务账号根本没权限碰这个日历。
4. 调用API获取事件并写入表格
授权完成后,就可以用OAuth2的token调用Calendar API拿事件,再写到电子表格里:
function fetchCalendarEvents() { const service = getCalendarService(); if (!service.hasAccess()) { throw new Error("服务账号没权限:" + service.getLastError()); } // 设置时间范围 const startDate = new Date("2024-01-01"); const endDate = new Date("2024-12-31"); const calendarId = encodeURIComponent("你的目标日历邮箱"); const url = `https://www.googleapis.com/calendar/v3/calendars/${calendarId}/events?timeMin=${encodeURIComponent(startDate.toISOString())}&timeMax=${encodeURIComponent(endDate.toISOString())}`; const response = UrlFetchApp.fetch(url, { headers: { Authorization: "Bearer " + service.getAccessToken() } }); const events = JSON.parse(response.getContentText()); // 把事件写到电子表格 const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(); sheet.clearContents(); sheet.appendRow(["标题", "开始时间", "结束时间"]); events.items.forEach(event => { sheet.appendRow([ event.summary || "无标题", event.start.dateTime || event.start.date, event.end.dateTime || event.end.date ]); }); }
三、总结
- 不需要额外的日历ID,CalendarEmail就是有效的日历ID。
- 之前CalendarApp调用失败是因为权限不匹配,必须用服务账号的OAuth2权限访问。
- OAuth2报错的核心是私钥格式错误,修正格式+给服务账号开日历权限后,就能正常获取事件。
内容的提问来源于stack exchange,提问作者Dan Muntean
相关产品推荐
相关产品推荐

