import platform
import shutil
import os
import re
import threading
import tkinter as tk
import urllib.request
import zipfile
import json
import time
try:
import winreg # Windows专用
except ImportError:
pass
import subprocess
from tkinter import filedialog, messagebox, ttk
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
from selenium.common.exceptions import (WebDriverException,
SessionNotCreatedException,
NoSuchElementException)
class WebScreenshotApp:
def __init__(self, root):
self.root = root
root.title("网页截图工具 v2.0")
root.geometry("800x600")
root.resizable(True, True)
# 创建主框架
main_frame = ttk.Frame(root, padding="20")
main_frame.pack(fill=tk.BOTH, expand=True)
# 创建输入区域
input_frame = ttk.LabelFrame(main_frame, text="截图参数", padding=10)
input_frame.pack(fill=tk.X, pady=(0, 10))
# URL输入
ttk.Label(input_frame, text="目标网址:").grid(row=0, column=0, sticky="w", padx=5, pady=5)
self.url_entry = ttk.Entry(input_frame, width=50)
self.url_entry.grid(row=0, column=1, columnspan=2, sticky="we", padx=5, pady=5)
self.url_entry.insert(0, "https://example.com")
# 登录凭证(可选)
auth_frame = ttk.Frame(input_frame)
auth_frame.grid(row=1, column=0, columnspan=3, sticky="we", pady=5)
ttk.Label(auth_frame, text="用户名:").pack(side=tk.LEFT, padx=(0, 5))
self.user_entry = ttk.Entry(auth_frame, width=20)
self.user_entry.pack(side=tk.LEFT, padx=(0, 10))
ttk.Label(auth_frame, text="密码:").pack(side=tk.LEFT)
self.pass_entry = ttk.Entry(auth_frame, width=20, show="*")
self.pass_entry.pack(side=tk.LEFT, padx=(0, 10))
# 关键词搜索(可选)
ttk.Label(input_frame, text="搜索关键词:").grid(row=2, column=0, sticky="w", padx=5, pady=5)
self.search_entry = ttk.Entry(input_frame, width=50)
self.search_entry.grid(row=2, column=1, columnspan=2, sticky="we", padx=5, pady=5)
# 驱动设置区域
driver_frame = ttk.LabelFrame(main_frame, text="Chrome驱动设置", padding=10)
driver_frame.pack(fill=tk.X, pady=(0, 10))
ttk.Label(driver_frame, text="驱动路径:").grid(row=0, column=0, sticky="w", padx=5, pady=5)
self.driver_entry = ttk.Entry(driver_frame, width=50)
self.driver_entry.grid(row=0, column=1, sticky="we", padx=5, pady=5)
ttk.Button(driver_frame, text="浏览...", width=10,
command=self.browse_driver).grid(row=0, column=2, padx=5)
ttk.Button(driver_frame, text="自动检测", width=10,
command=self.auto_detect_driver).grid(row=0, column=3, padx=5)
self.driver_version_var = tk.StringVar(value="未检测")
ttk.Label(driver_frame, text="驱动版本:").grid(row=1, column=0, sticky="w", padx=5)
ttk.Label(driver_frame, textvariable=self.driver_version_var).grid(row=1, column=1, sticky="w")
self.chrome_version_var = tk.StringVar(value="未检测")
ttk.Label(driver_frame, text="Chrome版本:").grid(row=1, column=2, sticky="w", padx=5)
ttk.Label(driver_frame, textvariable=self.chrome_version_var).grid(row=1, column=3, sticky="w")
ttk.Button(driver_frame, text="更新驱动",
command=self.update_driver).grid(row=2, column=3, padx=5, pady=5, sticky="e")
# 输出设置
output_frame = ttk.LabelFrame(main_frame, text="输出设置", padding=10)
output_frame.pack(fill=tk.X, pady=(0, 10))
ttk.Label(output_frame, text="保存目录:").grid(row=0, column=0, sticky="w", padx=5, pady=5)
self.folder_entry = ttk.Entry(output_frame, width=50)
self.folder_entry.grid(row=0, column=1, sticky="we", padx=5, pady=5)
ttk.Button(output_frame, text="选择文件夹...", width=10,
command=self.browse_folder).grid(row=0, column=2, padx=5)
# 文件名前缀
ttk.Label(output_frame, text="文件名前缀:").grid(row=1, column=0, sticky="w", padx=5, pady=5)
self.prefix_entry = ttk.Entry(output_frame, width=50)
self.prefix_entry.grid(row=1, column=1, sticky="we", padx=5, pady=5)
self.prefix_entry.insert(0, "screenshot")
# 状态栏
status_frame = ttk.Frame(main_frame)
status_frame.pack(fill=tk.X, pady=(10, 0))
self.status_var = tk.StringVar(value="就绪")
ttk.Label(status_frame, textvariable=self.status_var,
relief=tk.SUNKEN, anchor=tk.W).pack(side=tk.BOTTOM, fill=tk.X)
# 进度条
self.progress_var = tk.DoubleVar(value=0.0)
self.progress = ttk.Progressbar(status_frame, variable=self.progress_var, maximum=100)
self.progress.pack(side=tk.BOTTOM, fill=tk.X, pady=(0, 5))
# 操作按钮
button_frame = ttk.Frame(main_frame)
button_frame.pack(fill=tk.X, pady=(10, 0))
ttk.Button(button_frame, text="开始截图",
command=self.start_capture).pack(side=tk.RIGHT, padx=5)
ttk.Button(button_frame, text="退出",
command=root.destroy).pack(side=tk.RIGHT)
# 设置默认路径
self.set_default_paths()
self.auto_detect_driver()
def set_default_paths(self):
"""设置默认路径"""
# 默认保存目录:用户桌面
desktop_path = os.path.join(os.path.expanduser("~"), "Desktop")
self.folder_entry.delete(0, tk.END)
self.folder_entry.insert(0, os.path.join(desktop_path, "网页截图"))
# 尝试自动检测Chrome安装路径
chrome_path = self.detect_chrome_path()
if chrome_path:
driver_dir = os.path.dirname(chrome_path)
driver_path = os.path.join(driver_dir, "chromedriver.exe")
if os.path.exists(driver_path):
self.driver_entry.delete(0, tk.END)
self.driver_entry.insert(0, driver_path)
def detect_chrome_path(self):
"""自动检测Chrome安装路径"""
system = platform.system()
try:
if system == "Windows":
# 通过注册表查找
key_path = r"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe"
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key_path) as key:
path, _ = winreg.QueryValueEx(key, "")
return path
elif system == "Darwin": # macOS
return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
elif system == "Linux":
# 尝试常见安装路径
paths = [
"/usr/bin/google-chrome",
"/usr/bin/chromium-browser",
"/usr/bin/chromium",
"/snap/bin/chromium"
]
for path in paths:
if os.path.exists(path):
return path
except Exception:
pass
return None
def browse_folder(self):
"""浏览选择文件夹"""
folder = filedialog.askdirectory()
if folder:
self.folder_entry.delete(0, tk.END)
self.folder_entry.insert(0, folder)
def browse_driver(self):
"""浏览选择驱动文件"""
filetypes = [("可执行文件", "*.exe" if platform.system() == "Windows" else "*")]
driver_path = filedialog.askopenfilename(
title="选择ChromeDriver",
filetypes=filetypes
)
if driver_path:
self.driver_entry.delete(0, tk.END)
self.driver_entry.insert(0, driver_path)
self.check_driver_version()
def auto_detect_driver(self):
"""自动检测驱动路径"""
system = platform.system()
common_paths = []
# Windows常见路径
if system == "Windows":
common_paths = [
os.path.expandvars(r"%ProgramFiles%\Google\Chrome\Application\chromedriver.exe"),
os.path.expandvars(r"%ProgramFiles(x86)%\Google\Chrome\Application\chromedriver.exe"),
os.path.expandvars(r"%LOCALAPPDATA%\Google\Chrome\Application\chromedriver.exe")
]
# macOS常见路径
elif system == "Darwin":
common_paths = [
"/Applications/Google Chrome.app/Contents/MacOS/chromedriver",
"/usr/local/bin/chromedriver",
os.path.expanduser("~/bin/chromedriver")
]
# Linux常见路径
elif system == "Linux":
common_paths = [
"/usr/bin/chromedriver",
"/usr/local/bin/chromedriver",
"/snap/bin/chromedriver",
os.path.expanduser("~/bin/chromedriver")
]
# 检查常见路径
for path in common_paths:
if os.path.exists(path):
self.driver_entry.delete(0, tk.END)
self.driver_entry.insert(0, path)
self.check_driver_version()
return
# 尝试在PATH环境变量中查找
try:
path = subprocess.check_output(["which", "chromedriver"], text=True).strip()
if os.path.exists(path):
self.driver_entry.delete(0, tk.END)
self.driver_entry.insert(0, path)
self.check_driver_version()
return
except Exception:
pass
messagebox.showinfo("提示", "未找到ChromeDriver, 请手动选择路径或点击'更新驱动'")
def check_driver_version(self):
"""检查驱动和浏览器版本"""
driver_path = self.driver_entry.get().strip()
if not os.path.exists(driver_path):
self.driver_version_var.set("无效路径")
self.chrome_version_var.set("未检测")
return
try:
# 获取驱动版本
result = subprocess.run(
[driver_path, "--version"],
capture_output=True,
text=True,
check=True
)
driver_version = re.search(r"(\d+\.\d+\.\d+\.\d+)", result.stdout)
if driver_version:
self.driver_version_var.set(driver_version.group(1))
# 获取Chrome浏览器版本
chrome_path = self.detect_chrome_path()
if chrome_path:
result = subprocess.run(
[chrome_path, "--version"],
capture_output=True,
text=True,
check=True
)
chrome_version = re.search(r"(\d+\.\d+\.\d+\.\d+)", result.stdout)
if chrome_version:
self.chrome_version_var.set(chrome_version.group(1))
except Exception as e:
self.driver_version_var.set("检测失败")
self.chrome_version_var.set("检测失败")
def update_driver(self):
"""更新驱动版本"""
# 获取当前Chrome主版本
chrome_version = self.chrome_version_var.get()
if "未检测" in chrome_version or "失败" in chrome_version:
messagebox.showerror("错误", "请先检测到有效的Chrome版本")
return
major_version = chrome_version.split(".")[0]
threading.Thread(
target=self.download_and_update_driver,
args=(major_version,),
daemon=True
).start()
def start_capture(self):
"""启动截图流程"""
# 验证输入
url = self.url_entry.get().strip()
if not url:
messagebox.showerror("错误", "请输入有效的网址")
return
driver_path = self.driver_entry.get().strip()
if not os.path.exists(driver_path):
messagebox.showerror("错误", "ChromeDriver路径无效")
return
# 在后台线程中执行截图
threading.Thread(
target=self.capture_page,
args=(url, driver_path),
daemon=True
).start()
def secure_capture(url):
options = Options()
# 关键修复配置
options.add_argument("--headless=new")
options.add_argument("--disable-gpu")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--disable-software-rasterizer")
# 防止反爬虫检测
options.add_argument("--disable-blink-features=AutomationControlled")
options.add_experimental_option("excludeSwitches", ["enable-automation"])
# 模拟真实浏览器
options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36")
driver = webdriver.Chrome(options=options)
try:
driver.get(url)
# 双重等待机制
WebDriverWait(driver, 15).until(
EC.presence_of_element_located((By.XPATH, "//*[text()]"))
)
WebDriverWait(driver, 5).until(
lambda d: d.execute_script("return document.readyState === 'complete'")
)
# 强制渲染动态内容
driver.execute_script("""
document.body.style.overflow = 'visible';
document.documentElement.style.height = '100%';
if(typeof echarts !== 'undefined') {
echarts.getInstanceByDom(document.querySelector('.echarts')).resize();
}
""")
# 滚动触发渲染
driver.execute_script("window.scrollBy(0, 500)")
time.sleep(0.3)
# 截图前验证
if driver.execute_script("return document.body.clientHeight") < 100:
driver.execute_script("document.body.style.minHeight = window.innerHeight + 'px'")
# 使用PNG格式保存
return driver.get_screenshot_as_png()
finally:
driver.quit()
def capture_page(self, url, driver_path):
"""执行实际截图操作"""
try:
self.status_var.set("正在初始化浏览器...")
self.progress_var.set(10)
# 配置浏览器选项
options = Options()
options.add_argument("--headless=new") # 无头模式
options.add_argument("--disable-gpu")
options.add_argument("--window-size=1920,1080")
# 初始化WebDriver
service = Service(executable_path=driver_path)
driver = webdriver.Chrome(service=service, options=options)
# 登录逻辑(如果有凭证)
username = self.user_entry.get().strip()
password = self.pass_entry.get().strip()
if username and password:
self.status_var.set("正在登录...")
self.progress_var.set(30)
driver.get(url)
time.sleep(2) # 等待页面加载
# 尝试查找登录表单(实际实现需根据目标网站调整)
try:
driver.find_element(By.ID, "username").send_keys(username)
driver.find_element(By.ID, "password").send_keys(password)
driver.find_element(By.ID, "login-btn").click()
time.sleep(3) # 等待登录完成
except NoSuchElementException:
self.status_var.set("警告:未找到登录表单")
# 搜索逻辑(如果有关键词)
search_term = self.search_entry.get().strip()
if search_term:
self.status_var.set("正在执行搜索...")
self.progress_var.set(50)
try:
search_box = driver.find_element(By.NAME, "q")
search_box.send_keys(search_term)
search_box.send_keys(Keys.RETURN)
time.sleep(2) # 等待搜索结果
except NoSuchElementException:
self.status_var.set("警告:未找到搜索框")
# 执行截图
self.status_var.set("正在截图...")
self.progress_var.set(80)
timestamp = time.strftime("%Y%m%d-%H%M%S")
prefix = self.prefix_entry.get().strip() or "screenshot"
filename = f"{prefix}_{timestamp}.png"
save_path = os.path.join(self.folder_entry.get(), filename)
os.makedirs(os.path.dirname(save_path), exist_ok=True)
driver.save_screenshot(save_path)
self.progress_var.set(100)
self.status_var.set(f"截图成功: {os.path.basename(save_path)}")
except SessionNotCreatedException as e:
self.status_var.set(f"驱动版本错误: {str(e)}")
except WebDriverException as e:
self.status_var.set(f"浏览器错误: {str(e)}")
finally:
try:
driver.quit()
except:
pass
# 主程序入口
if __name__ == "__main__":
root = tk.Tk()
app = WebScreenshotApp(root)
root.mainloop()
这样的代码正确吗