请求无时的 HttpError 403 返回“权限不足

Posted

技术标签:

【中文标题】请求无时的 HttpError 403 返回“权限不足【英文标题】:HttpError 403 when requesting None returned "Insufficient Permission 【发布时间】:2021-12-02 06:29:24 【问题描述】:

从 Gmail 中的电子邮件附件上传到 Google Drive:

from __future__ import print_function
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

import io
import base64
from googleapiclient.http import MediaIoBaseUpload
from time import sleep


q='has:attachment'
maxResults=int(input("Please specify the number of emails with attachments that you would like to see:"))

#for drive api---------------------------------------------------------------------------------

# If modifying these scopes, delete the file token.pickle.

creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token_drive.pickle'):
          with open('token_drive.pickle', 'rb') as token_drive:
               creds = pickle.load(token_drive)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
   if creds and creds.expired and creds.refresh_token:
           creds.refresh(Request())
   else:
      flow = InstalledAppFlow.from_client_secrets_file(
     Credentials_drive.json', 'https://www.googleapis.com/auth/drive.metadata.readonly')
      creds1 = flow.run_local_server(port=0)
      # Save the credentials for the next run
      with open('token_drive.pickle', 'wb') as token_drive:
           pickle.dump(creds, token_drive)

   drive_service= build('drive', 'v3', credentials=creds1)


sleep(5)
    

# for gmail api---------------------------------------------------------------------------------   
# If modifying these scopes, delete the file token.pickle.



creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.

if os.path.exists('token.pickle'):
            with open('token.pickle', 'rb') as token:
                 creds = pickle.load(token)
        
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
    if creds and creds.expired and creds.refresh_token:
               creds.refresh(Request())
    else:
       flow = InstalledAppFlow.from_client_secrets_file(
      'Credentials.json', 'https://www.googleapis.com/auth/gmail.readonly')
       creds = flow.run_local_server(port=0)
       # Save the credentials for the next run
       with open('token.pickle', 'wb') as token:
             pickle.dump(creds, token)
            
    service = build('gmail', 'v1', credentials=creds)




# Call the Gmail API
results = service.users().labels().list(userId='me').execute()

#Get Messages

results = service.users().messages().list(userId='me',q=q, maxResults=maxResults ,labelIds=['INBOX']).execute()
messages = results.get('messages', [])


def create_folder_in_drive(service,folder_name,parent_folder=[]):
    file_metadata =
        
        'name':    folder_name,
        'parents': parent_folder,
        'mimeType':'application/vnd.google-apps.folder' 
        
              



for  message in messages:
            msg = service.users().messages().get(userId='me',metadataHeaders=['parts'], id=message['id']).execute()
            messageID=msg['threadId']
            messageSubject='(No Subject)(0)'.format(messageID)
            msgdetail=msg.get('payload')
          
            for item in msgdetail['headers']:
                if item['name']=='Subject':
                   if item['value']:
                       messageSubject='0 (1)'.format(item['value'],messageID)
                   else:
                       messageSubject='(No Subject)(0)'.format(messageID)
            print("messagesubject:" ,  messageSubject    )
            
            
            #create drive folder
            folder_id=create_folder_in_drive(drive_service,messageSubject)
            
            if 'parts' in msgdetail:
                
                for msgPayload in msgdetail['parts']:
                    mime_type=msgPayload['mimeType'] 
                    file_name=msgPayload['filename']
                    body=msgPayload['body']
                    print(body)
                    if 'attachmentId' in body:
                        attachment_id=body['attachmentId']
                        response=service.users().messages().attachments().get(
                            userId='me',
                            messageId=msg['id'],
                            id=attachment_id
                        ).execute()
                        
                        file_data=base64.urlsafe_b64decode(
                             response.get('data').encode('UTF-8'))
                                            
                        fh=io.BytesIO(file_data)
                        
                        file_metadata= 
                          'name':file_name,
                          'parents':[folder_id]
                            
                        
                        
                        media_body=MediaIoBaseUpload(fh,mimetype=mime_type,chunksize=1024*1024,resumable=True)
                        
                        file=drive_service.files().create(
                          body=  file_metadata,
                          media_body=media_body,
                          fields='id'
                            
                            ).execute()

朋友们好,如果我删除文件目录下的token.pickle和token_drive.pickle文件(这些文件是独立于google cloud创建的)并运行代码:

"ResumableUploadError: 错误”,当我运行代码而不删除 pickle 文件时,我收到错误消息“NameError: name 'service' is not defined。”

由于 media_body 和 file_metadata 返回一个值,同时对 Gmail 和 Drive 进行身份验证似乎有问题,但我无法解决问题。

【问题讨论】:

【参考方案1】:

我正在打电话查看您的代码。

您应该能够获得一个单个令牌,其范围足以用于 Gmail云端硬盘,而不是同时使用多个令牌。

Unpickling 对象也可能导致某种碰撞。即使只适合你,我还是建议尽可能避免腌制。

这是一个 Google sample,它显示了使用 Python 的 Gmail 的 OAuth 流程。该示例将令牌作为文件写入|读取到磁盘。用 2 个作用域试试这段代码。

【讨论】:

我尝试了单个 Credentials.json 文件、单个令牌,并且代码只生成了一个令牌。 pickle 文件,但唯一的驱动器可以被授权,Gmail 不能。 (身份验证流程已经完成。您可以关闭此窗口。,我只看到这个驱动器)最后我得到名称'服务'未定义错误。 (# 调用 Gmail API) 能否分享上述示例代码的链接? 道歉... 提到但忘记包括在内。更新的答案在这里:developers.google.com/gmail/api/quickstart/python【参考方案2】:

#for gmail and drive api------------------------------------------------------------

# If modifying these scopes, delete the file token.pickle.
SCOPES=['https://www.googleapis.com/auth/gmail.readonly','https://www.googleapis.com/auth/drive','https://www.googleapis.com/auth/drive.file','https://www.googleapis.com/auth/drive.metadata']


creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.

if os.path.exists('token.pickle'):
    with open('token.pickle', 'rb') as token:
         creds = pickle.load(token)

# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
    if creds and creds.expired and creds.refresh_token:
       creds.refresh(Request())
    else:
       flow = InstalledAppFlow.from_client_secrets_file(
      'Credentials.json', SCOPES)
       creds = flow.run_local_server(port=0)
       # Save the credentials for the next run
       with open('token.pickle', 'wb') as token:
             pickle.dump(creds, token)
    
service = build('gmail', 'v1', credentials=creds)
drive_service = build('drive', 'v3', credentials=creds)
    

【讨论】:

以上是关于请求无时的 HttpError 403 返回“权限不足的主要内容,如果未能解决你的问题,请参考以下文章

python3 raise HTTPError(req.full_url, code, msg, hdrs, fp) urllib.error.HTTPError: HTTP Error 403

调用 GMAIL API 时出现间歇性错误 - “调用者没有权限”

HTTPError:HTTP 错误 403:禁止

php请求nginx服务器返回403

SEC EDGAR 13F 源 HTTPError:HTTP 错误 403:禁止

IIS 10.0 HTTP 错误 403.0 - 禁止