我想用Python创建一个图像,matplotlib图,然后自动插入到google幻灯片演示文稿中。
我已经有了一个自动的google幻灯片演示。它更新了大量的文本,还用一些文本交换了一些设置的照片,即这5张照片总是相同的。使用Google API文档,弄清楚所有这些花了相当多的时间。我非常依赖Google API Github Documentation、Core Python Programming Blog和Replacing text & images with the Google Slides API。
虽然有一些与Google Service API帐户共享图像的例子,但我从来没有让它工作过。我只能通过使我的google驱动器中的图像对公众可见来让它将文本更新为图像。否则(例如,当服务帐户有访问权限,但公众没有访问权限时),我会得到这个错误:
<HttpError 400 when requesting [image URL] returned "Invalid requests[2].replaceAllShapesWithImage: There was a problem retrieving the image. The provided image should be publicly accessible, within size limit, and in supported formats.". Details: "Invalid requests[2].replaceAllShapesWithImage: There was a problem retrieving the image. The provided image should be publicly accessible, within size limit, and in supported formats.">
如果我将图像共享更改为公共,它就会起作用。
我知道有时谷歌的应用程序接口就是不起作用,正如here和我所看到的那样,我也会遇到这个错误。只需等待几个小时或等到明天,它就会再次工作,只要图像是公开的。通常,当出现这种情况时,我会得到以下错误:
HttpError 400 when requesting [image URL] "Invalid requests[0].replaceAllShapesWithImage[or createImage]: Access to the provided image was forbidden."
如何使用google API将在Python中创建的图像保存到google驱动器文件夹中,并使其可公开访问?或!更妙的是,当照片与创建幻灯片的google服务帐户共享时,如何将照片保存到google驱动器文件夹中,并使图像批量更新工作,但没有将其完全公开?
这是我到目前为止的代码:
from matplotlib import pyplot as plt
import json
import unidecode
from oauth2client.service_account import ServiceAccountCredentials
from httplib2 import Http
from apiclient import discovery
import pandas as pd
plt.bar(['hi', 'how', 'are', 'you'], [3,6,3,8], color="skyblue")
plt.savefig('test_bar_plot.png')
# google slides authorization
json_key = json.load(open('../../creds/creds_file.json'))
scope = ['https://spreadsheets.google.com/feeds', 'https://www.googleapis.com/auth/drive'] #would love to reduce scope if possible
creds = ServiceAccountCredentials.from_json_keyfile_dict(json_key, scope)
# file to copy for slide deck
TMPLFILE='test_run_slide_deck_template'
HTTP = creds.authorize(Http())
DRIVE = discovery.build('drive', 'v3', http=HTTP)
SLIDES = discovery.build('slides', 'v1', http=HTTP)
# find slide template, delete copy if it already exists, then make a new copy
file_list = pd.DataFrame(DRIVE.files().list().execute().get('files'))
tempfileSTR = 'test_run_slide_deck'
new_file = '_copy'
try:
DECK_ID= file_list[file_list.name == tempfileSTR+new_file]['id'].values[0]
print("** Found File ID for %s" % (tempfileSTR+new_file))
file = DRIVE.files().delete(fileId=DECK_ID).execute()
print("** Deleting file for %s" % (tempfileSTR+new_file))
except IndexError:
print("** Did NOT Find File ID for %s" % (tempfileSTR+new_file))
rsp = DRIVE.files().list(q="name='%s'" % TMPLFILE).execute().get('files')[0]
DATA = {'name': tempfileSTR+new_file, 'parents':['14CvIXemPv5fGcLaiTj_HfMXZS_OKI6Lv']}
print('** Copying template %r as %r' % (rsp['name'], DATA['name']))
DECK_ID = DRIVE.files().copy(body=DATA, fileId=rsp['id']).execute().get('id')
# get the presentation and slides
presentation = SLIDES.presentations().get(presentationId=DECK_ID).execute()
slides = presentation.get('slides')
# create dictionary of what to update for text
report_dict = {"{{change_to_hey_SO}}":"Hi StackOverflow"
,"{{change_to_farewell}}":"Bye StackOverflow"}
reqs =[]
for key in report_dict:
reqs.append({'replaceAllText': {'containsText': {'text': key},
'replaceText': report_dict[key]}},)
# create dictionary for what to update for an image already in the drive
def find_img(IMG_FILE):
rsp = DRIVE.files().list(q="name='%s'" % IMG_FILE).execute().get('files')[0]
baseURL = "http://drive.google.com/uc?export=view&id="
return baseURL+rsp['id']
def image_section(image_replace_text, image_file_name):
image_json = {
'replaceAllShapesWithImage': {
'imageUrl': find_img(image_file_name),
'imageReplaceMethod': 'CENTER_INSIDE',
'containsText': {
'text': image_replace_text,
'matchCase': True
}
}
}
return image_json
# add image update to the list for batch updating
reqs.append(image_section('{{make_me_a_rooster}}', 'rooster_test.png'))
# take saved bar plot, 'test_bar_plot.png', upload it to google drive, make it public or make sharing it with the service account work and use the image_section and find_image functions to add it to the reqs updating list for the batch update below.
# [added edit] this uploads the image file, to the same shared folder as the slide deck (shared with user and service account).
# but does not make the image public and I get the same error as above when it's not public.
file_metadata = {'name': 'test_bar_plot.png', 'parents':['14CvIXemPv5fGcLaiTj_HfMXZS_OKI6Lv']}
media = discovery.MediaFileUpload('test_bar_plot.png', mimetype='image/png')
file = DRIVE.files().create(body=file_metadata,
media_body=media,
fields='id').execute()
# add to reqs list
reqs.append(image_section('{{update_chart}}', 'test_bar_plot.png')) #, img_lookup
条形图
更新前的幻灯片
SLIDES.presentations().batchUpdate(body={'requests': reqs}, presentationId=DECK_ID).execute()
print('DONE')
更新后的幻灯片
This documentation是查找MediaFileUpload的主要链接,在示例中没有显示其他参数,我很难找到其他地方是否列出了其他参数。
发布于 2021-02-26 14:21:13
您遇到了问题,因为服务帐户是而不是域的一部分,因此权限将与任何不相关的用户相同。Like the documentation states
与用户帐户不同,
服务帐户不是您的Google Workspace域的成员。如果您与Google Workspace域中的所有成员共享Google Workspace资产,如文档或事件,则不会与服务帐户共享这些资产。同样,由服务帐户创建的Google Workspace资产也不是在您的Google Workspace域中创建的。因此,您的Google Workspace和云身份管理员无法拥有或管理这些资产。
发生在您身上的情况是,幻灯片的所有者没有添加图像的权限(您正尝试将其添加为链接)。当服务帐户尝试访问未公开的幻灯片时,也会发生同样的情况。
让它工作的最简单的方法是impersonate a user,这样服务帐户就可以像用户一样进行API调用。另一种选择是有一个共享文件夹/驱动器,服务帐户和用户都可以在其中添加切片和图像(文件将自动共享给所有帐户)。您也可以在每次上传或创建文件时手动与其他帐户共享该文件。
发布于 2021-08-31 08:43:28
我还想找到一种使用Python将图片上传到Google幻灯片的方法。我创建了一个程序,将图像上传到Google Cloud Storage;为它们创建一个签名的URL;然后使用该URL将图像导入到Google幻灯片中。它是麻省理工学院许可下的here,可能是一个有用的工具/资源。
https://stackoverflow.com/questions/66375296
复制相似问题