1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Python】Gmail API を使ってメールを送信する

Last updated at Posted at 2023-11-28

Gmail API を使ってみる

Gmail API のガイドを参考に Gmail アカウントからメール送信を試した際に、躓いた点について備忘録としてまとめておこうと思います。

サンプルコードをとりあえず実行してみる

Gmail API ガイドのメール送信に記載されている「send_message.py」のコードを参考にメール送信を試してみました。
が、ガイドに記載されている以下のコードをコピペして実行してみても、そのままでは動きません。

send_message.py
import base64
from email.message import EmailMessage

import google.auth
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError


def gmail_send_message():
  """Create and send an email message
  Print the returned  message id
  Returns: Message object, including message id

  Load pre-authorized user credentials from the environment.
  TODO(developer) - See https://developers.google.com/identity
  for guides on implementing OAuth2 for the application.
  """
  creds, _ = google.auth.default()

  try:
    service = build("gmail", "v1", credentials=creds)
    message = EmailMessage()

    message.set_content("This is automated draft mail")

    message["To"] = "gduser1@workspacesamples.dev"
    message["From"] = "gduser2@workspacesamples.dev"
    message["Subject"] = "Automated draft"

    # encoded message
    encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode()

    create_message = {"raw": encoded_message}
    # pylint: disable=E1101
    send_message = (
        service.users()
        .messages()
        .send(userId="me", body=create_message)
        .execute()
    )
    print(f'Message Id: {send_message["id"]}')
  except HttpError as error:
    print(f"An error occurred: {error}")
    send_message = None
  return send_message


if __name__ == "__main__":
  gmail_send_message()

クイックスタートのサンプルコードを確認

なぜ動かないのかというと、認証に関する処理が記述されていない為です。
Gmail API を使うには、事前に oAuth を利用してユーザーの認証を行う必要があります。認証処理のコードは クイックスタート に記載されている「quickstart.py」が参考になります。
以下のコードのうち、main() の中にある認証処理の箇所(28~49行目辺り)を「send_message.py」に追加すると動きそうです。

send_message.py
import os.path

from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError

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


def main():
  """Shows basic usage of the Gmail API.
  Lists the user's Gmail labels.
  """
  creds = None
  # The file token.json 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.json"):
    creds = Credentials.from_authorized_user_file("token.json", SCOPES)
  # 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.json", "w") as token:
      token.write(creds.to_json())

  try:
    # Call the Gmail API
    service = build("gmail", "v1", credentials=creds)
    results = service.users().labels().list(userId="me").execute()
    labels = results.get("labels", [])

    if not labels:
      print("No labels found.")
      return
    print("Labels:")
    for label in labels:
      print(label["name"])

  except HttpError as error:
    # TODO(developer) - Handle errors from gmail API.
    print(f"An error occurred: {error}")


if __name__ == "__main__":
  main()

コードを組み合わせて実行してみる

認証処理を追加したソースコードは以下になります。
「quickstart.py」の認証処理を get_token() 関数としてまとめて、関数の戻り値として creds オブジェクトをリターンするようにしました。
最後に main() 関数の中で認証処理とメール送信の関数を呼び出して、メールを送信しています。

send_message_withAuth.py
import base64
from email.message import EmailMessage

import google.auth
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError

import os.path

from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow


SCOPES = ['https://mail.google.com/']

def get_token():
    creds = None
    if os.path.exists('token.json'):
        creds = Credentials.from_authorized_user_file('token.json', SCOPES)
    # 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)

        with open('token.json', 'w') as token:
            token.write(creds.to_json())
    
    return creds


def gmail_send_message(creds):
    try:
       service = build("gmail", "v1", credentials=creds)
       message = EmailMessage()

       message.set_content("This is automated draft mail")
       message["To"] = "XXXXXXXX@XXXXXXXX.jp"
       message["From"] = "gduser2@workspacesamples.dev"
       message["Subject"] = "Automated draft"

       # encoded message
       encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode()

       create_message = {"raw": encoded_message}
       # pylint: disable=E1101
       send_message = service.users().messages().send(userId="me", body=create_message).execute()
       print(f'Message Id: {send_message["id"]}')
    except HttpError as error:
      print(f"An error occurred: {error}")
      send_message = None
    return send_message


if __name__ == "__main__":
    creds = get_token()
    gmail_send_message(creds)

こちらを実行することで、無事にメールの送信を行うことが出来ました。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?