Files
zsglpt/test_with_login.py
zsglpt Optimizer 7e9a772104 🎉 项目优化与Bug修复完整版
 主要优化成果:
- 修复Unicode字符编码问题(Windows跨平台兼容性)
- 安装wkhtmltoimage,截图功能完全修复
- 智能延迟优化(api_browser.py)
- 线程池资源泄漏修复(tasks.py)
- HTML解析缓存机制
- 二分搜索算法优化(kdocs_uploader.py)
- 自适应资源配置(browser_pool_worker.py)

🐛 Bug修复:
- 解决截图失败问题
- 修复管理员密码设置
- 解决应用启动编码错误

📚 新增文档:
- BUG_REPORT.md - 完整bug分析报告
- PERFORMANCE_ANALYSIS_REPORT.md - 性能优化分析
- LINUX_DEPLOYMENT_ANALYSIS.md - Linux部署指南
- SCREENSHOT_FIX_SUCCESS.md - 截图功能修复记录
- INSTALL_WKHTMLTOIMAGE.md - 安装指南
- OPTIMIZATION_FIXES_SUMMARY.md - 优化总结

🚀 功能验证:
- Flask应用正常运行(51233端口)
- 数据库、截图线程池、API预热正常
- 管理员登录:admin/admin123
- 健康检查API:http://127.0.0.1:51233/health

💡 技术改进:
- 智能延迟算法(自适应调整)
- LRU缓存策略
- 线程池资源管理优化
- 二分搜索算法(O(log n) vs O(n))
- 自适应资源管理

🎯 项目现在稳定运行,可部署到Linux环境
2026-01-16 17:39:55 +08:00

