尝试使用服务帐户从Python 3.7访问Gsheet webapp

2024-06-07 12:22:12 发布

您现在位置:Python中文网/ 问答频道 /正文

Code is not erroring according to logsPrevious related question

配置

  • Python 3.7
  • 谷歌认证1.11.0
  • google auth oauthlib 0.4.1
  • 谷歌服务帐户凭据
  • 与ServiceAccount共享的GSheet
  • GSheet作为Webapp公开
  • Gsheet有一个doGet方法,它读取工作表并将其转换为CSV并返回
  • 当您直接在浏览器中点击它时,它可以正常工作
  • 使用python时不起作用

可以看到python对webapp的调用-可以看到webapp的处理。获取返回给客户端的响应,该响应声明-

    b'<!DOCTYPE html><html><head><link rel="shortcut icon" 
    href="//ssl.gstatic.com/docs/script/images/favicon.ico"><title>Error</title><style 
    type="text/css">body {background-color: #fff; margin: 0; padding: 0;}.errorMessage {font- 
   family: Arial,sans-serif; font-size: 12pt; font-weight: bold; line-height: 150%; padding-top: 
    25px;}</style></head><body style="margin:20px"><div><img alt="Google Apps Script" 
    src="//ssl.gstatic.com/docs/script/images/logo.png"></div><div style="text- 
   align:center;font-family:monospace;margin:50px auto 0;max-width:600px">We&#39;re sorry, a 
    server error occurred. Please wait a bit and try again.</div></body></html>'

日志中没有错误

执行此工作的Python代码:-

from __future__ import print_function
from google.oauth2 import service_account
from google.auth.transport.urllib3 import AuthorizedHttp

SCOPES = ['https://www.googleapis.com/auth/spreadsheets',
      'https://www.googleapis.com/auth/drive',
      'https://www.googleapis.com/auth/drive.readonly']
credentials = service_account.Credentials.from_service_account_file(
'service_account.json', scopes=SCOPES)


def main():
try:
    authed_http = AuthorizedHttp(credentials)

    response = authed_http.request(
        'GET', "https://script.google.com/macros/s/AKfycbzmr5-g2ZIlsGFL5SDYdCYEKmhyqH_- 
QcAhFeBnfN0_D291kRA/exec")

    print(response._body)
except BaseException as err_base2:
    print(err_base2)

if __name__ == '__main__':
   main()

是否支持这种方法。感觉我错过了一些明显的东西

GSheet项目的服务帐户角色权限 Service Account Role Permissions to GSheet Project

GSheet的服务帐户权限 enter image description here

将一些日志记录到请求中,但它不再告诉我们。 调试:google.auth.transport.urllib3:发出请求:POST https://oauth2.googleapis.com/token 调试:urllib3.connectionpool:正在启动新的HTTPS连接(1): oauth2.googleapis.com:443 调试:urllib3.connectionpool:https://oauth2.googleapis.com:443POST /令牌HTTP/1.1“200无 调试:urllib3.connectionpool:正在启动新的HTTPS连接(1): script.google.com:443 调试:urllib3.connectionpool:https://script.google.com:443“获取 /宏/s/AKfycbzmr5-g2ZIlsGFL5SDYdCYEKmhyqH_uuqcahfebnfn0_D291kRA/exec HTTP/1.1“500无


Tags: fromhttpsdivcomauthstyleservicegoogle
1条回答
网友
1楼 · 发布于 2024-06-07 12:22:12

我犯了和你一样的错误:

We're sorry, a server error occurred. Please wait a bit and try again.

然后我使用了domain-wide delegation,它允许service account模拟G Suite domain中的任何用户。您必须拥有一个G套件帐户才能使用域范围的授权,如文档所述:

If you have a G Suite domain—if you use G Suite, for example—an administrator of the G Suite domain can authorize an application to access user data on behalf of users in the G Suite domain.

那么现在,为什么需要模拟用户(真实的人)?这是因为服务帐户是一个机器人(不是真人),用于服务器到服务器的交互,使你的应用程序调用Google API成为可能,尽管服务帐户有一个名为client_email的参数,它的结构类似于name@project-randomnumber.iam.gserviceaccount.com,但它不是一封真正属于真人的电子邮件(我知道有点混乱)

如果在第4步中检查Deploying a script as a web app,则状态为:

Under Execute the app as, select whose authorization the app should run with: your account (the developer's) or the account of the user who visits the app (see permissions).

因此,您不能使用“部署为web应用”提供的URL。要在代码中实现域范围的委派,您可以这样做:

from __future__ import print_function
from google.oauth2 import service_account
from google.auth.transport.urllib3 import AuthorizedHttp

SCOPES = ['https://www.googleapis.com/auth/spreadsheets', 'https://www.googleapis.com/auth/drive']
SERVICE_ACCOUNT_FILE = 'service_account.json'
# The user we want to "impersonate"
USER_EMAIL = "name@domain"

def main():
    try:
        credentials = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
        delegated_credentials = credentials.with_subject(USER_EMAIL)
        authed_http = AuthorizedHttp(delegated_credentials)
        response = authed_http.request('GET', "https://script.google.com/macros/s/<your-id>/exec")
        print(response._body)
    except BaseException as err_base2:
        print(err_base2)

if __name__ == '__main__':
   main()

相关问题 更多 >

    热门问题