带有服务帐户的 Google Calendar Python API 未返回任何结果

Posted

技术标签:

【中文标题】带有服务帐户的 Google Calendar Python API 未返回任何结果【英文标题】:Google Calendar Python API with service account returning no results 【发布时间】:2014-09-01 11:36:27 【问题描述】:

我正在尝试下载我的用户可以看到的 Google 日历列表。我已经设置了一个服务帐户,并且我有这个代码(它适用于 Analytics API 的不同帐户):

from apiclient.discovery import build
from oauth2client.client import SignedJwtAssertionCredentials
import pprint

def prepare_credentials():
    f = file(keyFilePath, 'rb')
    key = f.read()
    f.close()
    credentials = SignedJwtAssertionCredentials(
        service_account_email_address, key,
        scope='https://www.googleapis.com/auth/calendar.readonly')
    return credentials
http = httplib2.Http()
credentials = prepare_credentials()
http = credentials.authorize(http)
service = build('calendar', 'v3', http=http)
httpRequest = service.calendarList().list()
data = httpRequest.execute()
pprint.pprint(data)

结果:

u'etag': u'"YXs7dfDSFSFD9F0k/sofIhuT4dSLyxKaRH7vNpx5BOEU"',
u'items': [],
u'kind': u'calendar#calendarList',
u'nextSyncToken': u'00001405024697194000'

问题是,我知道我可以访问日历,而且我什至使用过“试试看!”本页部分:https://developers.google.com/google-apps/calendar/v3/reference/calendarList/list,返回预期结果,约10个日历。我尝试了不同版本的范围参数('https://www.googleapis.com/auth/calendar.readonly',和 ['https://www.googleapis.com/auth/calendar','https://www.googleapis.com/auth/calendar.readonly']),我还尝试将我自己的电子邮件地址添加为“prn”和“子”参数,所有这些都给我“拒绝访问”错误。我不知道为什么我可以使用我的服务帐户获得授权,但是当这些结果可以通过不同的身份验证方法清楚地查看时,我无法实际查看结果。

我做错了什么?

【问题讨论】:

【参考方案1】:

只是想用我最终使用的解决方案来更新这个问题。

