update: created main loop & pivot to wxPython
This commit is contained in:
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
86
邮件批量发送脚本.py
86
邮件批量发送脚本.py
@@ -2,6 +2,7 @@ import unicodedata
|
|||||||
import argparse
|
import argparse
|
||||||
import pandas
|
import pandas
|
||||||
import time
|
import time
|
||||||
|
import wx
|
||||||
|
|
||||||
from selenium import webdriver
|
from selenium import webdriver
|
||||||
from selenium.common.exceptions import StaleElementReferenceException, TimeoutException
|
from selenium.common.exceptions import StaleElementReferenceException, TimeoutException
|
||||||
@@ -11,20 +12,18 @@ from selenium.webdriver.support import expected_conditions as EC
|
|||||||
from selenium.webdriver.support.wait import WebDriverWait
|
from selenium.webdriver.support.wait import WebDriverWait
|
||||||
from selenium.webdriver.remote.webelement import WebElement
|
from selenium.webdriver.remote.webelement import WebElement
|
||||||
|
|
||||||
from tkinter import Tk, filedialog
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from zoneinfo import ZoneInfo
|
from zoneinfo import ZoneInfo
|
||||||
from nameparser import HumanName
|
from nameparser import HumanName
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="邮件批量发送脚本")
|
parser = argparse.ArgumentParser(description="邮件批量发送脚本")
|
||||||
parser.add_argument('input', nargs='?')
|
parser.add_argument('url', nargs='?', default='https://id.ionos.fr/identifier')
|
||||||
parser.add_argument('--column-address', type=str, nargs='?', default='邮箱')
|
parser.add_argument('--column-address', type=str, nargs='?', default='邮箱')
|
||||||
parser.add_argument('--column-name', type=str, nargs='?', default='主要联系人')
|
parser.add_argument('--column-name', 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-sent', type=str, nargs='?', default='已发送')
|
parser.add_argument('--column-sent', type=str, nargs='?', default='已发送')
|
||||||
parser.add_argument('-u', '--url', type=str, nargs='?', default='https://id.ionos.fr/identifier')
|
parser.add_argument('-a', '--address', type=str, nargs='?', default='')
|
||||||
parser.add_argument('-a', '--address', type=str, required=True)
|
parser.add_argument('-p', '--password', type=str, nargs='?', default='')
|
||||||
parser.add_argument('-p', '--password', type=str, required=True)
|
|
||||||
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)
|
||||||
@@ -77,36 +76,18 @@ greetings = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
if not args.input:
|
|
||||||
root = Tk()
|
|
||||||
root.withdraw()
|
|
||||||
args.input = filedialog.askopenfilename(defaultextension='xlsx')
|
|
||||||
|
|
||||||
print(f'[信息] 正在读取数据:{args.input}')
|
|
||||||
try:
|
try:
|
||||||
workbook = pandas.read_excel(args.input)
|
print('[信息] 程序初始化中...')
|
||||||
data = workbook.where(pandas.notnull(workbook), None).to_dict(orient='list')
|
app = wx.App(None)
|
||||||
recipients = data.get(args.column_address, [])
|
|
||||||
except Exception as e:
|
|
||||||
print(f'[!!!!] 读取数据表失败:{e}')
|
|
||||||
return 1
|
|
||||||
|
|
||||||
limit = len(recipients)
|
|
||||||
names = data.get(args.column_name, [None] * limit)
|
|
||||||
codes = data.get(args.column_code, [None] * limit)
|
|
||||||
sents = data.setdefault(args.column_sent, [None] * limit)
|
|
||||||
|
|
||||||
print(f'[信息] 已读取联系人信息共 {limit} 条')
|
|
||||||
if limit == 0: return 0
|
|
||||||
|
|
||||||
print('[信息] 正在启动 Chrome 自动化实例')
|
print('[信息] 正在启动 Chrome 自动化实例')
|
||||||
try:
|
|
||||||
opts = webdriver.ChromeOptions()
|
opts = webdriver.ChromeOptions()
|
||||||
opts.add_experimental_option("excludeSwitches", ["enable-logging"])
|
opts.add_experimental_option("excludeSwitches", ["enable-logging"])
|
||||||
driver = webdriver.Chrome(opts)
|
driver = webdriver.Chrome(opts)
|
||||||
|
driver.set_page_load_timeout(args.timeout)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f'[!!!!] 初始化时发生了错误:{e}')
|
print(f'[!!!!] 初始化时发生了错误:{e}')
|
||||||
return 2
|
return 1
|
||||||
|
|
||||||
def locate(selector, condition=EC.presence_of_element_located, parent=driver) -> WebElement:
|
def locate(selector, condition=EC.presence_of_element_located, parent=driver) -> WebElement:
|
||||||
for attempt in range(args.retry):
|
for attempt in range(args.retry):
|
||||||
@@ -176,6 +157,7 @@ def main():
|
|||||||
try: click("#selectAll")
|
try: click("#selectAll")
|
||||||
except: pass
|
except: pass
|
||||||
|
|
||||||
|
if args.address and args.password:
|
||||||
try:
|
try:
|
||||||
print(f'[信息] 正在登陆 {args.address}')
|
print(f'[信息] 正在登陆 {args.address}')
|
||||||
username = locate("#username")
|
username = locate("#username")
|
||||||
@@ -188,6 +170,8 @@ def main():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f'[!!!!] 登录时发生了错误:{e}')
|
print(f'[!!!!] 登录时发生了错误:{e}')
|
||||||
return 4
|
return 4
|
||||||
|
else:
|
||||||
|
print('[信息] 请在页面上输入账户凭据')
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
@@ -202,6 +186,35 @@ def main():
|
|||||||
except:
|
except:
|
||||||
time.sleep(args.interval)
|
time.sleep(args.interval)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
print('[信息] 请选择数据源')
|
||||||
|
dialog = wx.FileDialog(None, 'Open', wildcard='*.xlsx', style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
|
||||||
|
|
||||||
|
if dialog.ShowModal() == wx.ID_OK:
|
||||||
|
filepath = dialog.GetPath()
|
||||||
|
print(f'[信息] 正在读取数据:{filepath}')
|
||||||
|
else:
|
||||||
|
print('[警告] 操作取消')
|
||||||
|
return 0
|
||||||
|
|
||||||
|
workbook = pandas.read_excel(filepath)
|
||||||
|
data = workbook.where(pandas.notnull(workbook), None).to_dict(orient='list')
|
||||||
|
recipients = data.get(args.column_address, [])
|
||||||
|
except Exception as e:
|
||||||
|
print(f'[警告] 读取数据表失败:{e}')
|
||||||
|
continue
|
||||||
|
finally:
|
||||||
|
dialog.Destroy()
|
||||||
|
|
||||||
|
limit = len(recipients)
|
||||||
|
names = data.get(args.column_name, [None] * limit)
|
||||||
|
codes = data.get(args.column_code, [None] * limit)
|
||||||
|
sents = data.setdefault(args.column_sent, [None] * limit)
|
||||||
|
|
||||||
|
print(f'[信息] 已读取联系人信息共 {limit} 条')
|
||||||
|
if limit == 0: continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 打开草稿箱
|
# 打开草稿箱
|
||||||
click("li[data-id='default0/Brouillons']", condition=EC.presence_of_element_located)
|
click("li[data-id='default0/Brouillons']", condition=EC.presence_of_element_located)
|
||||||
@@ -237,13 +250,11 @@ def main():
|
|||||||
while command is None:
|
while command is None:
|
||||||
match input('\n[????] 请选择 (留空取消操作): ').strip():
|
match input('\n[????] 请选择 (留空取消操作): ').strip():
|
||||||
case keys if not keys:
|
case keys if not keys:
|
||||||
command = -1
|
print('[信息] 操作取消')
|
||||||
|
return 0
|
||||||
case keys if keys.isdigit():
|
case keys if keys.isdigit():
|
||||||
number = int(keys)
|
number = int(keys)
|
||||||
if number < len(greetings): command = number
|
if number < len(greetings): command = number
|
||||||
if command < 0:
|
|
||||||
print('[信息] 已取消发送')
|
|
||||||
exit()
|
|
||||||
|
|
||||||
global date
|
global date
|
||||||
date = datetime.now()
|
date = datetime.now()
|
||||||
@@ -372,10 +383,17 @@ def main():
|
|||||||
elif key in ['S', 's']: break
|
elif key in ['S', 's']: break
|
||||||
else: active = False
|
else: active = False
|
||||||
|
|
||||||
if input('[????] 是否保存到文件?确定 (y) / 取消 (N): ') in ['Y', 'y']:
|
progress = index / limit * 100
|
||||||
print(f'[信息] 正在写入文件:{args.input}')
|
print('[信息] 当前进度:%.2f %%' % progress)
|
||||||
|
|
||||||
|
key = input('[????] 继续运行 (C) / 保存并退出 (s) / 不保存退出 (w): ')
|
||||||
|
if key in ['W', 'w']: return 0
|
||||||
|
elif key in ['S', 's']: break
|
||||||
|
else: continue
|
||||||
|
|
||||||
|
print(f'[信息] 正在写入文件:{filepath}')
|
||||||
try:
|
try:
|
||||||
pandas.DataFrame.from_dict(data).to_excel(args.input, index=False, sheet_name='Sheet1')
|
pandas.DataFrame.from_dict(data).to_excel(filepath, index=False, sheet_name='Sheet1')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f'[警告] 写入文件时发生了错误:{e}')
|
print(f'[警告] 写入文件时发生了错误:{e}')
|
||||||
return 6
|
return 6
|
||||||
|
|||||||
Reference in New Issue
Block a user