"""
日报自动化填写脚本
依赖安装:
1. 安装Python依赖包:
pip install playwright
pip install psutil
pip install asyncio
2. 安装Playwright浏览器:
playwright install chromium
注意事项:
1. 确保Chrome浏览器已安装
2. 脚本使用Chrome用户配置文件,路径为:C:/Users/zhens/AppData/Local/Google/Chrome/User Data
3. 运行前请确保没有其他Chrome进程在运行
4. 脚本会自动关闭所有Chrome进程,请确保没有重要工作未保存
5. 如果修改了Chrome用户配置路径,需要相应修改代码中的user_data_dir变量
6. 脚本运行时会打开Chrome浏览器窗口,请勿手动操作浏览器
7. 如果遇到网络问题,可以适当增加等待时间(asyncio.sleep的值)
8. 如果提交失败,可以检查网络连接或适当增加等待时间
"""
import asyncio
import os
import psutil
import time
import tkinter as tk
from tkinter import scrolledtext
from playwright.async_api import async_playwright
def get_user_input():
"""通过GUI窗口获取用户输入的日报内容"""
root = tk.Tk()
root.title("日报填写助手")
root.geometry("900x700")
# 设置窗口样式
root.configure(bg='#f0f0f0')
root.option_add('*Font', '微软雅黑 10')
# 创建主框架
main_frame = tk.Frame(root, bg='#f0f0f0', padx=20, pady=20)
main_frame.pack(fill=tk.BOTH, expand=True)
# 创建标题
title_label = tk.Label(main_frame,
text="日报填写",
font=("微软雅黑", 16, "bold"),
bg='#f0f0f0',
pady=10)
title_label.pack()
# 创建今日工作总结输入区域
summary_frame = tk.LabelFrame(main_frame,
text="今日工作总结",
font=("微软雅黑", 12),
bg='#f0f0f0',
padx=10,
pady=5)
summary_frame.pack(fill=tk.BOTH, expand=True, pady=10)
summary_text = scrolledtext.ScrolledText(summary_frame,
width=80,
height=8,
font=("微软雅黑", 11),
wrap=tk.WORD,
padx=10,
pady=10)
summary_text.pack(fill=tk.BOTH, expand=True)
# 创建明日工作计划输入区域
plan_frame = tk.LabelFrame(main_frame,
text="明日工作计划",
font=("微软雅黑", 12),
bg='#f0f0f0',
padx=10,
pady=5)
plan_frame.pack(fill=tk.BOTH, expand=True, pady=10)
plan_text = scrolledtext.ScrolledText(plan_frame,
width=80,
height=8,
font=("微软雅黑", 11),
wrap=tk.WORD,
padx=10,
pady=10)
plan_text.pack(fill=tk.BOTH, expand=True)
# 创建底部控制区域
bottom_frame = tk.Frame(main_frame, bg='#f0f0f0', pady=10)
bottom_frame.pack(fill=tk.X)
# 创建复选框
keep_original = tk.BooleanVar(value=False)
check_button = tk.Checkbutton(bottom_frame,
text="保留原有内容",
variable=keep_original,
font=("微软雅黑", 11),
bg='#f0f0f0')
check_button.pack(side=tk.LEFT, padx=10)
# 创建提交按钮
result = {"summary": "", "plan": "", "is_submitted": False, "keep_original": False}
def submit():
result["summary"] = summary_text.get("1.0", tk.END).strip()
result["plan"] = plan_text.get("1.0", tk.END).strip()
result["keep_original"] = keep_original.get()
result["is_submitted"] = True
root.quit()
root.destroy()
submit_button = tk.Button(bottom_frame,
text="提交",
command=submit,
font=("微软雅黑", 12, "bold"),
bg='#4CAF50',
fg='white',
width=15,
height=2,
relief=tk.FLAT)
submit_button.pack(side=tk.RIGHT, padx=10)
# 设置窗口居中
root.update_idletasks()
width = root.winfo_width()
height = root.winfo_height()
x = (root.winfo_screenwidth() // 2) - (width // 2)
y = (root.winfo_screenheight() // 2) - (height // 2)
root.geometry(f'{width}x{height}+{x}+{y}')
# 设置窗口图标(如果有的话)
try:
root.iconbitmap('icon.ico') # 如果有图标文件的话
except:
pass
root.mainloop()
return result["summary"], result["plan"], result["is_submitted"], result["keep_original"]
# 执行自动化浏览器操作的协程函数
def close_chrome():
"""关闭所有Chrome进程"""
for proc in psutil.process_iter(['name']):
try:
if proc.info['name'] == 'chrome.exe':
proc.kill()
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
# 等待进程完全关闭
time.sleep(2)
async def run(playwright):
# 确保Chrome已关闭
close_chrome()
# 使用用户实际的Chrome配置,配置查询chrome://version
user_data_dir = r'C:/Users/zhens/AppData/Local/Google/Chrome/User Data'
def text_to_div_html(text):
"""将多行文本转换为
...
格式的HTML"""
lines = [line.strip() for line in text.strip().splitlines() if line.strip()]
return ''.join(f'{line}
' for line in lines)
# 获取用户输入的日报内容
summary_text, plan_text, is_submitted, keep_original = get_user_input()
# 如果用户取消输入,直接退出程序
if not is_submitted:
print("用户取消了日报填写,程序退出")
return
summary_html = text_to_div_html(summary_text)
plan_html = text_to_div_html(plan_text)
try:
# 使用已有的Chrome配置文件启动浏览器
context = await playwright.chromium.launch_persistent_context(
user_data_dir,
channel="chrome",
headless=False,
args=[
'--start-maximized',
'--disable-blink-features=AutomationControlled',
'--profile-directory=Default',
'--no-first-run',
'--no-default-browser-check'
],
ignore_default_args=['--enable-automation']
)
try:
# 创建新页面
page = await context.new_page()
# 设置实际的User-Agent
await page.set_extra_http_headers({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36'
})
# 访问页面并等待网络空闲
await page.goto('https://doc.weixin.qq.com/forms/j/AFIADwd9AA4AUcA6AbCADgJXbq19fUR0j_base?page=6',
wait_until='networkidle')
# 等待元素出现并点击
await page.wait_for_selector('div.TitleBarBtn_title__dKAyV:text("填写")', timeout=60000)
await page.click('div.TitleBarBtn_title__dKAyV:text("填写")', delay=100)
# 等待操作完成,确保页面完全加载
await asyncio.sleep(5)
# 查找所有可编辑的输入框
editors = await page.query_selector_all('.maileditor-editorview[contenteditable="true"]')
print("找到输入框数量:", len(editors))
# 打印每个输入框的父级HTML,帮助确认 data-qid,每次打开data-qid都会不同所以不准确
# for i, editor in enumerate(editors):
# parent = await editor.evaluate_handle('el => el.closest("div.question")')
# parent_html = await parent.evaluate('el => el.outerHTML')
# print(f"输入框{i+1}父级HTML:\n{parent_html}\n")
# if len(editors) >= 2:
# await editors[0].evaluate(f'el => el.innerHTML = `{summary_html}`')
# await editors[1].evaluate(f'el => el.innerHTML = `{plan_html}`')
# 今日工作总结输入
# 1. 先点击输入框,确保获得焦点
await page.locator('div.question:has-text("今日工作总结") .maileditor-editorview[contenteditable="true"]').click()
# 2. 如果不保留原有内容,则清空输入框
if not keep_original:
await page.keyboard.press('Control+A')
await page.keyboard.press('Delete')
# 3. 使用键盘输入方式模拟真实输入
await page.keyboard.type(summary_text)
# 4. 等待输入完成,确保内容被正确输入
await asyncio.sleep(2)
# 明日工作计划输入
# 1. 先点击输入框,确保获得焦点
await page.locator('div.question:has-text("明日工作计划") .maileditor-editorview[contenteditable="true"]').click()
# 2. 如果不保留原有内容,则清空输入框
if not keep_original:
await page.keyboard.press('Control+A')
await page.keyboard.press('Delete')
# 3. 使用键盘输入方式模拟真实输入
await page.keyboard.type(plan_text)
# 4. 等待输入完成
await asyncio.sleep(2)
# 等待内容保存
# 给系统足够的时间来保存输入的内容
await asyncio.sleep(5)
# 点击提交按钮
# 1. 等待提交按钮出现
# await page.wait_for_selector('button.FillFooter_confirm__0ClPl', timeout=60000)
# # 2. 点击提交按钮,添加延迟模拟真实点击
# await page.click('button.FillFooter_confirm__0ClPl', delay=100)
# 等待提交完成
# 给系统足够的时间来处理提交操作
await asyncio.sleep(5)
finally:
# 确保浏览器正常关闭
await context.close()
except Exception as e:
print(f"发生错误: {str(e)}")
raise
# 主函数,用于启动 playwright 并调用 run 函数
async def main():
async with async_playwright() as playwright:
await run(playwright)
# 判断当前环境是否已经有事件循环在运行
if __name__ == "__main__":
try:
# 尝试获取正在运行的事件循环(某些 IDE/Jupyter 会预先启动)
loop = asyncio.get_running_loop()
except RuntimeError:
loop = None
# 如果事件循环存在且正在运行(比如在 Jupyter Notebook 中)
if loop and loop.is_running():
print("检测到事件循环正在运行,使用 create_task 启动协程")
asyncio.create_task(main()) # 使用 create_task 异步运行
else:
# 否则,正常使用 asyncio.run 启动主协程
asyncio.run(main())