簡體   English   中英

如何在不啟用“不安全訪問”的情況下通過 gmail 發送電子郵件?

[英]How to send an email through gmail without enabling 'insecure access'?

Google 正在推動我們提高腳本訪問其 gmail smtp 服務器的安全性。 對於那件事我沒有任何疑問。 事實上,我很樂意提供幫助。

但他們並沒有讓事情變得容易。 建議我們Upgrade to a more secure app that uses the most up to date security measures這一切都很好,但這並不能幫助我弄清楚如何升級如下所示的代碼:

server = smtplib.SMTP("smtp.gmail.com", 587)
server.ehlo()
server.starttls()
server.login(GMAIL_USER, GMAIL_PASSWORD)
server.sendmail(FROM, TO, MESSAGE)
server.close()

當然,我會打開“訪問不太安全的應用程序”,但如果有人想出用什么來替換此代碼,我將不勝感激。

這很痛苦,但我現在似乎有些事情......

Python3不支持(尚未)

我認為這不會太難實現,因為我在轉換軟件包時遇到了磕磕絆絆而沒有發生任何大規模的事情:只是通常的2to3。 然而,經過幾個小時,我厭倦了向上游游泳。 在撰寫本文時,我找不到已發布的用於Python 3公共消費的軟件包.python 2體驗很簡單(相比之下)。

導航谷歌網站是成功的一半

毫無疑問,隨着時間的推移,這將改變。 最終,您需要下載client_secret.json文件。 你只能(可能)通過網絡瀏覽器進行設置:

  1. 你需要一個谷歌帳戶 - 谷歌應用程序或Gmail。 所以,如果你還沒有,那么去買一個。
  2. 開始使用開發人員控制台
  3. 創建一個新項目,然后等待4或400秒才能完成。
  4. 導航到API's and Auth - > Credentials
  5. OAuth選擇“ Create New Client ID
  6. 選擇Installed Application作為應用程序類型和Other
  7. 你現在應該有一個按鈕Download JSON 去做。 這是你的client_secret.json - 可以這么說的密碼

但等等不是全部!

您必須為您的應用程序提供“產品名稱”以避免一些奇怪的錯誤。 (看看我有多痛苦給你這個;-)

  1. 導航到API's & auth - > Consent Screen
  2. 選擇你的郵箱
  3. 輸入產品名稱。 它是什么並不重要。 “Foobar”會很好。

新聞快報! 哇。 現在還有更多!

  1. 導航到API的&auth - > API - > Gmail API
  2. 單擊“啟用API”按鈕

好極了。 現在我們可以更新電子郵件腳本。

Python 2

您需要第一次以交互方式運行腳本。 它將在您的計算機上打開Web瀏覽器,您將授予權限(點擊按鈕)。 此練習會將文件保存到包含可重用令牌的計算機gmail.storage中。

[我沒有幸運將令牌轉移到沒有圖形瀏覽器功能的機器 - 返回HTTPError。 我試圖通過lynx圖形瀏覽器來完成它。 這也失敗了,因為谷歌已將最終的“接受”按鈕設置為“禁用”! 我會提出另一個問題來跳過這個障礙(更多的抱怨)]

首先你需要一些庫:

pip install --upgrade google-api-python-client
pip install --upgrade python-gflags
  • 你需要改變地址和地址
  • 確保您擁有Storage指令所期望的client_token.json文件
  • 該目錄需要是可寫的,因此它可以保存gmail.storage文件

最后一些代碼:

import base64
import httplib2

from email.mime.text import MIMEText

from apiclient.discovery import build
from oauth2client.client import flow_from_clientsecrets
from oauth2client.file import Storage
from oauth2client.tools import run


# Path to the client_secret.json file downloaded from the Developer Console
CLIENT_SECRET_FILE = 'client_secret.json'

# Check https://developers.google.com/gmail/api/auth/scopes for all available scopes
OAUTH_SCOPE = 'https://www.googleapis.com/auth/gmail.compose'

# Location of the credentials storage file
STORAGE = Storage('gmail.storage')

# Start the OAuth flow to retrieve credentials
flow = flow_from_clientsecrets(CLIENT_SECRET_FILE, scope=OAUTH_SCOPE)
http = httplib2.Http()

# Try to retrieve credentials from storage or run the flow to generate them
credentials = STORAGE.get()
if credentials is None or credentials.invalid:
  credentials = run(flow, STORAGE, http=http)

# Authorize the httplib2.Http object with our credentials
http = credentials.authorize(http)

# Build the Gmail service from discovery
gmail_service = build('gmail', 'v1', http=http)

# create a message to send
message = MIMEText("Message goes here.")
message['to'] = "yourvictim@goes.here"
message['from'] = "you@go.here"
message['subject'] = "your subject goes here"
body = {'raw': base64.b64encode(message.as_string())}

# send it
try:
  message = (gmail_service.users().messages().send(userId="me", body=body).execute())
  print('Message Id: %s' % message['id'])
  print(message)
except Exception as error:
  print('An error occurred: %s' % error)

