如何将 Python 列表作为单个电子邮件发送

How to Send Python List as Single Email

下面的亚马逊抓取程序代码按预期 100% 工作。它从 CSV 文件 amazon-list-of-asins.csv[ 中提取亚马逊产品 AsinNotification Price =36=] 然后在亚马逊网站上查看相应产品,如果更便宜,则发送降价电子邮件。

它循环遍历 CSV 文件,直到检查完所有产品为止,每次成功降价都会发送一封电子邮件。

问题

我需要一种方法来仅集体发送 一封电子邮件 显示列表中捕获的成功降价后的产品及其各自信息 csv_line_entry.

我不是程序员,我已经设法整理了所提供的代码。正如您所理解的那样,几次成功的降价将导致多封电子邮件,这可能会导致数据使用问题、丢失电子邮件、垃圾邮件、糟糕的用户体验等。

亚马逊列表-asins.csv

阿信,通知价格,产品
B07GS6ZB7T, 50.00, 罗技 G502 HERO 高性能有线游戏鼠标
B001D7UYBO,58.00,RØDE PSA1 旋转安装录音室麦克风吊臂
B0011UB9CQ, 110.00, beyerdynamic DT 990 PRO 录音室耳机

Python 脚本

import csv
import datetime

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header

import requests
from requests.sessions import CaseInsensitiveDict
from requests_html import HTMLSession


# Set User Agent
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/  537.36",
    "Accept-Language": "en-GB,en;q=0.5",
    "Referer": "http://google.com",
    "DNT": "1",
}

# Start session and create lists
s = HTMLSession()
amz_product_list = []
date = datetime.datetime.today()

def run_scraper():

    # Read CSV File
    with open("amazon-list-of-asins.csv", "r") as f:
        csv_reader = csv.reader(f)
        next(csv_reader)

        # Scrape data
        for csv_line_entry in csv_reader:
            
            r = s.get("https://www.amazon.co.uk/dp/" + csv_line_entry[0], headers=headers)
            r.html.render(sleep=1)

            try:
                amz_product_title = r.html.xpath('//*[@id="productTitle"]')[0].text.strip()

                amazon_price = (
                    r.html.xpath('//*[@id="priceblock_ourprice"]')[0]
                    .text.replace("£", "")
                    .replace(",", "")
                    .strip()
                )

            except:
                amazon_price = (
                    r.html.xpath('//*[@id="price_inside_buybox"]')[0]
                    .text.replace("£", "")
                    .replace(",", "")
                    .strip()
                )
            # Check Amazon price is lower than csv notify price
            if (float(amazon_price)) < float(csv_line_entry[1]):
                saving = round(float(csv_line_entry[1]) - float(amazon_price),2)
                print('Amazon Product Title:',amz_product_title)
                print('Amazon Asin:',csv_line_entry[0])
                print('CSV Notify Price:',csv_line_entry[1])
                print('Current Amazon Price:',amazon_price)
                print('   > Saving:',saving)

                #CSV List Format: [csv_line_entry[0], csv_line_entry[1], amz_product_title]
                amz_product_list.append(csv_line_entry)

                #Additional Entries to CSV List Format : [csv_line_entry[0], csv_line_entry[1], amz_product_title, [amazon_price, saving]]
                csv_line_entry.append([amazon_price, saving])

                send_email(amz_product_title, csv_line_entry, amazon_price, saving)


# Define Email Routine
def send_email(amz_product_title, csv_line_entry, amazon_price, saving):

    server = smtplib.SMTP('smtp.gmail.com', 587)
    server.ehlo()
    server.starttls()
    server.ehlo()
    server.login('myemailaddress@gmail.com', 'myemailpassword')
  
    frm = "myemailaddress@gmail.com"
    to = "sampleuser@domain.co.uk"
    msg = MIMEMultipart('alternative')
    msg.set_charset('utf8')
    msg['FROM'] = frm
    msg['To'] = to

    msg['Subject'] = f'Amazon Saving: {amz_product_title} - £{amazon_price}'

    bodyStr = f'<!DOCTYPE html><html><body>Date & Time Checked: {date.strftime("%a %d %B %Y at %I:%M%p")}<br><br>Link: https://amazon.co.uk/dp/{csv_line_entry[0]}<br>Product Name: {amz_product_title}<br>Asin: {csv_line_entry[0]}<br>Store Price: £{amazon_price}<br>Notification Price: £{csv_line_entry[1]}<br>Total Saving: £{saving}<br></body></html>'

    _attach = MIMEText(bodyStr.encode('utf-8'), 'html', 'UTF-8')        
    msg.attach(_attach)

    server.sendmail(frm, to, msg.as_string())
    server.quit()   

    print("   > EMAIL HAS BEEN SENT\n\n")

run_scraper()

您已经确定了手头的任务:您不想为每个项目都发送一封电子邮件,而是希望将这些项目收集在一个列表中,然后将所有这些项目格式化为一个电子邮件正文并发送该电子邮件。

首先,在#scrape data步骤

之前创建一个列表来存储您的所有信息
all_email_items = []

# Scrape data
for csv_line_entry in csv_reader:
    ... 

不是为每个项目调用 send_email,而是将所有信息保存到列表中。


if (float(amazon_price)) < float(csv_line_entry[1]):
    # print stuff like you already do
    ...

    #Make a tuple with the relevant info of the current item
    current_item = (amz_product_title, csv_line_entry, amazon_price, saving)
    all_email_items.append(current_item)

    # DON'T CALL send_email() here
    # send_email(amz_product_title, csv_line_entry, amazon_price, saving)

最后,for csv_line_entry 循环之外,用这个列表调用 send_email

with open("amazon-list-of-asins.csv", "r") as f:
    ...
    for csv_line_entry in csv_reader
        ...
    
    send_email(all_email_items)

记得修改 send_email() 以获取元组列表而不是常规参数:

def send_email(all_items):
    # Connect to the server, etc.

    bodyStr = f'<!DOCTYPE html><html><body>Date & Time Checked: {date.strftime("%a %d %B %Y at %I:%M%p")}'

    for current_item in all_items:
        # Unpack the tuple into variables you already use
        amz_product_title, csv_line_entry, amazon_price, saving = current_item
        # Add info for current item to bodyStr
        bodyStr = bodyStr + f"<br><br>Link: https://amazon.co.uk/dp/{csv_line_entry[0]}<br>Product Name: {amz_product_title}<br>Asin: {csv_line_entry[0]}<br>Store Price: £{amazon_price}<br>Notification Price: £{csv_line_entry[1]}<br>Total Saving: £{saving}<br>"

    # Close html body
    bodyStr = bodyStr + '</body></html>'


    # Finish sending the email
    ...