如果您希望服务帐户能够在日历上拥有所有者权限,似乎唯一的方法就是使用 API 创建辅助日历作为服务帐户进行身份验证时 (https://developers.google.com/google-apps/calendar/v3/reference/calendars/insert)。服务帐户没有任何它是其所有者的主日历,但它将是新的辅助日历的所有者。

然后,您可以使用访问控制规则 API 端点 (https://developers.google.com/google-apps/calendar/v3/reference/acl/insert) 与您自己的 Google 帐户(或任何可以登录以在浏览器中查看日历的非服务帐户)共享此日历。新用户也可以设置为所有者。

编辑:下面的解决方案不准确。

这不是问题,但我在任何地方都找不到它,我认为人们可能想知道,所以我也将在这里回答:

如果您希望与会者在您插入或更新活动时收到电子邮件通知,您需要做的不仅仅是将“sendNotifications”参数设置为 True。与该用户共享日历后,该用户还需要进入您服务帐户日历的“日历设置”,并在“提醒和通知”选项卡中激活电子邮件通知。

编辑:这是不准确的。任何更改设置以在此日历上接收通知的人都会收到通知,无论他们是否被邀请参加活动。我还没有找到通过 API 邀请人们参加活动的方法服务帐户,并且只有受邀者会收到邀请电子邮件。我会在 *** 上问另一个问题。

【讨论】:

【参考方案2】:

只有在使用该用户的凭据执行呼叫时,才能看到特定用户的日历列表。服务帐户将始终为您提供属于该服务帐户的日历列表。如果您想访问您的日历列表,我建议您使用 Oauth2 令牌作为您的帐户。

【讨论】:

使用 OAuth 的问题在于,我相信夜间脚本无法在我不在时访问日历,除非我遗漏了什么。我需要一个 Python 脚本,以便能够在一夜之间访问和更改这些数据,而无需用户干预。 我是个白痴,与我的服务帐户共享日历完全有效!谢谢!! 看来我只能授予它读取权限。您是否知道一种方法也可以授予服务帐户写入权限? 有刷新 Oauth 令牌,允许在用户不在线时检索凭据(在第一次令牌交换时,您会获得访问令牌和刷新令牌,然后您可以使用它们来获得更多访问权限令牌)。通常,您应该能够向服务帐户授予所有权限,除非您的帐户位于已限制最大权限的域中。 关于令牌的更多细节在这里:developers.google.com/accounts/docs/OAuth2WebServer#refresh 请注意,您需要使 access_type 离线才能获得刷新令牌。【参考方案3】:

经过一番挖掘,您可以允许域外的用户(api 服务帐户)访问谷歌日历。

检查以下设置:Google Admin、Google Apps、日历、共享设置 并选择以下选项之一:

( ) Only free/busy information (hide event details) Default
( ) Share all information, but outsiders cannot change calendars  
(X) Share all information, and outsiders can change calendars
( ) Share all information, and allow managing of calendars

然后您可以在Share with specific people下的各个日历共享设置中将您的日历共享到api服务中

这对某人有什么帮助。

【讨论】:

【参考方案4】:

最初,我自己也在为此苦苦挣扎。但我认为我已经取得了足够的进展,可以提供一些帮助。

看起来每个服务帐户都分配了一个唯一的“client_email”。此值是包含在可下载 JSON 文件中的字段,来自项目的 API -> Credentials 部分。格式类似于 xxxxx-yyyyy@developer.gserviceaccount.com。在上面的代码中,您似乎通过“service_account_email_address”变量引用了这个值。

由于 client_email 地址与您的个人电子邮件地址不同,因此您引用的主日历和辅助日历不是一回事。默认情况下,我认为服务帐户电子邮件没有与之关联的任何日历,这就是您在结果中看不到任何日历的原因。当您添加两个日历然后使用 calendarList.list() 方法时,这一点变得很明显。但在添加任何日历之前,您必须将范围从只读更改为读/写。您可以通过将代码更改为:

## Original Scope needs to be changed...
#scope='https://www.googleapis.com/auth/calendar.readonly'
## Must allow the ability to write to calendars...
scope='https://www.googleapis.com/auth/calendar'

一旦您允许读/写,您可以添加两个日历,然后通过执行以下操作列出它们:

def createCalendar():  ## Create the calendar...
    calendar = 
        'summary': 'Calendar used to communicate lease end dates.',
        'timeZone': 'America/Los_Angeles'
        
    created_calendar = service.calendars().insert(body=calendar).execute()
    print "Created Calendar ID:  ", created_calendar['id']

## In main block of code, call the createCalendar() twice...
createCalendar()
createCalendar()
## Show the current list of calendars (should have 2 listed)...
httpRequest = service.calendarList().list()
data = httpRequest.execute()
pprint.pprint(data)

创建日历后,再次尝试运行您的代码,您现在应该会在“数据”结果中看到列出的两个新日历。但是,这两个日历仍然只与服务帐户相关联。因此,如果您登录个人电子邮件 (myEmail@gmail.com),这些日历将不会显示在您的个人资料 (http://calendar.google.com) 中。

要让它们显示在您的个人日历列表中,您必须授予您的个人电子邮件地址查看和/或修改日历的权限。为此,您必须插入一个 ACL,为您的个人电子邮件 (myEmail@gmail.com) 提供适当的权限。

但是,如果您尝试采取其他方式(允许服务帐户访问您的个人日历)。您必须登录您的个人电子邮件帐户 (myEmail@gmail.com),与服务帐户 client_email 共享特定日历,并分配适当的权限级别(进行更改和管理共享等)。与服务帐户共享日历后,日历将列在您的 calendarList().list() 结果中,您应该能够按预期查看事件。

【讨论】:

查看各个日历事件时,必须指定要使用的 calendarId。默认情况下,它使用不正确的“主要”。 eventsResult = service.events().list(calendarId=gCalendarId, singleEvents=True, orderBy='startTime').execute() events = eventsResult.get('items', [])

以上是关于带有服务帐户的 Google Calendar Python API 未返回任何结果的主要内容,如果未能解决你的问题,请参考以下文章

使用服务帐户身份验证访问 Google Calendar API

从google-calendar-API获取详细信息,无需帐户关联

G Suite帐户在Calendar API中缺少displayName参数

使用服务帐户插入 Google 日历条目

带有 Google 服务帐户主题的 generate_access_token()

Google Calendar API 不适用于 Google Apps for Work 中的日历