希望這讓我們全部開始。 不像舊方式那么簡單,但看起來復雜得多,現在我可以在肉體中看到它。

John Mee的答案似乎已經過時了。 它在2016年7月無效。可能是由於Gmail的API更新。 我更新他的代碼(python 2)如下:

    """Send an email message from the user's account.
"""

import base64
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import mimetypes
import os

#from __future__ import print_function
import httplib2
import os

from apiclient import discovery
import oauth2client
from oauth2client import client
from oauth2client import tools

from apiclient import errors

SCOPES = 'https://www.googleapis.com/auth/gmail.compose'
CLIENT_SECRET_FILE = 'client_secret.json'
APPLICATION_NAME = 'Gmail API Python Quickstart'

try:
    import argparse
    flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
    flags = None

def SendMessage(service, user_id, message):
  """Send an email message.

  Args:
    service: Authorized Gmail API service instance.
    user_id: User's email address. The special value "me"
    can be used to indicate the authenticated user.
    message: Message to be sent.

  Returns:
    Sent Message.
  """
  try:
    message = (service.users().messages().send(userId=user_id, body=message)
               .execute())
    print 'Message Id: %s' % message['id']
    return message
  except errors.HttpError, error:
    print 'An error occurred: %s' % error


def CreateMessage(sender, to, subject, message_text):
  """Create a message for an email.

  Args:
    sender: Email address of the sender.
    to: Email address of the receiver.
    subject: The subject of the email message.
    message_text: The text of the email message.

  Returns:
    An object containing a base64url encoded email object.
  """
  message = MIMEText(message_text)
  message['to'] = to
  message['from'] = sender
  message['subject'] = subject
  return {'raw': base64.urlsafe_b64encode(message.as_string())}


def get_credentials():
    """Gets valid user credentials from storage.

    If nothing has been stored, or if the stored credentials are invalid,
    the OAuth2 flow is completed to obtain the new credentials.

    Returns:
        Credentials, the obtained credential.
    """
    home_dir = os.path.expanduser('~')
    credential_dir = os.path.join(home_dir, '.credentials')
    if not os.path.exists(credential_dir):
        os.makedirs(credential_dir)
    credential_path = os.path.join(credential_dir,
                                   'sendEmail.json')

    store = oauth2client.file.Storage(credential_path)
    credentials = store.get()
    if not credentials or credentials.invalid:
        flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
        flow.user_agent = APPLICATION_NAME
        if flags:
            credentials = tools.run_flow(flow, store, flags)
        else: # Needed only for compatibility with Python 2.6
            credentials = tools.run(flow, store)
        print('Storing credentials to ' + credential_path)
    return credentials

if __name__ == "__main__":

    try:
        credentials = get_credentials()
        http = credentials.authorize(httplib2.Http())
        service = discovery.build('gmail', 'v1', http=http)
        SendMessage(service, "me", CreateMessage("send@gmail.com", "receive@gmail.com", "Test gmail automation", "Hello world"))

    except Exception, e:
        print e
        raise

請注意,如果遇到“ Insufficient Permission ”錯誤,可能的原因之一是程序中的范圍未正確設置。 另一個可能的原因可能是您需要刪除存儲json文件(此程序中的“sendEmail.json”)並刷新您的程序。 在這篇文章中可以看到更多細節。

您是否考慮過使用Gmail API? 該API具有內置的安全功能,並專門針對Gmail進行了優化。 您可以在http://developers.google.com上找到API文檔 - 例如,這里是Send API調用的文檔:

https://developers.google.com/gmail/api/v1/reference/users/messages/send

我包含了一些已針對python 3使用而更新的代碼 - 一旦獲得必要的權限和OAuth令牌工作,它似乎會發送電子郵件。 它主要基於google api網站樣本

    from __future__ import print_function

import base64
import os
from email.mime.text import MIMEText

import httplib2
from apiclient import discovery
from googleapiclient import errors
from oauth2client import client
from oauth2client import tools
from oauth2client.file import Storage

try:
    import argparse

    flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
    flags = None

# If modifying these scopes, delete your previously saved credentials
# at ~/.credentials/gmail-python-quickstart.json
SCOPES = 'https://www.googleapis.com/auth/gmail.send'
CLIENT_SECRET_FILE = 'client_secret.json'
APPLICATION_NAME = 'Gmail API Python Quickstart'


def get_credentials():
    """Gets valid user credentials from storage.

    If nothing has been stored, or if the stored credentials are invalid,
    the OAuth2 flow is completed to obtain the new credentials.

    Returns:
        Credentials, the obtained credential.
    """
    home_dir = os.path.expanduser('~')
    credential_dir = os.path.join(home_dir, '.credentials')
    if not os.path.exists(credential_dir):
        os.makedirs(credential_dir)
    credential_path = os.path.join(credential_dir,
                                   'gmail-python-quickstart.json')

    store = Storage(credential_path)
    credentials = store.get()
    if not credentials or credentials.invalid:
        flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
        flow.user_agent = APPLICATION_NAME
        if flags:
            credentials = tools.run_flow(flow, store, flags)
        else:  # Needed only for compatibility with Python 2.6
            credentials = tools.run(flow, store)
        print('Storing credentials to ' + credential_path)
    return credentials


