Я попробовал рекомендации из ответа Али Нури Секера, но они не сработали:
- eml2pdf: не работает в Windows
- eml-to-pdf: ошибка в Mime Type
- pyPdf: тоженужно много работы, чтобы построить весь дизайн - gmail-to-pdf: ошибка в коде для некоторых электронных писем (см. ошибку, упомянутую на github)
Что сработало (та же общая идея, что у Али Нури Секера):
- сохранить электронные письма как .eml с
email.generator.Generator
- Использовать eml-to-pdf-converter (не на основе Python, но с открытым исходным кодом GUI) для преобразованияфайл .eml в pdf (вы просто отбрасываете папку, содержащую ваши файлы .eml, нажимаете кнопку, и вы получаете свой pdf. Он даже работает с подпапками!)
Более подробные сценарии можно найти здесь
Это первый сценарий "сохранить письмо как .eml":
# source = https://developers.google.com/gmail/api/quickstart/python?authuser=2
# In brief:
# this script will connect to your gmail account and download as .eml file your email from a specified label.
# then you can convert the .eml files to pdf : https://github.com/nickrussler/eml-to-pdf-converter
# set up
# 1) save this script in a folder
# 2) save the script "get labels id.py" in the same folder
# 3) go to this link https://developers.google.com/gmail/api/quickstart/python and click on "Enable the gmail API", then click on "Download client configuration" and save this .json file in the same folder as this script
# 4) GMAIL API doesn't use Label name but ID so you need to run the script "get labels id.py" and to copy the ID for the label you need (the firt time, you will be ask the persmission on a consent screen, accept it with the account where you want to download your email)
# 5) copy your label id below in custom var
# 6) run this script and your emails will be saved as .eml file in a subfolder "emails as eml"
# connect to gmail api
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
# decode response from Gmail api and save a email
import base64
import email
#for working dir and path for saving file
import os
# CUSTOM VAR
labelid = "Label_18" # change your label id
# set working directory https://stackoverflow.com/a/1432949/3154274
abspath = os.path.abspath(__file__)
dname = os.path.dirname(abspath)
os.chdir(dname)
print("working dir set to ", dname)
# create folder to save email
emailfolder= dname+"\emails as eml"
if not os.path.exists(emailfolder):
os.makedirs(emailfolder)
# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
def main():
# create the credential the first tim and save then in token.pickle
creds = None
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
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()
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
#create the service
service = build('gmail', 'v1', credentials=creds)
# get the *list* of all emails in the labels (if there are multiple pages, navigate to them)
#*************************************
# ressources for *list* email by labels
# https://developers.google.com/resources/api-libraries/documentation/gmail/v1/python/latest/index.html
# https://developers.google.com/resources/api-libraries/documentation/gmail/v1/python/latest/gmail_v1.users.messages.html#list
# example of code for list: https://developers.google.com/gmail/api/v1/reference/users/messages/list?apix_params=%7B%22userId%22%3A%22me%22%2C%22includeSpamTrash%22%3Afalse%2C%22labelIds%22%3A%5B%22LM%22%5D%7D
#*************************************
response= service.users().messages().list(userId="me", labelIds=labelid, q=None, pageToken=None, maxResults=None, includeSpamTrash=None).execute()
all_message_in_label = []
if 'messages' in response:
all_message_in_label.extend(response['messages'])
while 'nextPageToken' in response:
page_token = response['nextPageToken']
response = service.users().messages().list(userId="me", labelIds=labelid, q=None, pageToken=page_token, maxResults=None, includeSpamTrash=None).execute()
all_message_in_label.extend(response['messages'])
# all_message_in_label looks like this
# for email in all_message_in_label:
# print(email)
#{'id': '169735e289ba7310', 'threadId': '169735e289ba7310'}
#{'id': '169735c76a4b93af', 'threadId': '169735c76a4b93af'}
if not all_message_in_label:
print('No email LM found.')
else:
# for each ID in all_message_in_label we *get* the message
#*************************************
# ressources for *get* email
# https://developers.google.com/resources/api-libraries/documentation/gmail/v1/python/latest/gmail_v1.users.messages.html#get
# code example for decode https://developers.google.com/gmail/api/v1/reference/users/messages/get
# + decode for python 3 https://python-forum.io/Thread-TypeError-initial-value-must-be-str-or-None-not-bytes--12161
#*************************************
for emails in all_message_in_label:
message= service.users().messages().get(userId="me", id=emails["id"], format="raw", metadataHeaders=None).execute()
msg_str = base64.urlsafe_b64decode(message['raw'].encode('ASCII'))
try:
mime_msg = email.message_from_string(msg_str.decode())
# the the message as a .eml file
outfile_name = os.path.join(emailfolder, f'{emails["id"]}.eml')
with open(outfile_name, 'w') as outfile:
gen = email.generator.Generator(outfile)
gen.flatten(mime_msg)
print("mail saved: ", emails["id"])
except:
print("error in message ", message["snippet"])
if __name__ == '__main__':
main()
Это второй сценарийget get ids.py (см. «Настройка», шаг 4. в первом скрипте)
# source = https://developers.google.com/gmail/api/quickstart/python?authuser=2
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
# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
def main():
creds = None
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
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()
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('gmail', 'v1', credentials=creds)
# Get list of all labels
# https://developers.google.com/resources/api-libraries/documentation/gmail/v1/python/latest/index.html
results = service.users().labels().list(userId='me').execute()
labels = results.get('labels', [])
if not labels:
print('No labels found.')
else:
print('Labels:')
for label in labels:
print(label['name'] + " "+label['id'])
if __name__ == '__main__':
main()
input("continue")