连接到Gmail使用电子邮件地址和密码与Python

huangapple go评论81阅读模式
英文:

Connect to Gmail Using Email Address and Password with Python

问题

I am trying to connect to my Gmail account using Python. I want to connect to it using both SMTP and IMAP. I am aware that it is possible to use an app password to make this connection, but is there a way to use the actual email password instead?

我正在尝试使用Python连接到我的Gmail帐户。我希望能够同时使用SMTP和IMAP进行连接。我知道可以使用应用程序密码来建立连接,但是否有一种方法可以使用实际的电子邮件密码呢?

I have been reading this particular article,
我一直在阅读这篇特定的文章,

连接到Gmail使用电子邮件地址和密码与Python

Do I have that right?
我理解得对吗?

Given below is the code I am using to send and search emails. Like I said though, this only works with an app password,

以下是我用来发送和搜索电子邮件的代码。不过,就像我之前说的,这仅适用于应用程序密码,

import smtplib
import imaplib
import email
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import pandas as pd

class EmailClient:
    def __init__(self, email, password, smtp_server='smtp.gmail.com', smtp_port=587, imap_server="imap.gmail.com"):
        self.email = email
        self.password = password
        self.smtp_server = smtplib.SMTP(smtp_server, smtp_port)
        self.imap_server = imaplib.IMAP4_SSL(imap_server)

    def send_email(self, to_addr, subject, body):
        msg = MIMEMultipart()
        msg['From'] = self.email
        msg['To'] = to_addr
        msg['Subject'] = subject
        msg.attach(MIMEText(body, 'plain'))

        self.smtp_server.starttls()
        self.smtp_server.login(self.email, self.password)
        self.smtp_server.send_message(msg)
        self.smtp_server.quit()

    def search_email(self, mailbox="INBOX", subject=None, to=None, from_=None, since_date=None, until_date=None,
                     since_emailid=None):

        self.imap_server.login(self.email, self.password)

        self.imap_server.select(mailbox)

        query_parts = []
        if subject is not None:
            query_parts.append(f'(SUBJECT "{subject}")')
        if to is not None:
            query_parts.append(f'(TO "{to}")')
        if from_ is not None:
            query_parts.append(f'(FROM "{from_}")')
        if since_date is not None:
            since_date_str = since_date.strftime("%d-%b-%Y")
            query_parts.append(f'(SINCE "{since_date_str}")')
        if until_date is not None:
            until_date_str = until_date.strftime("%d-%b-%Y")
            query_parts.append(f'(BEFORE "{until_date_str}")')
        if since_emailid is not None:
            query_parts.append(f'(UID {since_emailid}:*)')

        query = ' '.join(query_parts)

        ret = []

        resp, items = self.imap_server.uid('search', None, query)
        items = items[0].split()
        for emailid in items[::-1]:
            resp, data = self.imap_server.uid('fetch', emailid, "(BODY[HEADER.FIELDS (SUBJECT TO FROM DATE)])")
            try:
                raw_email = data[0][1].decode("utf-8")
            except UnicodeDecodeError:
                ValueError(f"Could not decode email with id {emailid}")
            email_message = email.message_from_string(raw_email)

            email_line = {}
            email_line['id'] = emailid
            email_line["to"] = email_message['To']
            email_line["from"] = email_message['From']
            email_line["subject"] = str(email_message['Subject'])
            email_line["created_at"] = email_message['Date']
            resp, email_data = self.imap_server.uid('fetch', emailid, '(BODY[TEXT])')
            email_line["body"] = email_data[0][1].decode('utf-8')

            ret.append(email_line)

        self.imap_server.logout()

        return pd.DataFrame(ret)
英文:

I am trying to connect to my Gmail account using Python. I want to connect to it using both SMTP and IMAP. I am aware that it is possible to use an app password to make this connection, but is there a way to use the actual email password instead?

I have been reading this particular article,
<br>
https://support.google.com/accounts/answer/6010255#zippy=%2Cif-less-secure-app-access-is-on-for-your-account%2Cif-less-secure-app-access-is-off-for-your-account

The warning given at the top tells me that it is not possible to do so, even if 'Less secure app access' is allowed,

连接到Gmail使用电子邮件地址和密码与Python

Do I have that right?

Given below is the code I am using to send and search emails. Like I said though, this only works with an app password,

import smtplib
import imaplib
import email
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import pandas as pd
class EmailClient:
def __init__(self, email, password, smtp_server=&#39;smtp.gmail.com&#39;, smtp_port=587, imap_server=&quot;imap.gmail.com&quot;):
self.email = email
self.password = password
self.smtp_server = smtplib.SMTP(smtp_server, smtp_port)
self.imap_server = imaplib.IMAP4_SSL(imap_server)
def send_email(self, to_addr, subject, body):
msg = MIMEMultipart()
msg[&#39;From&#39;] = self.email
msg[&#39;To&#39;] = to_addr
msg[&#39;Subject&#39;] = subject
msg.attach(MIMEText(body, &#39;plain&#39;))
self.smtp_server.starttls()
self.smtp_server.login(self.email, self.password)
self.smtp_server.send_message(msg)
self.smtp_server.quit()
def search_email(self, mailbox=&quot;INBOX&quot;, subject=None, to=None, from_=None, since_date=None, until_date=None,
since_emailid=None):
self.imap_server.login(self.email, self.password)
self.imap_server.select(mailbox)
query_parts = []
if subject is not None:
query_parts.append(f&#39;(SUBJECT &quot;{subject}&quot;)&#39;)
if to is not None:
query_parts.append(f&#39;(TO &quot;{to}&quot;)&#39;)
if from_ is not None:
query_parts.append(f&#39;(FROM &quot;{from_}&quot;)&#39;)
if since_date is not None:
since_date_str = since_date.strftime(&quot;%d-%b-%Y&quot;)
query_parts.append(f&#39;(SINCE &quot;{since_date_str}&quot;)&#39;)
if until_date is not None:
until_date_str = until_date.strftime(&quot;%d-%b-%Y&quot;)
query_parts.append(f&#39;(BEFORE &quot;{until_date_str}&quot;)&#39;)
if since_emailid is not None:
query_parts.append(f&#39;(UID {since_emailid}:*)&#39;)
query = &#39; &#39;.join(query_parts)
ret = []
resp, items = self.imap_server.uid(&#39;search&#39;, None, query)
items = items[0].split()
for emailid in items[::-1]:
resp, data = self.imap_server.uid(&#39;fetch&#39;, emailid, &quot;(BODY[HEADER.FIELDS (SUBJECT TO FROM DATE)])&quot;)
try:
raw_email = data[0][1].decode(&quot;utf-8&quot;)
except UnicodeDecodeError:
ValueError(f&quot;Could not decode email with id {emailid}&quot;)
email_message = email.message_from_string(raw_email)
email_line = {}
email_line[&#39;id&#39;] = emailid
email_line[&quot;to&quot;] = email_message[&#39;To&#39;]
email_line[&quot;from&quot;] = email_message[&#39;From&#39;]
email_line[&quot;subject&quot;] = str(email_message[&#39;Subject&#39;])
email_line[&quot;created_at&quot;] = email_message[&#39;Date&#39;]
resp, email_data = self.imap_server.uid(&#39;fetch&#39;, emailid, &#39;(BODY[TEXT])&#39;)
email_line[&quot;body&quot;] = email_data[0][1].decode(&#39;utf-8&#39;)
ret.append(email_line)
self.imap_server.logout()
return pd.DataFrame(ret)

答案1

得分: 1

"我知道可以使用应用程序密码进行连接,但是否可以使用实际的电子邮件密码呢?

不,不能这样做,您需要执行以下两种操作之一。

  1. 在帐户上启用两步验证并创建应用程序密码。
  2. 使用Xoauth2并请求用户授权以访问其帐户。

您的代码在我看来很好,只需创建一个应用程序密码。

Xoauth示例

from __future__ import print_function

import base64
import os.path
import smtplib

from email.mime.text import MIMEText
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow

# 如果修改了这些范围,请删除token.json文件。
SCOPES = ['https://mail.google.com/']

# 用户令牌存储
USER_TOKENS = 'token.json'

# 应用程序凭据
CREDENTIALS = 'C:\YouTube\dev\credentials.json'


def getToken() -> str:
    creds = None

    # 文件token.json存储用户的访问和刷新令牌,并在首次授权流程完成时自动生成。
    if os.path.exists(USER_TOKENS):
        creds = Credentials.from_authorized_user_file(USER_TOKENS, SCOPES)
        creds.refresh(Request())
    # 如果没有(有效的)凭据可用,让用户登录。
    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, SCOPES)
            creds = flow.run_local_server(port=0)
        # 保存凭据以供下次运行
        with open(USER_TOKENS, 'w') as token:
            token.write(creds.to_json())

    return creds.token


def generate_oauth2_string(username, access_token) -> str:
    auth_string = 'user=' + username + '\1auth=Bearer ' + access_token + '\1\1'
    return base64.b64encode(auth_string.encode('ascii')).decode('ascii')


def send_email(host, port, subject, msg, sender, recipients):
    access_token = getToken()
    auth_string = generate_oauth2_string(sender, access_token)

    msg = MIMEText(msg)
    msg['Subject'] = subject
    msg['From'] = sender
    msg['To'] = ', '.join(recipients)

    server = smtplib.SMTP(host, port)
    server.starttls()
    server.docmd('AUTH', 'XOAUTH2 ' + auth_string)
    server.sendmail(sender, recipients, msg.as_string())
    server.quit()


def main():
    host = "smtp.gmail.com"
    port = 587

    user = "REDACTED@gmail.com"
    recipient = "REDACTED@gmail.com"
    subject = "Test email Oauth2"
    msg = "Hello world"
    sender = user
    recipients = [recipient]
    send_email(host, port, subject, msg, sender, recipients)


if __name__ == '__main__':
    main()

"

英文:

>I am aware that it is possible to use an app password to make this connection, but is there a way to use the actual email password instead?

No there is not, you need to do one of two things.

  1. enable 2fa on the account and create an apps password
  2. Use Xoauth2 and request authorization of the user to access their account.

Your code looks fine to me just crate an apps password.

Xoauth sample

from __future__ import print_function
import base64
import os.path
import smtplib
from email.mime.text import MIMEText
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
# If modifying these scopes, delete the file token.json.
SCOPES = [&#39;https://mail.google.com/&#39;]
# user token storage
USER_TOKENS = &#39;token.json&#39;
# application credentials
CREDENTIALS = &#39;C:\YouTube\dev\credentials.json&#39;
def getToken() -&gt; str:
creds = None
# The file token.json stores the user&#39;s access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists(USER_TOKENS):
creds = Credentials.from_authorized_user_file(USER_TOKENS, SCOPES)
creds.refresh(Request())
# 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, SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open(USER_TOKENS, &#39;w&#39;) as token:
token.write(creds.to_json())
return creds.token
def generate_oauth2_string(username, access_token) -&gt; str:
auth_string = &#39;user=&#39; + username + &#39;\1auth=Bearer &#39; + access_token + &#39;\1\1&#39;
return base64.b64encode(auth_string.encode(&#39;ascii&#39;)).decode(&#39;ascii&#39;)
def send_email(host, port, subject, msg, sender, recipients):
access_token = getToken()
auth_string = generate_oauth2_string(sender, access_token)
msg = MIMEText(msg)
msg[&#39;Subject&#39;] = subject
msg[&#39;From&#39;] = sender
msg[&#39;To&#39;] = &#39;, &#39;.join(recipients)
server = smtplib.SMTP(host, port)
server.starttls()
server.docmd(&#39;AUTH&#39;, &#39;XOAUTH2 &#39; + auth_string)
server.sendmail(sender, recipients, msg.as_string())
server.quit()
def main():
host = &quot;smtp.gmail.com&quot;
port = 587
user = &quot;REDACTED@gmail.com&quot;
recipient = &quot;REDACTED@gmail.com&quot;
subject = &quot;Test email Oauth2&quot;
msg = &quot;Hello world&quot;
sender = user
recipients = [recipient]
send_email(host, port, subject, msg, sender, recipients)
if __name__ == &#39;__main__&#39;:
main()

huangapple
  • 本文由 发表于 2023年8月10日 12:56:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/76872744.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定