to = 'test@email.com'
sender = 'test@email.com'
subject = 'test emails'
message_text = 'hello this is a text test message'
user_id = 'me'

def create_message(sender, to, subject, message_text):
    """Create a message for an email.

    Args:
      sender: Email address of the sender.
      to: Email address of the receiver.
      subject: The subject of the email message.
      message_text: The text of the email message.

    Returns:
      An object containing a base64url encoded email object.
    """
    message = MIMEText(message_text)
    message['to'] = to
    message['from'] = sender
    message['subject'] = subject
    return {'raw': (base64.urlsafe_b64encode(message.as_bytes()).decode())}


def send_message(service, user_id, message):
    """Send an email message.

    Args:
      service: Authorized Gmail API service instance.
      user_id: User's email address. The special value "me"
      can be used to indicate the authenticated user.
      message: Message to be sent.

    Returns:
      Sent Message.
    """
    try:
        message = (service.users().messages().send(userId=user_id, body=message)
                   .execute())
        print('Message Id: {}'.format(message['id']))
        return message
    except errors.HttpError as error:
        print('An error occurred: {}'.format(error))


def main():
    """Shows basic usage of the Gmail API.

    Creates a Gmail API service object and outputs a list of label names
    of the user's Gmail account.
    """
    credentials = get_credentials()
    http = credentials.authorize(httplib2.Http())
    service = discovery.build('gmail', 'v1', http=http)

    msg = create_message(sender,to,subject,message_text)
    message = (service.users().messages().send(userId=user_id, body=msg)
               .execute())
    print('Message Id: {}'.format(message['id']))
    results = service.users().messages().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'])


if __name__ == '__main__':
    main()

下面是Python 3和GMail當前API的更新示例。

請注意,要獲取下面的credentials.json文件,您需要在選擇相關GCP項目后在此處創建Oauth客戶端ID憑據。 創建后,您將看到客戶端密鑰和客戶端密鑰。 關閉該提示,然后單擊該帳戶旁邊的向下箭頭。 這是您需要的文件。

在此輸入圖像描述

import base64
import logging
import mimetypes
import os
import os.path
import pickle
from email.mime.text import MIMEText
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient import errors
from googleapiclient.discovery import build

def get_service():
    """Gets an authorized Gmail API service instance.

    Returns:
        An authorized Gmail API service instance..
    """    

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

    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)
    return service

def send_message(service, sender, message):
  """Send an email message.

  Args:
    service: Authorized Gmail API service instance.
    user_id: User's email address. The special value "me"
    can be used to indicate the authenticated user.
    message: Message to be sent.

  Returns:
    Sent Message.
  """
  try:
    sent_message = (service.users().messages().send(userId=sender, body=message)
               .execute())
    logging.info('Message Id: %s', sent_message['id'])
    return sent_message
  except errors.HttpError as error:
    logging.error('An HTTP error occurred: %s', error)

def create_message(sender, to, subject, message_text):
  """Create a message for an email.

  Args:
    sender: Email address of the sender.
    to: Email address of the receiver.
    subject: The subject of the email message.
    message_text: The text of the email message.

  Returns:
    An object containing a base64url encoded email object.
  """
  message = MIMEText(message_text)
  message['to'] = to
  message['from'] = sender
  message['subject'] = subject
  s = message.as_string()
  b = base64.urlsafe_b64encode(s.encode('utf-8'))
  return {'raw': b.decode('utf-8')}

if __name__ == '__main__':
    logging.basicConfig(
        format="[%(levelname)s] %(message)s",
        level=logging.INFO
    )

    try:
        service = get_service()
        message = create_message("from@gmail.com", "to@gmail.com", "Test subject", "Test body")
        send_message(service, "from@gmail.com", message)

    except Exception as e:
        logging.error(e)
        raise

這很簡單。 轉到 Gmail 帳戶設置。 搜索“應用密碼”。 生成應用密碼。 使用該密碼而不是 Gmail 密碼。 應用密碼的優點是您不需要關閉兩因素身份驗證,也不需要允許不安全的應用。

這是關於 Google 應用密碼的官方鏈接https://support.google.com/mail/answer/185833?hl=en-GB

希望這有幫助👍

  protected string SendEmail(string toAddress, string subject, string body)
    {
        string result = "Message Sent Successfully..!!";

        string senderID = "...........";// use sender's email id here..
        const string senderPassword = "........."; // sender password here...

        try
        {
            SmtpClient smtp = new SmtpClient
            {
                Host = "smtp.gmail.com", // smtp server address here...
                Port = 587,
                EnableSsl = true,
                DeliveryMethod = SmtpDeliveryMethod.Network,
                Credentials = new System.Net.NetworkCredential(senderID, senderPassword),
                Timeout = 30000,

            };

            MailMessage message = new MailMessage(senderID, toAddress, subject, body);

            smtp.Send(message);
        }
        catch (Exception ex)
        {
            result = "Error sending email.!!!";
        }

        return result;
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM