import re
import time
import json
import threading
from selenium import webdriver
from datetime import datetime, timedelta
from selenium.webdriver.edge.service import Service as EdgeService
from selenium.webdriver.edge.options import Options as EdgeOptions
from selenium.webdriver.support.ui import WebDriverWait, Select
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from apscheduler.schedulers.background import BackgroundScheduler
from pytz import timezone
import FreeSimpleGUI as sg
BEIJING_TZ = timezone('Asia/Shanghai')
CONFIG_FILE = 'browser_config.json'
ELEMENT_TYPES = {
"按钮": "button",
"复选框": "checkbox",
"单选按钮": "radio",
"下拉框": "dropdown",
"标签页": "tab",
"时间": "time",
"文本": "text"
}
WEEKDAYS = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
SCHEDULER = None
def load_config():
default_config = {
"order": ["选项1", "选项2", "选项3", "选项4", "选项5", "选项6", "选项7"],
"elements": {
"target_url": "http://www.igs.gnsswhu.cn/index.php",
"选项1": {"type": "标签页", "identifier": "OBS观测值"},
"选项2": {"type": "下拉框", "identifier": "IGS测站列表"},
"选项3": {"type": "复选框", "identifier": "ABMF00GLP"},
"选项4": {"type": "单选按钮", "identifier": "d文件"},
"选项5": {"type": "时间", "identifier": "开始时间"},
"选项6": {"type": "时间", "identifier": "结束时间"},
"选项7": {"type": "按钮", "identifier": "检索"},
},
"start_date": "",
"end_date": "",
"schedule": {
"start_preset": "--",
"end_preset": "--",
"schedule_type": "--",
"schedule_time": "00:00:00"
},
"stay_duration": 60
}
try:
with open(CONFIG_FILE, 'r') as f:
user_config = json.load(f)
merged_config = default_config.copy()
merged_config.update(user_config) # 用户配置覆盖默认
merged_config['order'] = user_config.get('order', default_config['order']) # 确保顺序正确
# 处理日期格式
if 'start_date' in merged_config and ' ' not in merged_config.get('start_date', ''):
merged_config['start_date'] += " 00:00:00"
if 'end_date' in merged_config and ' ' not in merged_config.get('end_date', ''):
merged_config['end_date'] += " 00:00:00"
return merged_config
except:
return default_config
def save_config(values, current_order):
try:
elements = {}
for label in current_order:
elements[label] = {
"type": values[f'-{label}_TYPE-'],
"identifier": values[f'-{label}_ID-']
}
if values[f'-{label}_TYPE-'] == "文本":
elements[label]["text"] = values[f'-{label}_TEXT-']
def get_full_datetime(date_str, h, m, s):
if not date_str:
return ""
# 格式化为两位数
h = f"{int(h):02d}" if h else "00"
m = f"{int(m):02d}" if m else "00"
s = f"{int(s):02d}" if s else "00"
return f"{date_str} {h}:{m}:{s}"
start_time = get_full_datetime(values['-START_DATE-'],
values['-START_HOUR-'],
values['-START_MINUTE-'],
values['-START_SECOND-'])
end_time = get_full_datetime(values['-END_DATE-'],
values['-END_HOUR-'],
values['-END_MINUTE-'],
values['-END_SECOND-'])
config = {
"order": current_order,
"elements": {
"target_url": values['-URL-'],
**elements
},
"start_identifier": load_config().get('start_identifier', '开始时间'),
"end_identifier": load_config().get('end_identifier', '结束时间'),
"start_date": start_time,
"end_date": end_time,
"schedule": load_config().get('schedule', {}),
"stay_duration": load_config().get('stay_duration', 60)
}
with open(CONFIG_FILE, 'w') as f:
json.dump(config, f, ensure_ascii=False)
return True
except Exception as e:
print(f"保存配置失败: {str(e)}")
return False
def create_element_row(label, element_type='', identifier='', text_value=''):
return [
sg.Text(label, size=(12, 1)),
sg.Combo(
list(ELEMENT_TYPES.keys()),
default_value=element_type,
key=f'-{label}_TYPE-',
size=(15, 1),
enable_events=True
),
sg.Input(
identifier,
key=f'-{label}_ID-',
size=(20, 1)
),
sg.Input(
text_value,
key=f'-{label}_TEXT-',
size=(20, 1),
visible=(element_type == "文本")
),
sg.Button('↑', key=f'-{label}_UP-'),
sg.Button('↓', key=f'-{label}_DOWN-'),
sg.Button('-', key=f'-{label}_DEL-')
]
def create_elements_column(current_order):
config = load_config()
elements = []
for label in current_order:
element_config = config['elements'].get(label, {})
elements.append(create_element_row(
label,
element_config.get('type', ''),
element_config.get('identifier', ''),
element_config.get('text', '')
))
return sg.Column(
elements,
scrollable=True,
vertical_scroll_only=True,
size=(800, 300),
key='-ELEMENTS_COL-'
)
def show_settings_window(parent_window):
"""显示停留时间设置窗口"""
config = load_config()
start_id = config.get('start_identifier', '开始时间')
end_id = config.get('end_identifier', '结束时间')
layout = [
[sg.Text('网页停留时间(秒):'),
sg.Input(config.get('stay_duration', 60), key='-STAY_DURATION-')],
[sg.HorizontalSeparator()],
[sg.Text('开始时间字段标识:'),
sg.Input(start_id, key='-START_ID-', size=15)],
[sg.Text('结束时间字段标识:'),
sg.Input(end_id, key='-END_ID-', size=15)],
[sg.Button('保存'), sg.Button('取消')]
]
window = sg.Window('设置', layout, modal=True)
while True:
event, values = window.read()
if event in (None, '取消'):
break
if event == '保存':
try:
stay_duration = int(values['-STAY_DURATION-'])
if stay_duration <= 0:
raise ValueError("停留时间必须大于0")
config['stay_duration'] = stay_duration
config['start_identifier'] = values['-START_ID-'].strip()
config['end_identifier'] = values['-END_ID-'].strip()
with open(CONFIG_FILE, 'w') as f:
json.dump(config, f, ensure_ascii=False)
sg.popup('设置已保存!')
break
except Exception as e:
sg.popup_error(f'输入无效: {str(e)}')
window.close()
def create_gui():
config = load_config()
current_order = config["order"].copy()
elements_config = config["elements"]
schedule_config = config.get('schedule', {})
# 时间处理函数
def split_datetime(dt_str):
if not dt_str:
return "", "00", "00", "00"
if ' ' in dt_str:
date_part, time_part = dt_str.split(' ', 1)
h, m, s = time_part.split(':')[:3]
else:
date_part, h, m, s = dt_str, '00', '00', '00'
return date_part, h, m, s
# 初始化日期时间
start_date, start_h, start_m, start_s = split_datetime(config.get('start_date', ''))
end_date, end_h, end_m, end_s = split_datetime(config.get('end_date', ''))
# 主布局
layout = [
[sg.Text('目标网址'), sg.Input(config['elements']['target_url'], key='-URL-')],
[sg.HorizontalSeparator()],
[create_elements_column(current_order)],
[sg.Button('+ 添加选项', key='-ADD_OPTION-')],
[sg.HorizontalSeparator()],
# 开始时间选择
[sg.Frame('开始时间', [
[sg.CalendarButton('选择日期', target='-START_DATE-', format='%Y-%m-%d'),
sg.Input(start_date, key='-START_DATE-', size=(12, 1)),
sg.Spin([f"{i:02}" for i in range(24)], start_h, key='-START_HOUR-', size=3),
sg.Text(':'),
sg.Spin([f"{i:02}" for i in range(60)], start_m, key='-START_MINUTE-', size=3),
sg.Text(':'),
sg.Spin([f"{i:02}" for i in range(60)], start_s, key='-START_SECOND-', size=3)]
])],
# 结束时间选择
[sg.Frame('结束时间', [
[sg.CalendarButton('选择日期', target='-END_DATE-', format='%Y-%m-%d'),
sg.Input(end_date, key='-END_DATE-', size=(12, 1)),
sg.Spin([f"{i:02}" for i in range(24)], end_h, key='-END_HOUR-', size=3),
sg.Text(':'),
sg.Spin([f"{i:02}" for i in range(60)], end_m, key='-END_MINUTE-', size=3),
sg.Text(':'),
sg.Spin([f"{i:02}" for i in range(60)], end_s, key='-END_SECOND-', size=3)]
])],
# 控制按钮
[sg.HorizontalSeparator()],
[sg.Button('运行', size=10),
sg.Button('定时', key='-SCHEDULE-', size=10),
sg.Button('保存配置', size=10),
sg.Button('设置', size=10),
sg.Button('退出', size=10)],
[sg.Output(size=(90, 10), echo_stdout_stderr=True, key='-OUTPUT-')]
]
window = sg.Window('浏览器自动化工具', layout, finalize=True)
setup_scheduler(window)
# 动态更新元素可见性
def update_element_visibility():
for label in current_order:
element_type = values.get(f'-{label}_TYPE-', '')
window[f'-{label}_TEXT-'].update(visible=(element_type == "文本"))
# 主事件循环
while True:
event, values = window.read()
if event in (None, '退出'):
break
# 元素类型变化事件
if '_TYPE-' in event:
update_element_visibility()
# 添加新选项
if event == '-ADD_OPTION-':
new_label = f'选项{len(current_order) + 1}'
current_order.append(new_label)
window.extend_layout(window['-ELEMENTS_COL-'], [create_element_row(new_label)])
window['-ELEMENTS_COL-'].contents_changed()
# 删除选项
if '_DEL-' in event:
label = event.split('_')[1]
if label in current_order:
current_order.remove(label)
# 删除对应的UI元素
for element in window['-ELEMENTS_COL-'].Widget.winfo_children():
if f'_{label}_' in str(element):
element.destroy()
# 重新编号剩余选项
new_order = [f"选项{i + 1}" for i in range(len(current_order))]
config_changes = {}
for old, new in zip(current_order, new_order):
config_changes[old] = new
# 更新配置和当前顺序
current_order = new_order.copy()
window['-ELEMENTS_COL-'].update(visible=False)
window['-ELEMENTS_COL-'].update(visible=True)
# 移动选项位置
if '_UP-' in event or '_DOWN-' in event:
direction = -1 if '_UP-' in event else 1
label = event.split('_')[1]
index = current_order.index(label)
new_index = index + direction
if 0 <= new_index < len(current_order):
# 交换顺序
current_order.insert(new_index, current_order.pop(index))
# 重新排列UI元素
elements = [create_element_row(lbl) for lbl in current_order]
window['-ELEMENTS_COL-'].update(visible=False)
window['-ELEMENTS_COL-'].update(elements)
window['-ELEMENTS_COL-'].contents_changed()
window['-ELEMENTS_COL-'].update(visible=True)
# 保存配置
if event == '保存配置':
elements = {}
for label in current_order:
elements[label] = {
"type": values[f'-{label}_TYPE-'],
"identifier": values[f'-{label}_ID-']
}
if values[f'-{label}_TYPE-'] == "文本":
elements[label]["text"] = values[f'-{label}_TEXT-']
# 构建时间字符串
def build_time_str(date_part, h, m, s):
return f"{date_part} {h}:{m}:{s}" if date_part else ""
new_config = {
"order": current_order,
"elements": {
"target_url": values['-URL-'],
** elements
},
"start_date": build_time_str(
values['-START_DATE-'],
values['-START_HOUR-'],
values['-START_MINUTE-'],
values['-START_SECOND-']
),
"end_date": build_time_str(
values['-END_DATE-'],
values['-END_HOUR-'],
values['-END_MINUTE-'],
values['-END_SECOND-']
),
"schedule": config.get('schedule', {}),
"stay_duration": config.get('stay_duration', 60)
}
try:
with open(CONFIG_FILE, 'w') as f:
json.dump(new_config, f, indent=4, ensure_ascii=False)
sg.popup('配置保存成功!', title='保存结果')
except Exception as e:
sg.popup_error(f'保存失败: {str(e)}')
# 运行自动化
if event == '运行':
print("启动浏览器自动化...")
threading.Thread(
target=browser_automation,
args=(values, current_order),
daemon=True
).start()
# 打开设置窗口
if event == '设置':
show_settings_window(window)
# 定时设置
if event == '-SCHEDULE-':
show_schedule_settings(window, config)
# 清理资源
window.close()
if SCHEDULER and SCHEDULER.running:
SCHEDULER.shutdown()
def calculate_dynamic_date(preset):
if not preset or preset == "--":
return None
week_type, weekday_str = preset[:1], preset[1:]
week_offset = -1 if week_type == "上" else 0
try:
weekday_index = WEEKDAYS.index(weekday_str)
except ValueError:
return None
today = datetime.now(BEIJING_TZ)
current_week_monday = today - timedelta(days=today.weekday())
target_week_monday = current_week_monday + timedelta(weeks=week_offset)
return target_week_monday + timedelta(days=weekday_index)
def show_schedule_settings(parent_window, config):
schedule = config.get('schedule', {})
def split_preset_time(time_str):
if time_str and re.match(r"\d{2}:\d{2}:\d{2}", time_str):
return time_str.split(":")
return ["00", "00", "00"]
start_time_parts = split_preset_time(schedule.get('start_preset_time', "00:00:00"))
end_time_parts = split_preset_time(schedule.get('end_preset_time', "00:00:00"))
layout = [
[sg.Text('开始日期预设:'),
sg.Combo(["--"] + [f"上{day}" for day in WEEKDAYS] + [f"本{day}" for day in WEEKDAYS],
default_value=schedule.get('start_preset', '--'),
key='-START_PRESET-',
enable_events=True,
size=(12, 1)),
sg.Text("时间:"),
sg.Spin([f"{i:02}" for i in range(24)], start_time_parts[0], key='-START_PRESET_H-', size=3),
sg.Text(':'),
sg.Spin([f"{i:02}" for i in range(60)], start_time_parts[1], key='-START_PRESET_M-', size=3),
sg.Text(':'),
sg.Spin([f"{i:02}" for i in range(60)], start_time_parts[2], key='-START_PRESET_S-', size=3),
sg.Text("实际日期:", size=(10, 1)),
sg.Text("", key='-REAL_START-', size=15)],
[sg.Text('结束日期预设:'),
sg.Combo(["--"] + [f"上{day}" for day in WEEKDAYS] + [f"本{day}" for day in WEEKDAYS],
default_value=schedule.get('end_preset', '--'),
key='-END_PRESET-',
enable_events=True,
size=(12, 1)),
sg.Text("时间:"),
sg.Spin([f"{i:02}" for i in range(24)], end_time_parts[0], key='-END_PRESET_H-', size=3),
sg.Text(':'),
sg.Spin([f"{i:02}" for i in range(60)], end_time_parts[1], key='-END_PRESET_M-', size=3),
sg.Text(':'),
sg.Spin([f"{i:02}" for i in range(60)], end_time_parts[2], key='-END_PRESET_S-', size=3),
sg.Text("实际日期:", size=(10, 1)),
sg.Text("", key='-REAL_END-', size=15)],
[sg.Text('定时执行:'),
sg.Combo(["--"] + WEEKDAYS,
default_value=schedule.get('schedule_type', '--'),
key='-SCHEDULE_TYPE-',
size=(8, 1)),
sg.Input(schedule.get('schedule_time', '00:00:00'),
key='-SCHEDULE_TIME-',
size=(8, 1),
tooltip="格式: HH:MM:SS")],
[sg.Button('保存'), sg.Button('清除定时'), sg.Button('取消')],
]
window = sg.Window('定时设置', layout, finalize=True)
def update_dates():
start_date = calculate_dynamic_date(window['-START_PRESET-'].get())
end_date = calculate_dynamic_date(window['-END_PRESET-'].get())
window['-REAL_START-'].update(start_date.strftime('%Y-%m-%d') if start_date else "")
window['-REAL_END-'].update(end_date.strftime('%Y-%m-%d') if end_date else "")
update_dates()
while True:
event, values = window.read(timeout=500)
if event in (None, '取消'):
break
if event == '清除定时':
window['-START_PRESET-'].update('--')
window['-END_PRESET-'].update('--')
window['-SCHEDULE_TYPE-'].update('--')
window['-SCHEDULE_TIME-'].update('00:00:00')
window['-REAL_START-'].update('')
window['-REAL_END-'].update('')
config['schedule'] = {
"start_preset": "--",
"end_preset": "--",
"schedule_type": "--",
"schedule_time": "00:00:00"
}
with open(CONFIG_FILE, 'w') as f:
json.dump(config, f, ensure_ascii=False)
global SCHEDULER
if SCHEDULER and SCHEDULER.running:
SCHEDULER.shutdown()
SCHEDULER = None
sg.popup("定时设置已重置")
if event in ('-START_PRESET-', '-END_PRESET-', '__TIMEOUT__'):
update_dates()
if event == '保存':
try:
start_h = int(values['-START_PRESET_H-'])
start_m = int(values['-START_PRESET_M-'])
start_s = int(values['-START_PRESET_S-'])
end_h = int(values['-END_PRESET_H-'])
end_m = int(values['-END_PRESET_M-'])
end_s = int(values['-END_PRESET_S-'])
if not (0 <= start_h <= 23 and 0 <= end_h <= 23):
raise ValueError("小时需在00-23之间")
if not (0 <= start_m <= 59 and 0 <= end_m <= 59):
raise ValueError("分钟需在00-59之间")
if not (0 <= start_s <= 59 and 0 <= end_s <= 59):
raise ValueError("秒数需在00-59之间")
except ValueError as e:
sg.popup_error(f"时间输入错误: {str(e)}")
continue
config['schedule']['start_preset_time'] = f"{values['-START_PRESET_H-']}:{values['-START_PRESET_M-']}:{values['-START_PRESET_S-']}"
config['schedule']['end_preset_time'] = f"{values['-END_PRESET_H-']}:{values['-END_PRESET_M-']}:{values['-END_PRESET_S-']}"
if values['-START_PRESET-'] != "--":
parent_window['-START_HOUR-'].update(values['-START_PRESET_H-'])
parent_window['-START_MINUTE-'].update(values['-START_PRESET_M-'])
parent_window['-START_SECOND-'].update(values['-START_PRESET_S-'])
if values['-END_PRESET-'] != "--":
parent_window['-END_HOUR-'].update(values['-END_PRESET_H-'])
parent_window['-END_MINUTE-'].update(values['-END_PRESET_M-'])
parent_window['-END_SECOND-'].update(values['-END_PRESET_S-'])
if not re.match(r'^([0-1]\d|2[0-3]):[0-5]\d:[0-5]\d$', values['-SCHEDULE_TIME-']):
sg.popup_error("时间格式应为HH:MM:SS")
continue
start_preset_val = values['-START_PRESET-']
end_preset_val = values['-END_PRESET-']
calculated_start = calculate_dynamic_date(start_preset_val)
calculated_end = calculate_dynamic_date(end_preset_val)
if start_preset_val != "--" and calculated_start:
date_part = calculated_start.strftime('%Y-%m-%d')
# 更新主窗口的开始日期输入框
parent_window['-START_DATE-'].update(date_part)
# 同时更新配置中的日期部分
config[
'start_date'] = f"{date_part} {values['-START_PRESET_H-']}:{values['-START_PRESET_M-']}:{values['-START_PRESET_S-']}"
if end_preset_val != "--" and calculated_end:
date_part = calculated_end.strftime('%Y-%m-%d')
# 更新主窗口的结束日期输入框
parent_window['-END_DATE-'].update(date_part)
# 同时更新配置中的日期部分
config[
'end_date'] = f"{date_part} {values['-END_PRESET_H-']}:{values['-END_PRESET_M-']}:{values['-END_PRESET_S-']}"
config['schedule'] = {
"start_preset": start_preset_val,
"end_preset": end_preset_val,
"schedule_type": values['-SCHEDULE_TYPE-'],
"schedule_time": values['-SCHEDULE_TIME-']
}
with open(CONFIG_FILE, 'w') as f:
json.dump(config, f, ensure_ascii=False)
setup_scheduler(parent_window)
sg.popup("定时设置已保存!")
window.close()
def setup_scheduler(parent_window):
global SCHEDULER
if SCHEDULER and SCHEDULER.running:
SCHEDULER.shutdown()
config = load_config()
schedule = config.get('schedule', {})
if schedule.get('schedule_type') == "--" or not schedule.get('schedule_time'):
return
SCHEDULER = BackgroundScheduler(timezone=BEIJING_TZ)
def scheduled_task():
try:
now = datetime.now(BEIJING_TZ)
print(f"\n[{now.strftime('%Y-%m-%d %H:%M:%S')}] 定时任务启动")
parent_window.write_event_value('执行定时任务', None)
except Exception as e:
print(f"定时任务异常: {str(e)}")
try:
h, m, s = map(int, schedule['schedule_time'].split(':'))
weekday_index = WEEKDAYS.index(schedule['schedule_type'])
SCHEDULER.add_job(
scheduled_task,
'cron',
day_of_week=weekday_index,
hour=h,
minute=m,
second=s,
misfire_grace_time=60
)
SCHEDULER.start()
next_run = SCHEDULER.get_jobs()[0].next_run_time.astimezone(BEIJING_TZ)
print(f"定时任务已激活,每周{WEEKDAYS[weekday_index]} {schedule['schedule_time']} 执行")
print(f"下次执行时间: {next_run.strftime('%Y-%m-%d %H:%M:%S')}")
except Exception as e:
sg.popup_error(f"定时设置错误: {str(e)}")
def browser_automation(values, current_order):
try:
if current_order is None:
config = load_config()
current_order = config["order"]
config = load_config()
STAY_DURATION = config.get('stay_duration', 60)
schedule = config.get('schedule', {})
start_date = values['-START_DATE-']
start_time = f"{values['-START_HOUR-']}:{values['-START_MINUTE-']}:{values['-START_SECOND-']}"
full_start = f"{start_date} {start_time}" if start_date else ""
end_date = values['-END_DATE-']
end_time = f"{values['-END_HOUR-']}:{values['-END_MINUTE-']}:{values['-END_SECOND-']}"
full_end = f"{end_date} {end_time}" if end_date else ""
if not start_date or not end_date:
print("错误:日期预设配置无效")
return
print(f"当前日期范围: {full_start} 至 {full_end}")
service = EdgeService(r"C:\Program Files (x86)\Microsoft\Edge\Application\msedgedriver.exe")
options = EdgeOptions()
options.use_chromium = True
driver = webdriver.Edge(service=service, options=options)
modified_values = values.copy()
modified_values['-FULL_START-'] = full_start
modified_values['-FULL_END-'] = full_end
driver.get(values['-URL-'])
time.sleep(2)
for label in current_order:
element_config = config['elements'].get(label, {})
if element_config and element_config.get('identifier'):
handle_element(driver, label, element_config, modified_values)
time.sleep(0.5)
print("自动化操作成功完成!")
print(f"网页将保持打开状态{STAY_DURATION}秒...")
time.sleep(STAY_DURATION)
except Exception as e:
print(f"执行过程中发生错误: {str(e)}")
if 'driver' in locals():
driver.save_screenshot(f'error_{int(time.time())}.png')
finally:
if 'driver' in locals():
driver.quit()
print("浏览器已关闭")
def handle_element(driver, label, element_config, config):
element_type = ELEMENT_TYPES[element_config['type']]
identifier = element_config['identifier']
clean_identifier = identifier.replace(' ', '')
schedule_config = config
start_identifier = schedule_config.get('start_identifier', '开始时间')
end_identifier = schedule_config.get('end_identifier', '结束时间')
try:
if element_type == 'button':
xpath = f"//div[translate(normalize-space(), ' ', '') = '{clean_identifier}']"
element = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, xpath)))
driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", element)
driver.execute_script("arguments[0].click();", element)
elif element_type == 'checkbox':
xpath = f"//span[translate(normalize-space(), ' ', '')='{clean_identifier}']/preceding-sibling::input[@type='checkbox']"
checkbox = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, xpath)))
if not checkbox.is_selected():
driver.execute_script("arguments[0].click();", checkbox)
elif element_type == 'radio':
xpath = f"//input[@type='radio']/following-sibling::text()[contains(., '{identifier}')]/preceding::input[1]"
radio = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, xpath)))
driver.execute_script("arguments[0].click();", radio)
elif element_type == 'dropdown':
xpath = f"//span[translate(normalize-space(), ' ', '')='{clean_identifier}']/following::select[1]"
select_element = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, xpath)))
Select(select_element).select_by_index(0)
elif element_type == 'tab':
xpath = f"//div[contains(@class,'tab')]/span[translate(normalize-space(), ' ', '')='{clean_identifier}']"
tab = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, xpath)))
driver.execute_script("arguments[0].click();", tab)
time.sleep(1)
elif element_type == 'time':
xpath = f"//span[translate(normalize-space(), ' ', '')='{clean_identifier}']/following::input[1]"
field = WebDriverWait(driver, 20).until(EC.presence_of_element_located((By.XPATH, xpath)))
schedule_config = config.get('schedule', {})
start_id = schedule_config.get('start_identifier', '开始时间')
end_id = schedule_config.get('end_identifier', '结束时间')
if element_config['identifier'] == start_id:
full_value = config.get('-FULL_START-', '')
elif element_config['identifier'] == end_id:
full_value = config.get('-FULL_END-', '')
else:
print(f"未配置的时间字段: {element_config['identifier']}")
return
attempts = []
if full_value:
attempts.append(full_value)
if ' ' in full_value:
attempts.append(full_value.split(' ')[0])
attempts.append(full_value.split(' ')[1])
success = False
for attempt in attempts:
try:
field.clear()
field.send_keys(attempt)
time.sleep(0.5)
current_value = field.get_attribute('value')
if current_value.strip() == attempt.strip():
success = True
break
except Exception as e:
print(f"尝试输入 '{attempt}' 失败: {str(e)}")
continue
if not success:
raise ValueError(f"无法输入日期时间: {full_value}")
elif element_type == 'text':
try:
# 查找包含标识文本的元素
base_element = WebDriverWait(driver, 20).until(
EC.presence_of_element_located(
(By.XPATH, f"//*[contains(text(), '{identifier}')]"))
)
# 查找最近的输入框(前、后或父级相邻)
input_element = base_element.find_element(By.XPATH,
"./following-sibling::input | " +
"./preceding-sibling::input | " +
"../following-sibling::input | " +
"ancestor::div/following-sibling::input"
)
# 清空并输入文本
input_element.clear()
input_element.send_keys(element_config.get('text', ''))
print(f"成功输入文本: {element_config['text']}")
except Exception as e:
print(f"查找输入框失败: {str(e)}")
raise
return
print(f"成功处理: {label}")
except Exception as e:
print(f"处理 {label} 时出错: {str(e)}")
raise
if __name__ == "__main__":
create_gui()
以上代码的删除功能键点击无效,点击上下移动界面闪退,修改