update: pivot to pandas from csv
This commit is contained in:
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
84
邮件批量发送脚本.py
84
邮件批量发送脚本.py
@@ -1,6 +1,7 @@
|
|||||||
import argparse
|
import argparse
|
||||||
|
import pandas
|
||||||
import time
|
import time
|
||||||
import csv
|
import itertools
|
||||||
|
|
||||||
from selenium import webdriver
|
from selenium import webdriver
|
||||||
from selenium.common.exceptions import StaleElementReferenceException, TimeoutException
|
from selenium.common.exceptions import StaleElementReferenceException, TimeoutException
|
||||||
@@ -15,10 +16,10 @@ parser = argparse.ArgumentParser(description="邮件批量发送脚本")
|
|||||||
parser.add_argument('input', nargs='?')
|
parser.add_argument('input', nargs='?')
|
||||||
parser.add_argument('--column-address', type=str, nargs='?', default='邮箱')
|
parser.add_argument('--column-address', type=str, nargs='?', default='邮箱')
|
||||||
parser.add_argument('--column-code', type=str, nargs='?', default='客户编号')
|
parser.add_argument('--column-code', type=str, nargs='?', default='客户编号')
|
||||||
|
parser.add_argument('--column-mark', type=str, nargs='?', default='已发送')
|
||||||
parser.add_argument('-u', '--url', type=str, nargs='?', default='https://id.ionos.fr/identifier')
|
parser.add_argument('-u', '--url', type=str, nargs='?', default='https://id.ionos.fr/identifier')
|
||||||
parser.add_argument('-a', '--address', type=str, required=True)
|
parser.add_argument('-a', '--address', type=str, required=True)
|
||||||
parser.add_argument('-p', '--password', type=str, required=True)
|
parser.add_argument('-p', '--password', type=str, required=True)
|
||||||
parser.add_argument('-e', '--encoding', type=str, nargs='?', default='utf-8')
|
|
||||||
parser.add_argument('-t', '--timeout', type=int, nargs='?', default=60)
|
parser.add_argument('-t', '--timeout', type=int, nargs='?', default=60)
|
||||||
parser.add_argument('-i', '--interval', type=int, nargs='?', default=10)
|
parser.add_argument('-i', '--interval', type=int, nargs='?', default=10)
|
||||||
parser.add_argument('-m', '--max-occurrence', type=int, nargs='?', default=5)
|
parser.add_argument('-m', '--max-occurrence', type=int, nargs='?', default=5)
|
||||||
@@ -35,20 +36,23 @@ def main():
|
|||||||
if not args.input:
|
if not args.input:
|
||||||
root = Tk()
|
root = Tk()
|
||||||
root.withdraw()
|
root.withdraw()
|
||||||
args.input = filedialog.askopenfilename(defaultextension='csv')
|
args.input = filedialog.askopenfilename(defaultextension='xlsx')
|
||||||
|
|
||||||
print(f'[信息] 正在读取数据:{args.input}')
|
print(f'[信息] 正在读取数据:{args.input}')
|
||||||
try:
|
try:
|
||||||
with open(args.input, 'r', encoding=args.encoding) as file:
|
workbook = pandas.read_excel(args.input)
|
||||||
rows = csv.DictReader(file)
|
data = workbook.where(pandas.notnull(workbook), None).to_dict(orient='list')
|
||||||
data = list(rows)
|
recipients = data.get(args.column_address, [])
|
||||||
|
codes = data.get(args.column_code, [])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f'[!!!!] 读取数据表失败:{e}')
|
print(f'[!!!!] 读取数据表失败:{e}')
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
rate = 60 / (args.interval + 3)
|
limit = len(recipients)
|
||||||
limit = len(data)
|
marks = data.setdefault(args.column_mark, [None] * limit)
|
||||||
|
|
||||||
print(f'[信息] 已读取联系人信息共 {limit} 条')
|
print(f'[信息] 已读取联系人信息共 {limit} 条')
|
||||||
|
if limit == 0: return 0
|
||||||
|
|
||||||
print('[信息] 正在启动 Chrome 自动化实例')
|
print('[信息] 正在启动 Chrome 自动化实例')
|
||||||
try:
|
try:
|
||||||
@@ -144,13 +148,14 @@ def main():
|
|||||||
print(f'[!!!!] 读取邮件时发生了错误:{e}')
|
print(f'[!!!!] 读取邮件时发生了错误:{e}')
|
||||||
return 5
|
return 5
|
||||||
|
|
||||||
|
rate = 60 / (args.interval + 3)
|
||||||
print(f'[信息] 当前发送速率 {round(rate, 2)} 封/分钟')
|
print(f'[信息] 当前发送速率 {round(rate, 2)} 封/分钟')
|
||||||
print(f'[信息] 预计使用时间 {timedelta(minutes=limit / rate)}')
|
print(f'[信息] 预计使用时间 {timedelta(minutes=limit / rate)}')
|
||||||
|
|
||||||
if rate > 8.33: print('[警告] 当前发送速率已超出限制 8.33 封/分钟')
|
if rate > 8.33: print('[警告] 当前发送速率已超出限制 8.33 封/分钟')
|
||||||
key = input('[????] 是否确定发送?确定 (Y) / 取消 (N): ')
|
key = input('[????] 是否确定发送?确定 (Y) / 取消 (N): ')
|
||||||
|
|
||||||
if key == 'y' or key == 'Y':
|
if key in ['Y', 'y']:
|
||||||
print('[信息] 已确定发送')
|
print('[信息] 已确定发送')
|
||||||
else:
|
else:
|
||||||
print('[信息] 已取消发送')
|
print('[信息] 已取消发送')
|
||||||
@@ -159,25 +164,28 @@ def main():
|
|||||||
global date
|
global date
|
||||||
date = datetime.now()
|
date = datetime.now()
|
||||||
|
|
||||||
current = None
|
index = 0
|
||||||
occurrence = 0
|
active = True
|
||||||
|
occurrences = {}
|
||||||
|
|
||||||
for row in data:
|
while active and index < limit:
|
||||||
recipient = row.get(args.column_address)
|
recipient = recipients[index]
|
||||||
code = row.get(args.column_code)
|
code = codes[index]
|
||||||
|
mark = marks[index]
|
||||||
|
occurrence = occurrences.setdefault(code, [0])
|
||||||
|
index += 1
|
||||||
|
|
||||||
if code == current:
|
if mark is not None:
|
||||||
occurrence += 1
|
print(f'[信息] 已跳过项目 {recipient}')
|
||||||
else:
|
continue
|
||||||
current = code
|
|
||||||
occurrence = 1
|
|
||||||
|
|
||||||
if args.max_occurrence > 0 and occurrence > args.max_occurrence:
|
if args.max_occurrence > 0 and occurrence[0] > args.max_occurrence:
|
||||||
print(f'[警告] 收件人 {recipient} 所属组织已重复出现 {occurrence} 次')
|
print(f'[警告] 收件人 {recipient} 所属组织已重复出现 {occurrence} 次')
|
||||||
global warnings
|
global warnings
|
||||||
warnings += 1
|
warnings += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
while active:
|
||||||
try:
|
try:
|
||||||
print(f'[信息] 正在发送:{recipient}')
|
print(f'[信息] 正在发送:{recipient}')
|
||||||
click("button[aria-label='Edit copy']")
|
click("button[aria-label='Edit copy']")
|
||||||
@@ -198,21 +206,16 @@ def main():
|
|||||||
# 发送邮件
|
# 发送邮件
|
||||||
click("div.io-ox-mail-compose-window button[data-action='send']")
|
click("div.io-ox-mail-compose-window button[data-action='send']")
|
||||||
|
|
||||||
global sent
|
|
||||||
sent += 1
|
|
||||||
except Exception as e:
|
|
||||||
print(f'[警告] 发生错误:{e}')
|
|
||||||
key = input('[????] 是否继续?确定 (Y) / 取消 (N): ')
|
|
||||||
|
|
||||||
if key == 'y' or key == 'Y': continue
|
|
||||||
else: return 6
|
|
||||||
|
|
||||||
# 检测页面警告
|
# 检测页面警告
|
||||||
try:
|
try:
|
||||||
wait = WebDriverWait(driver, timeout=args.interval)
|
wait = WebDriverWait(driver, timeout=args.interval)
|
||||||
alert = wait.until(lambda x: x.find_element(By.CSS_SELECTOR, "div.io-ox-alert.io-ox-alert-error"))
|
alert = wait.until(lambda x: x.find_element(By.CSS_SELECTOR, "div.io-ox-alert.io-ox-alert-error"))
|
||||||
except:
|
except:
|
||||||
continue
|
marks[index] = '✔'
|
||||||
|
occurrence[0] += 1
|
||||||
|
global sent
|
||||||
|
sent += 1
|
||||||
|
break
|
||||||
|
|
||||||
message = alert.text.replace('\n', ' ')
|
message = alert.text.replace('\n', ' ')
|
||||||
print(f'[警告] 来自网页:{message}')
|
print(f'[警告] 来自网页:{message}')
|
||||||
@@ -220,15 +223,30 @@ def main():
|
|||||||
global errors
|
global errors
|
||||||
errors += 1
|
errors += 1
|
||||||
|
|
||||||
# 发送失败不计入重复项
|
|
||||||
if occurrence > 1: occurrence -= 1
|
|
||||||
|
|
||||||
# 关闭警告
|
# 关闭警告
|
||||||
click("div.io-ox-alert.io-ox-alert-error button[data-action='close']")
|
click("div.io-ox-alert.io-ox-alert-error button[data-action='close']")
|
||||||
# 关闭过期邮件
|
# 关闭过期邮件
|
||||||
click("div.io-ox-mail-compose-window button[data-action='close']")
|
click("div.io-ox-mail-compose-window button[data-action='close']")
|
||||||
# 删除过期邮件
|
# 删除过期邮件
|
||||||
click("div.modal-footer button[data-action='delete']")
|
click("div.modal-footer button[data-action='delete']")
|
||||||
|
except Exception as e:
|
||||||
|
print(f'[警告] 发生错误:{e}')
|
||||||
|
key = input('[????] 重试 (R) / 跳过 (S) / 取消 (C): ')
|
||||||
|
match key:
|
||||||
|
case 'R', 'r': continue
|
||||||
|
case 'S', 's': break
|
||||||
|
case _: active = False
|
||||||
|
|
||||||
|
key = input('[????] 是否需要保存到文件?确定 (Y) / 取消 (N): ')
|
||||||
|
if key in ['Y', 'y']:
|
||||||
|
print(f'[信息] 正在写入文件:{args.input}')
|
||||||
|
try:
|
||||||
|
pandas.DataFrame.from_dict(data).to_excel(args.input, index=False, sheet_name='Sheet1')
|
||||||
|
except Exception as e:
|
||||||
|
print(f'[警告] 写入文件时发生了错误:{e}')
|
||||||
|
return 6
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
try: status = main()
|
try: status = main()
|
||||||
except KeyboardInterrupt: status = 145
|
except KeyboardInterrupt: status = 145
|
||||||
|
|||||||
Reference in New Issue
Block a user