update: created main loop & pivot to wxPython

This commit is contained in:
2025-07-31 14:33:18 +08:00
parent 4173ff9b5a
commit 6597ced3cb
2 changed files with 232 additions and 214 deletions

Binary file not shown.

View File

@@ -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