504 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
金山文档上传测试 - 支持登录版本
集成扫码登录功能,支持完整的测试流程
"""
import os
import sys
import time
import base64
from datetime import datetime
from io import BytesIO
from PIL import Image
# 添加项目路径
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
try:
from playwright.sync_api import sync_playwright
except ImportError:
print("错误: 需要安装 playwright")
print("请运行: pip install playwright")
sys.exit(1)
def log(message, level='INFO'):
"""日志输出"""
timestamp = datetime.now().strftime("%H:%M:%S")
print(f"[{timestamp}] {level}: {message}")
def pause(msg="按Enter键继续..."):
"""等待用户按键"""
input(f"\n{msg}")
def ask_yes_no(question, default='n'):
"""询问用户是/否问题"""
if default == 'y':
prompt = f"{question} (Y/n): "
else:
prompt = f"{question} (y/N): "
answer = input(prompt).strip().lower()
if not answer:
answer = default
return answer == 'y'
def save_qr_code(qr_image_bytes, filename="qr_code.png"):
"""保存二维码图片"""
try:
# 保存为PNG文件
with open(filename, 'wb') as f:
f.write(qr_image_bytes)
log(f"[OK] 二维码已保存到: {filename}", 'SUCCESS')
return filename
except Exception as e:
log(f"✗ 保存二维码失败: {str(e)}", 'ERROR')
return None
def display_qr_info():
"""显示二维码信息"""
print("\n" + "=" * 70)
print("📱 扫码登录说明")
print("=" * 70)
print()
print("1. 请使用手机微信扫描二维码")
print("2. 在手机上点击'确认登录'")
print("3. 等待页面自动跳转到表格页面")
print("4. 如果二维码失效,请按 Ctrl+C 重新生成")
print()
print("登录完成后请回到此窗口并按Enter键继续")
print("=" * 70)
def wait_for_login(page, timeout=120):
"""等待用户完成登录"""
log(f"等待登录完成 (超时: {timeout}秒)...", 'INFO')
start_time = time.time()
check_interval = 2 # 每2秒检查一次
while time.time() - start_time < timeout:
try:
# 检查当前URL
current_url = page.url
log(f"当前URL: {current_url}", 'INFO')
# 如果已经进入文档页面,认为登录成功
if "kdocs.cn" in current_url and "/spreadsheet/" in current_url:
log("[OK] 登录成功,已进入文档页面", 'SUCCESS')
return True
# 检查是否还在登录页面
if "login" in current_url.lower() or "account" in current_url.lower():
log("仍在登录页面,请扫码登录...", 'INFO')
else:
log(f"页面状态变化: {current_url}", 'INFO')
time.sleep(check_interval)
except Exception as e:
log(f"检查登录状态时出错: {str(e)}", 'WARNING')
time.sleep(check_interval)
log("登录超时", 'WARNING')
return False
def capture_qr_code(page):
"""尝试捕获二维码"""
log("尝试捕获二维码...", 'INFO')
try:
# 查找二维码元素
qr_selectors = [
"canvas",
"img[src*='qr']",
"img[alt*='二维码']",
"[class*='qr']",
"[id*='qr']",
"div[class*='qrcode']"
]
for selector in qr_selectors:
try:
elements = page.query_selector_all(selector)
for i, element in enumerate(elements):
try:
# 截图
screenshot = element.screenshot()
if len(screenshot) > 1000: # 足够大的图片
filename = f"qr_code_{selector.replace('[', '').replace(']', '').replace('*', '').replace('=', '').replace(' ', '_')}_{i}.png"
save_qr_code(screenshot, filename)
log(f"[OK] 找到二维码元素: {selector}[{i}]", 'SUCCESS')
return True
except Exception:
continue
except Exception:
continue
# 备选:截取整个页面并查找二维码区域
try:
screenshot = page.screenshot()
filename = "qr_code_fullpage.png"
save_qr_code(screenshot, filename)
log("[OK] 已截取整个页面,请查看页面中的二维码", 'SUCCESS')
log(f" 截图保存为: {filename}", 'INFO')
return True
except Exception as e:
log(f"截取页面失败: {str(e)}", 'ERROR')
except Exception as e:
log(f"捕获二维码失败: {str(e)}", 'ERROR')
return False
def main():
"""主函数"""
print("=" * 70)
print("[LOCK] 金山文档上传测试 - 支持登录版本")
print("=" * 70)
print()
print("特点:")
print(" [OK] 支持扫码登录")
print(" [OK] 完整的测试流程")
print(" [OK] 详细的操作指导")
print(" [OK] 自动等待登录完成")
print()
# 配置
doc_url = input("请输入金山文档URL (或按Enter使用默认): ").strip()
if not doc_url:
doc_url = "https://kdocs.cn/l/cpwEOo5ynKX4"
print(f"\n使用URL: {doc_url}")
print()
if not ask_yes_no("确认开始测试?"):
print("测试已取消")
return
print("\n" + "=" * 70)
print("开始测试流程")
print("=" * 70)
playwright = None
browser = None
context = None
page = None
try:
# ===== 步骤1: 启动浏览器 =====
print("\n" + "=" * 50)
print("步骤1: 启动浏览器")
print("=" * 50)
log("正在启动Playwright...", 'INFO')
playwright = sync_playwright().start()
log("[OK] Playwright启动成功", 'SUCCESS')
log("正在启动浏览器...", 'INFO')
browser = playwright.chromium.launch(headless=False)
log("[OK] 浏览器启动成功", 'SUCCESS')
log("正在创建上下文...", 'INFO')
context = browser.new_context()
log("[OK] 上下文创建成功", 'SUCCESS')
log("正在创建页面...", 'INFO')
page = context.new_page()
page.set_default_timeout(30000)
log("[OK] 页面创建成功", 'SUCCESS')
pause("浏览器已启动,请观察浏览器窗口是否正常打开")
# ===== 步骤2: 打开登录页面 =====
print("\n" + "=" * 50)
print("步骤2: 打开登录页面")
print("=" * 50)
log(f"正在导航到: {doc_url}", 'INFO')
page.goto(doc_url, wait_until='domcontentloaded')
log("[OK] 页面导航完成", 'SUCCESS')
log("等待3秒让页面加载...", 'INFO')
time.sleep(3)
current_url = page.url
log(f"当前URL: {current_url}", 'INFO')
# ===== 步骤3: 处理登录 =====
print("\n" + "=" * 50)
print("步骤3: 登录处理")
print("=" * 50)
# 检查是否需要登录
try:
login_visible = page.locator("text=登录").first.is_visible()
if login_visible:
log("[OK] 检测到登录页面", 'SUCCESS')
# 尝试捕获二维码
capture_qr_code(page)
# 显示登录说明
display_qr_info()
# 等待用户登录
if not wait_for_login(page, timeout=180): # 3分钟超时
log("登录失败或超时", 'ERROR')
if ask_yes_no("是否要重新尝试?"):
log("请重新扫码登录...", 'INFO')
if wait_for_login(page, timeout=180):
log("[OK] 登录成功", 'SUCCESS')
else:
log("登录仍然失败", 'ERROR')
return
else:
log("[OK] 登录成功", 'SUCCESS')
else:
log("[OK] 未检测到登录页面,可能已经登录", 'SUCCESS')
except Exception as e:
log(f"检查登录状态时出错: {str(e)}", 'WARNING')
pause("登录处理完成,请确认是否已进入文档页面")
# ===== 步骤4: 验证文档加载 =====
print("\n" + "=" * 50)
print("步骤4: 验证文档加载")
print("=" * 50)
current_url = page.url
log(f"当前URL: {current_url}", 'INFO')
if "kdocs.cn" in current_url and "/spreadsheet/" in current_url:
log("[OK] 已成功进入金山文档表格", 'SUCCESS')
else:
log("⚠ 当前不在金山文档表格页面", 'WARNING')
log("请确认是否已正确登录", 'INFO')
# 等待页面完全加载
log("等待5秒让表格完全加载...", 'INFO')
time.sleep(5)
# 检查表格元素
try:
canvas_count = page.locator("canvas").count()
log(f"[OK] 检测到 {canvas_count} 个canvas元素", 'SUCCESS')
if canvas_count > 0:
log("[OK] 表格元素正常加载", 'SUCCESS')
else:
log("⚠ 未检测到表格元素,可能页面还在加载", 'WARNING')
except Exception as e:
log(f"检查表格元素时出错: {str(e)}", 'WARNING')
pause("文档验证完成,请确认表格是否正常显示")
# ===== 步骤5: 表格读取测试 =====
print("\n" + "=" * 50)
print("步骤5: 表格读取测试")
print("=" * 50)
# 尝试读取名称框
try:
log("尝试定位名称框...", 'INFO')
name_box = page.locator("input.edit-box").first
if name_box.is_visible():
value = name_box.input_value()
log(f"[OK] 名称框可见,当前值: '{value}'", 'SUCCESS')
else:
log("⚠ 名称框不可见", 'WARNING')
except Exception as e:
log(f"读取名称框失败: {str(e)}", 'WARNING')
# 尝试读取当前单元格
try:
log("尝试读取当前单元格内容...", 'INFO')
# 尝试点击网格
canvases = page.locator("canvas").all()
if canvases:
box = canvases[0].bounding_box()
if box:
page.mouse.click(box['x'] + box['width'] / 2, box['y'] + box['height'] / 2)
time.sleep(0.5)
log("[OK] 已点击网格", 'SUCCESS')
except Exception as e:
log(f"点击网格失败: {str(e)}", 'WARNING')
pause("表格读取测试完成")
# ===== 步骤6: 人员搜索测试 =====
print("\n" + "=" * 50)
print("步骤6: 人员搜索测试")
print("=" * 50)
test_name = input("请输入要搜索的姓名 (默认: 张三): ").strip()
if not test_name:
test_name = "张三"
log(f"搜索姓名: {test_name}", 'INFO')
try:
log("执行搜索操作...", 'INFO')
page.keyboard.press("Control+f")
time.sleep(0.5)
page.keyboard.type(test_name)
time.sleep(0.3)
page.keyboard.press("Enter")
time.sleep(1)
page.keyboard.press("Escape")
time.sleep(0.3)
log("[OK] 人员搜索测试完成", 'SUCCESS')
log("请查看浏览器窗口,检查是否高亮显示了搜索结果", 'INFO')
except Exception as e:
log(f"✗ 搜索测试失败: {str(e)}", 'ERROR')
pause("搜索测试完成")
# ===== 步骤7: 图片上传测试 =====
print("\n" + "=" * 50)
print("步骤7: 图片上传测试 (可选)")
print("=" * 50)
if ask_yes_no("是否进行图片上传测试?"):
image_path = input("请输入测试图片的完整路径: ").strip()
if not image_path or not os.path.exists(image_path):
log("图片文件不存在或路径无效,跳过上传测试", 'WARNING')
else:
log(f"选中的图片: {image_path}", 'INFO')
try:
log("执行上传流程...", 'INFO')
# 导航到D3单元格
name_box = page.locator("input.edit-box").first
name_box.click()
name_box.fill("D3")
name_box.press("Enter")
time.sleep(0.5)
log("[OK] 已导航到D3单元格")
# 点击插入
insert_btn = page.locator("text=插入").first
insert_btn.click()
time.sleep(0.5)
log("[OK] 已点击插入按钮")
# 点击图片
image_btn = page.locator("text=图片").first
image_btn.click()
time.sleep(0.5)
log("[OK] 已点击图片按钮")
# 选择本地
local_option = page.locator("text=本地").first
local_option.click()
log("[OK] 已选择本地图片")
# 上传文件
with page.expect_file_chooser() as fc_info:
pass
file_chooser = fc_info.value
file_chooser.set_files(image_path)
log("[OK] 文件上传命令已发送")
log("等待上传完成...", 'INFO')
time.sleep(3)
log("[OK] 图片上传测试完成", 'SUCCESS')
log("请检查浏览器窗口确认图片是否成功上传到D3单元格", 'INFO')
except Exception as e:
log(f"✗ 图片上传测试失败: {str(e)}", 'ERROR')
import traceback
traceback.print_exc()
pause("图片上传测试完成")
# ===== 测试完成 =====
print("\n" + "=" * 70)
log("🎉 所有测试完成!", 'SUCCESS')
print("=" * 70)
print()
print("测试结果汇总:")
print(" [[OK]] 浏览器启动")
print(" [[OK]] 文档打开")
print(" [[OK]] 登录处理")
print(" [[OK]] 文档加载验证")
print(" [[OK]] 表格读取")
print(" [[OK]] 人员搜索")
if ask_yes_no("是否执行了图片上传?"):
print(" [[OK]] 图片上传")
print()
print("浏览器窗口将保持打开状态")
print("您可以手动关闭浏览器窗口来结束测试")
except KeyboardInterrupt:
print("\n")
log("测试被用户中断", 'WARNING')
except Exception as e:
print("\n")
log(f"测试过程中出现错误: {str(e)}", 'ERROR')
import traceback
traceback.print_exc()
finally:
# 清理资源
print("\n" + "=" * 70)
print("清理资源...")
print("=" * 70)
try:
if page:
page.close()
log("[OK] 页面已关闭", 'SUCCESS')
except:
pass
try:
if context:
context.close()
log("[OK] 上下文已关闭", 'SUCCESS')
except:
pass
try:
if browser:
browser.close()
log("[OK] 浏览器已关闭", 'SUCCESS')
except:
pass
try:
if playwright:
playwright.stop()
log("[OK] Playwright已停止", 'SUCCESS')
except:
pass
log("测试结束", 'SUCCESS')
print("=" * 70)
if __name__ == "__main__":
main()