当前位置:首页>python>别再手动操作了!用Python自动化搞定一切重复工作,效率提升10倍

别再手动操作了!用Python自动化搞定一切重复工作,效率提升10倍

  • 2026-06-30 10:46:15
别再手动操作了!用Python自动化搞定一切重复工作,效率提升10倍

别再手动操作了!用Python自动化搞定一切重复工作,效率提升10倍

你是不是经常遇到这些情况?

  • 每天要填几十份一模一样的表格,手都点麻了
  • 需要定时截图记录工作过程,眼睛盯着屏幕不敢离开
  • 重复测试软件功能,一个按钮点了上百遍
  • 批量录入数据,眼睛一行行盯着Excel核对 如果你点头了,那这篇文章就是为你准备的。

今天分享一个我用了5年的Python库—— PyAutoGUI ,它能帮你自动控制鼠标和键盘,把那些机械重复的操作全部交给程序去做。

一、PyAutoGUI 简介

PyAutoGUI 是一个用于自动化控制鼠标和键盘的 Python 库,可以模拟用户的操作,实现 GUI 自动化任务。它支持 Windows、macOS 和 Linux 系统,是自动化测试、重复性任务处理的绝佳工具。

安装方法

pip install pyautogui

可选依赖

  • opencv-python:支持 confidence 参数进行图像识别
  • pillow:图像处理(PyAutoGUI 会自动安装)
  • screeninfo:多显示器支持

基础配置

import pyautogui

# 设置操作间隔(秒),防止操作过快
pyautogui.PAUSE = 1

# 设置失败安全功能,移动鼠标到左上角可中断脚本(默认开启)
pyautogui.FAILSAFE = True

# 获取屏幕分辨率(仅主显示器)
screenWidth, screenHeight = pyautogui.size()
print(f"屏幕分辨率: {screenWidth} x {screenHeight}")

二、入门教程

2.1 鼠标控制

import pyautogui

# 获取当前鼠标位置
x, y = pyautogui.position()
print(f"当前鼠标位置: ({x}{y})")

# 移动鼠标到指定位置(绝对坐标)
pyautogui.moveTo(100200, duration=0.5)

# 相对移动(从当前位置偏移)
pyautogui.move(50-30, duration=0.3)

# 单击
pyautogui.click()

# 双击
pyautogui.doubleClick()

# 右键单击
pyautogui.rightClick()

# 鼠标拖拽(绝对坐标)
pyautogui.dragTo(300400, duration=0.5, button='left')

# 鼠标拖拽(相对坐标)
pyautogui.drag(500, duration=0.2, button='left')

# 滚轮滚动(正数向上,负数向下)
pyautogui.scroll(10)   # 向上滚动
pyautogui.scroll(-10)  # 向下滚动

注意:鼠标移动的 duration 如果小于 pyautogui.MINIMUM_DURATION(默认 0.1 秒),移动会是瞬时的。

2.2 键盘控制

import pyautogui

# 输入文本(支持 interval 参数设置按键间隔)
pyautogui.write('Hello, PyAutoGUI!')
pyautogui.write('Hello', interval=0.25)  # 每个按键间隔 0.25 秒

# 按下和释放键
pyautogui.keyDown('shift')
pyautogui.press('a')
pyautogui.keyUp('shift')

# 组合键(hotkey 会自动处理按下和释放顺序)
pyautogui.hotkey('ctrl''c')  # 复制
pyautogui.hotkey('ctrl''v')  # 粘贴

# 特殊按键
pyautogui.press('enter')
pyautogui.press('tab')
pyautogui.press('backspace')
pyautogui.press('escape')

# 按住某个键执行多个操作
with pyautogui.hold('shift'):
    pyautogui.press(['left''left''left'])  # 选中左侧3个字符

说明pyautogui.typewrite() 是旧版函数名,新版本推荐使用 pyautogui.write(),两者功能相同。

2.3 屏幕截图

import pyautogui

# 截取整个屏幕,返回 PIL Image 对象
screenshot = pyautogui.screenshot()
screenshot.save('full_screen.png')

# 截取指定区域 (left, top, width, height)
screenshot = pyautogui.screenshot(region=(00300400))
screenshot.save('region.png')

# 截取并同时保存到文件
pyautogui.screenshot('my_screenshot.png')

# 查找图像位置
# 注意:PyAutoGUI 0.9.41+ 版本中,找不到图像会抛出 ImageNotFoundException
try:
    location = pyautogui.locateOnScreen('button.png')
    x, y = pyautogui.center(location)
    pyautogui.click(x, y)
except pyautogui.ImageNotFoundException:
    print("未找到目标图像")

# 使用置信度匹配(需要安装 opencv-python)
# confidence 越低,匹配越宽松
# 注意:未安装 OpenCV 时使用 confidence 会抛出 NotImplementedError
try:
    location = pyautogui.locateOnScreen('button.png', confidence=0.8)
    print(f"找到图像,位置: {location}")
except pyautogui.ImageNotFoundException:
    print("未找到目标图像")
except NotImplementedError:
    print("未安装 OpenCV,confidence 参数不可用")
    print("请运行: pip install opencv-python")

三、有趣案例

案例1:自动画画

import pyautogui
import time

# 打开画图软件(Windows)
pyautogui.hotkey('win''r')
pyautogui.write('mspaint')
pyautogui.press('enter')
time.sleep(2)

# 点击画布区域准备绘画
pyautogui.click(300300)

# 画一个正方形螺旋
distance = 200
while distance > 0:
    pyautogui.drag(distance, 0, duration=0.2)   # 向右
    distance -= 5
    pyautogui.drag(0, distance, duration=0.2)   # 向下
    pyautogui.drag(-distance, 0, duration=0.2)  # 向左
    distance -= 5
    pyautogui.drag(0, -distance, duration=0.2)  # 向上

案例2:鼠标追逐游戏

import pyautogui
import random
import time

screenWidth, screenHeight = pyautogui.size()

print("移动鼠标追逐目标!按 Ctrl+C 退出")

try:
whileTrue:
# 随机生成目标位置
        targetX = random.randint(50, screenWidth - 50)
        targetY = random.randint(50, screenHeight - 50)

# 获取当前鼠标位置
        currentX, currentY = pyautogui.position()

# 计算距离
        distance = ((targetX - currentX) ** 2 + (targetY - currentY) ** 2) ** 0.5

if distance < 30:
            print(f"抓到了!目标位置: ({targetX}{targetY})")
            time.sleep(1)
else:
            print(f"距离目标: {int(distance)} 像素", end='\r')

        time.sleep(0.1)
except KeyboardInterrupt:
    print("\n游戏结束")

案例3:自动玩Flappy Bird

import pyautogui
import time

print("3秒后开始...请切换到游戏窗口")
time.sleep(3)

# 游戏主循环
try:
whileTrue:
# 截取游戏区域(根据实际游戏位置调整坐标)
# 只截取需要检测的区域,提高效率
        screenshot = pyautogui.screenshot(region=(400300100150))

# 检测小鸟前方是否有障碍物
        found_obstacle = False
        width, height = screenshot.size

for x in range(width):
for y in range(height):
# 检测深色像素(障碍物,根据实际游戏颜色调整)
                pixel = screenshot.getpixel((x, y))
# 判断是否为深色
if pixel[0] < 50and pixel[1] < 50and pixel[2] < 50:
                    found_obstacle = True
break
if found_obstacle:
break

if found_obstacle:
            pyautogui.press('space')  # 跳跃
            time.sleep(0.3)  # 跳跃后等待一段时间

        time.sleep(0.05)
except KeyboardInterrupt:
    print("游戏结束")

说明:需要根据实际游戏窗口位置调整 region 参数和颜色检测阈值。


四、实用案例

案例1:自动填写表单

import pyautogui
import time

deffill_form(data):
# 点击姓名输入框
    pyautogui.click(200150)
    pyautogui.write(data['name'])

# 按 Tab 切换到邮箱输入框
    pyautogui.press('tab')
    pyautogui.write(data['email'])

# 按 Tab 切换到电话输入框
    pyautogui.press('tab')
    pyautogui.write(data['phone'])

# 按 Tab 切换到提交按钮,回车提交
    pyautogui.press('tab')
    pyautogui.press('enter')

# 示例数据
form_data = {
'name''张三',
'email''zhangsan@example.com',
'phone''13800138000'
}

print("请在3秒内切换到表单页面...")
time.sleep(3)
fill_form(form_data)

提示:对于中文输入,建议使用 pyperclip 复制粘贴,因为 write() 只支持英文输入。

案例2:定时自动截图工具

自动定时截取屏幕,按日期整理保存到文件夹,可用于记录工作过程、监控屏幕变化等。

import pyautogui
import os
import time
from datetime import datetime

classAutoScreenshot:
"""定时自动截图工具"""

def__init__(self, save_dir='~/Pictures/AutoScreenshots', interval=60, max_screenshots=None):
"""
        初始化截图工具

        :param save_dir: 截图保存目录
        :param interval: 截图间隔(秒)
        :param max_screenshots: 最大截图数量,None 表示不限制
        """

        self.save_dir = os.path.expanduser(save_dir)
        self.interval = interval
        self.max_screenshots = max_screenshots
        self.screenshot_count = 0
        self.running = False

# 创建保存目录
        os.makedirs(self.save_dir, exist_ok=True)

defget_today_folder(self):
"""获取当天的文件夹路径,不存在则创建"""
        today = datetime.now().strftime('%Y-%m-%d')
        folder = os.path.join(self.save_dir, today)
        os.makedirs(folder, exist_ok=True)
return folder

deftake_screenshot(self, region=None):
"""
        截取屏幕并保存

        :param region: 截图区域 (left, top, width, height),None 表示全屏
        :return: 保存的文件路径
        """

# 生成文件名:时分秒.png
        timestamp = datetime.now().strftime('%H-%M-%S')
        folder = self.get_today_folder()
        filename = f"{timestamp}.png"
        filepath = os.path.join(folder, filename)

# 截取屏幕
if region:
            screenshot = pyautogui.screenshot(region=region)
else:
            screenshot = pyautogui.screenshot()

# 保存图片
        screenshot.save(filepath)
        self.screenshot_count += 1

        print(f"[{self.screenshot_count}] 已保存: {filepath}")
return filepath

defcompare_screenshots(self, img1_path, img2_path, threshold=10):
"""
        比较两张截图的差异

        :param img1_path: 第一张图片路径
        :param img2_path: 第二张图片路径
        :param threshold: 差异阈值(0-100,值越小越敏感)
        :return: 是否有显著差异
        """

try:
from PIL import Image
import numpy as np

            img1 = Image.open(img1_path).convert('L')
            img2 = Image.open(img2_path).convert('L')

# 调整为相同大小
            img2 = img2.resize(img1.size)

            arr1 = np.array(img1)
            arr2 = np.array(img2)

# 计算差异百分比
            diff = np.abs(arr1 - arr2)
            diff_percent = (diff > 30).mean() * 100

return diff_percent > threshold
except ImportError:
            print("提示:安装 numpy 和 Pillow 可启用差异检测")
returnTrue

defstart(self, region=None, detect_change=False):
"""
        开始定时截图

        :param region: 截图区域,None 表示全屏
        :param detect_change: 是否只在屏幕变化时才保存(需要 numpy + Pillow)
        """

        print(f"自动截图工具已启动")
        print(f"保存目录: {self.save_dir}")
        print(f"截图间隔: {self.interval} 秒")
if self.max_screenshots:
            print(f"最大截图数: {self.max_screenshots}")
        print(f"按 Ctrl+C 停止\n")

        self.running = True
        last_screenshot_path = None

try:
while self.running:
# 检查是否达到最大截图数量
if self.max_screenshots and self.screenshot_count >= self.max_screenshots:
                    print(f"已达到最大截图数量 {self.max_screenshots},停止")
break

# 截图
                current_path = self.take_screenshot(region=region)

# 如果开启了变化检测且不是第一张
if detect_change and last_screenshot_path:
                    has_changed = self.compare_screenshots(
                        last_screenshot_path, current_path
                    )
ifnot has_changed:
# 没有变化就删除当前截图
                        os.remove(current_path)
                        self.screenshot_count -= 1
                        print("屏幕无变化,跳过保存")
else:
                        last_screenshot_path = current_path
else:
                    last_screenshot_path = current_path

# 等待下一次截图
                time.sleep(self.interval)

except KeyboardInterrupt:
            print(f"\n用户停止,共保存 {self.screenshot_count} 张截图")
finally:
            self.running = False

# 使用示例
if __name__ == '__main__':
# 创建截图工具:每60秒截一次,最多截100张
    screener = AutoScreenshot(
        save_dir='~/Pictures/AutoScreenshots',
        interval=60,
        max_screenshots=100
    )

# 开始截图(全屏)
    screener.start()

# 只截取指定区域
# screener.start(region=(0, 0, 800, 600))

# 只在屏幕变化时保存(节省空间)
# screener.start(detect_change=True)

使用场景

  • 记录工作过程,方便回顾
  • 监控屏幕变化(如软件安装进度、数据刷新)
  • 定时保存重要信息页面
  • 远程办公时记录工作时长

案例3:自动化数据批量录入工具

从 Excel/CSV 表格读取数据,自动填入表单系统(如OA、CRM、内部管理系统等)。

import pyautogui
import pandas as pd
import time
import os
from datetime import datetime

classDataEntryAutomation:
"""自动化数据批量录入工具"""

def__init__(self, data_file, form_config):
"""
        初始化数据录入工具

        :param data_file: 数据源文件路径(Excel或CSV)
        :param form_config: 表单配置字典
        """

        self.data_file = data_file
        self.form_config = form_config
        self.data = None
        self.success_count = 0
        self.fail_count = 0

# 检测 OpenCV 支持
try:
import cv2
            self.has_opencv = True
except ImportError:
            self.has_opencv = False
            print("提示:未安装 OpenCV,confidence 参数不可用")

# 加载数据
        self._load_data()

def_load_data(self):
"""加载数据文件"""
ifnot os.path.exists(self.data_file):
raise FileNotFoundError(f"数据文件不存在: {self.data_file}")

        ext = os.path.splitext(self.data_file)[1].lower()
if ext == '.csv':
            self.data = pd.read_csv(self.data_file)
elif ext in ['.xlsx''.xls']:
            self.data = pd.read_excel(self.data_file)
else:
raise ValueError(f"不支持的文件格式: {ext}")

        print(f"成功加载 {len(self.data)} 条数据")

def_safe_click(self, x, y, duration=0.5):
"""安全点击,带失败重试"""
        pyautogui.moveTo(x, y, duration=duration)
        pyautogui.click()

def_safe_input(self, text, interval=0.05):
"""安全输入文本"""
# 清除现有内容
        pyautogui.hotkey('ctrl''a')
        pyautogui.write(str(text), interval=interval)

def_find_and_click_image(self, image_path, confidence=0.8, timeout=10):
"""
        查找图像并点击(带超时和降级处理)

        :param image_path: 图像路径
        :param confidence: 匹配置信度
        :param timeout: 超时时间(秒)
        :return: 是否成功
        """

        start_time = time.time()
while time.time() - start_time < timeout:
try:
if self.has_opencv and confidence:
                    location = pyautogui.locateOnScreen(image_path, confidence=confidence)
else:
                    location = pyautogui.locateOnScreen(image_path)

if location:
                    x, y = pyautogui.center(location)
                    pyautogui.click(x, y)
returnTrue
except pyautogui.ImageNotFoundException:
                time.sleep(0.5)
except NotImplementedError:
# OpenCV 不可用,降级为精确匹配
try:
                    location = pyautogui.locateOnScreen(image_path)
if location:
                        x, y = pyautogui.center(location)
                        pyautogui.click(x, y)
returnTrue
except pyautogui.ImageNotFoundException:
                    time.sleep(0.5)

returnFalse

def_wait_for_element(self, image_path, timeout=30):
"""等待元素出现"""
return self._find_and_click_image(image_path, timeout=timeout)

def_fill_single_record(self, row):
"""
        填写单条记录

        :param row: pandas Series 行数据
        :return: 是否成功
        """

try:
# 遍历表单配置,填写每个字段
for field_name, config in self.form_config.items():
                field_value = row.get(field_name, '')

if config['type'] == 'input':
# 输入框
                    x, y = config['position']
                    self._safe_click(x, y)
                    time.sleep(0.2)
                    self._safe_input(field_value, interval=0.05)
                    time.sleep(0.1)

elif config['type'] == 'dropdown':
# 下拉选择
                    x, y = config['position']
                    self._safe_click(x, y)
                    time.sleep(0.3)
# 查找选项
                    option_index = config['options'].get(str(field_value), 0)
# 根据实际情况调整按键
for _ in range(option_index):
                        pyautogui.press('down')
                    pyautogui.press('enter')
                    time.sleep(0.1)

elif config['type'] == 'checkbox':
# 复选框
if str(field_value).lower() in ['true''1''yes''是']:
                        x, y = config['position']
                        self._safe_click(x, y)
                        time.sleep(0.1)

elif config['type'] == 'button':
# 按钮点击
if config.get('before_fill'):
# 点击按钮打开输入框
                        self._find_and_click_image(config['image'])
                        time.sleep(0.5)

# 提交按钮
if'submit'in self.form_config:
                time.sleep(0.5)
                submit_config = self.form_config['submit']
if submit_config.get('type') == 'image':
                    self._find_and_click_image(submit_config['image'])
else:
                    x, y = submit_config['position']
                    self._safe_click(x, y)

            time.sleep(1)
returnTrue

except Exception as e:
            print(f"填写失败: {e}")
returnFalse

defrun(self, start_row=0, end_row=None, pause_between=2):
"""
        执行批量录入

        :param start_row: 起始行(0索引)
        :param end_row: 结束行,None 表示到最后
        :param pause_between: 每条记录间隔(秒)
        """

        total = len(self.data) if end_row isNoneelse min(end_row, len(self.data))
        print(f"\n开始批量录入,第 {start_row + 1} - {total} 条记录")
        print("按 Ctrl+C 可随时停止\n")

try:
for i in range(start_row, total):
                row = self.data.iloc[i]
                print(f"[{i + 1}/{total}] 正在录入: {row.get('name', row.to_dict())}")

if self._fill_single_record(row):
                    self.success_count += 1
                    print(f"  ✓ 成功")
else:
                    self.fail_count += 1
                    print(f"  ✗ 失败")

if i < total - 1:  # 最后一条不需要等待
                    time.sleep(pause_between)

except KeyboardInterrupt:
            print("\n用户中断录入")

        print(f"\n录入完成!成功: {self.success_count}, 失败: {self.fail_count}")
return self.success_count, self.fail_count


# ==================== 使用示例 ====================

if __name__ == '__main__':
# 方式1:使用图像识别定位元素
    form_config_with_image = {
'username': {
'type''input',
'image''username_field.png',  # 输入框截图
'position'None# 使用图像定位时不需要 position
        },
'department': {
'type''dropdown',
'image''dept_dropdown.png',
'options': {'销售部'0'技术部'1'财务部'2}
        },
'submit': {
'type''image',
'image''submit_button.png'
        }
    }

# 方式2:使用坐标定位元素(更快但需要先测量坐标)
    form_config_with_coords = {
'name': {
'type''input',
'position': (400200)  # (x, y) 坐标
        },
'phone': {
'type''input',
'position': (400250)
        },
'email': {
'type''input',
'position': (400300)
        },
'agree_terms': {
'type''checkbox',
'position': (400350)  # 复选框坐标
        },
'submit': {
'type''input',
'position': (500400)  # 提交按钮坐标
        }
    }

# 创建自动化工具
    print("=" * 50)
    print("自动化数据批量录入工具")
    print("=" * 50)

# 数据文件路径
    data_file = '员工信息表.xlsx'# 替换为实际路径

# 表单配置
    form_config = form_config_with_coords  # 或使用 form_config_with_image

try:
        automation = DataEntryAutomation(data_file, form_config)

# 执行录入:跳过前3行,从第4行开始,录入10条
# automation.run(start_row=3, end_row=13, pause_between=3)

# 或录入全部
        automation.run(pause_between=2)

except FileNotFoundError as e:
        print(f"错误: {e}")
        print("\n请确保:")
        print("1. 数据文件存在")
        print("2. 已准备好要填写的表单页面")
        print("3. 根据实际情况调整 form_config 中的坐标或图像")

使用流程

  1. 准备数据文件(Excel/CSV)
  2. 配置表单字段映射(坐标或图像)
  3. 手动打开目标表单页面
  4. 运行脚本开始自动录入

配置说明

  • type: 字段类型(input/dropdown/checkbox/button)
  • position: 元素坐标 (x, y),使用 pyautogui.position() 获取
  • image: 元素截图,用于图像识别定位
  • options: 下拉选项映射(显示文本 -> 按键次数)

五、进阶案例

案例1:自动化测试框架

一个完整的自动化测试框架,支持测试用例管理、报告生成、图像识别定位和错误处理。

import pyautogui
import unittest
import time
import os
import json
import logging
from datetime import datetime
from pathlib import Path

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('auto_test.log'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

classAutoTestFramework:
"""自动化测试框架"""

def__init__(self, config_file='test_config.json'):
        self.config = self._load_config(config_file)
        self.results = []
        self.screenshot_dir = Path('test_screenshots')
        self.screenshot_dir.mkdir(exist_ok=True)

# 检测 OpenCV 支持
try:
import cv2
            self.has_opencv = True
            logger.info("OpenCV 已安装,支持 confidence 参数")
except ImportError:
            self.has_opencv = False
            logger.warning("OpenCV 未安装,confidence 参数不可用")

def_load_config(self, config_file):
"""加载测试配置文件"""
if os.path.exists(config_file):
with open(config_file, 'r', encoding='utf-8'as f:
return json.load(f)
return {
'app_path''',
'test_cases': [],
'default_timeout'30,
'screenshot_on_error'True
        }

def_find_element(self, locator, timeout=10):
"""
        查找元素(支持坐标和图像两种方式)

        :param locator: 定位器,格式:{'type': 'coords/image', 'value': (x,y)/path}
        :param timeout: 超时时间
        :return: 元素位置或 None
        """

        start_time = time.time()

if locator['type'] == 'coords':
return locator['value']  # 直接返回坐标

# 图像识别定位
        image_path = locator['value']
        confidence = locator.get('confidence'0.8)

while time.time() - start_time < timeout:
try:
if self.has_opencv:
                    location = pyautogui.locateOnScreen(
                        image_path, 
                        confidence=confidence,
                        grayscale=locator.get('grayscale'True)
                    )
else:
                    location = pyautogui.locateOnScreen(image_path)

if location:
return pyautogui.center(location)
except pyautogui.ImageNotFoundException:
                time.sleep(0.5)
except NotImplementedError:
# 降级到精确匹配
try:
                    location = pyautogui.locateOnScreen(image_path)
if location:
return pyautogui.center(location)
except pyautogui.ImageNotFoundException:
                    time.sleep(0.5)

returnNone

def_click(self, locator, timeout=10):
"""点击元素"""
        pos = self._find_element(locator, timeout)
if pos:
            pyautogui.click(pos[0], pos[1])
            logger.debug(f"点击成功: {locator}")
returnTrue
        logger.error(f"未找到元素: {locator}")
returnFalse

def_type(self, locator, text, interval=0.05, timeout=10):
"""输入文本"""
if self._click(locator, timeout):
            time.sleep(0.2)
            pyautogui.hotkey('ctrl''a')
            pyautogui.write(str(text), interval=interval)
            logger.debug(f"输入成功: {text}")
returnTrue
returnFalse

def_assert_image_exists(self, image_path, timeout=10, confidence=0.8):
"""断言图像存在"""
        locator = {
'type''image',
'value': image_path,
'confidence': confidence
        }
return self._find_element(locator, timeout) isnotNone

def_take_screenshot(self, name):
"""截取测试截图"""
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        filename = f"{timestamp}_{name}.png"
        filepath = self.screenshot_dir / filename
        pyautogui.screenshot(str(filepath))
return str(filepath)

defrun_test_case(self, test_case):
"""
        运行单个测试用例

        :param test_case: 测试用例字典
        :return: {'name': str, 'status': 'PASS/FAIL', 'error': str, 'screenshot': str}
        """

        result = {
'name': test_case['name'],
'status''PASS',
'error''',
'screenshot''',
'start_time': datetime.now().strftime('%H:%M:%S')
        }

        logger.info(f"开始测试: {test_case['name']}")

try:
# 执行测试步骤
for step in test_case['steps']:
                action = step['action']

if action == 'click':
ifnot self._click(step['locator'], step.get('timeout'10)):
raise Exception(f"点击失败: {step['locator']}")

elif action == 'type':
ifnot self._type(
                        step['locator'], 
                        step['text'], 
                        step.get('interval'0.05),
                        step.get('timeout'10)
                    ):
raise Exception(f"输入失败: {step['locator']}")

elif action == 'wait':
                    time.sleep(step.get('seconds'1))

elif action == 'hotkey':
                    pyautogui.hotkey(*step['keys'])

elif action == 'assert_image':
ifnot self._assert_image_exists(
                        step['image'], 
                        step.get('timeout'10),
                        step.get('confidence'0.8)
                    ):
raise Exception(f"断言失败: 未找到图像 {step['image']}")

elif action == 'assert_position':
                    x, y = pyautogui.position()
                    expected_x, expected_y = step['position']
                    tolerance = step.get('tolerance'10)
if abs(x - expected_x) > tolerance or abs(y - expected_y) > tolerance:
raise Exception(f"位置断言失败: 期望 ({expected_x},{expected_y}), 实际 ({x},{y})")

# 步骤间等待
                time.sleep(step.get('delay'0.3))

            logger.info(f"测试通过: {test_case['name']}")

except Exception as e:
            result['status'] = 'FAIL'
            result['error'] = str(e)
if self.config.get('screenshot_on_error'True):
                result['screenshot'] = self._take_screenshot(test_case['name'])
            logger.error(f"测试失败: {test_case['name']} - {e}")

        result['end_time'] = datetime.now().strftime('%H:%M:%S')
        self.results.append(result)
return result

defrun_all_tests(self):
"""运行所有测试用例"""
        logger.info("=" * 60)
        logger.info("开始执行自动化测试套件")
        logger.info("=" * 60)

        start_time = datetime.now()

for test_case in self.config.get('test_cases', []):
            self.run_test_case(test_case)

        end_time = datetime.now()
        duration = (end_time - start_time).total_seconds()

        self.generate_report(duration)
return self.results

defgenerate_report(self, duration):
"""生成测试报告"""
        report = {
'summary': {
'total': len(self.results),
'passed': sum(1for r in self.results if r['status'] == 'PASS'),
'failed': sum(1for r in self.results if r['status'] == 'FAIL'),
'duration'f"{duration:.2f}秒",
'run_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            },
'details': self.results
        }

# 生成 JSON 报告
        report_file = f"test_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
with open(report_file, 'w', encoding='utf-8'as f:
            json.dump(report, f, ensure_ascii=False, indent=2)

# 生成文本报告
        text_report = f"""
测试报告
========

执行时间: {report['summary']['run_time']}
总用时: {report['summary']['duration']}

测试统计
--------
总用例: {report['summary']['total']}
通过: {report['summary']['passed']}
失败: {report['summary']['failed']}
通过率: {report['summary']['passed']/report['summary']['total']*100:.1f}%

测试详情
--------
"""

for idx, result in enumerate(self.results, 1):
            status_icon = '✓'if result['status'] == 'PASS'else'✗'
            text_report += f"{idx}. [{status_icon}{result['name']}\n"
if result['status'] == 'FAIL':
                text_report += f"   错误: {result['error']}\n"
if result['screenshot']:
                    text_report += f"   截图: {result['screenshot']}\n"

        text_report_file = f"test_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
with open(text_report_file, 'w', encoding='utf-8'as f:
            f.write(text_report)

        logger.info("=" * 60)
        logger.info("测试报告已生成")
        logger.info(f"JSON 报告: {report_file}")
        logger.info(f"文本报告: {text_report_file}")
        logger.info("=" * 60)

# 打印摘要
        print(text_report)


# ==================== 测试用例配置示例 ====================
TEST_CONFIG = {
'app_path''notepad',
'default_timeout'30,
'screenshot_on_error'True,
'test_cases': [
        {
'name''测试记事本启动',
'steps': [
                {'action''hotkey''keys': ['win''r']},
                {'action''type''locator': {'type''coords''value': (200100)}, 'text''notepad'},
                {'action''hotkey''keys': ['enter']},
                {'action''wait''seconds'2},
                {'action''assert_image''image''notepad_window.png''confidence'0.7}
            ]
        },
        {
'name''测试文本输入',
'steps': [
                {'action''type''locator': {'type''coords''value': (100100)}, 'text''Hello, PyAutoGUI!'},
                {'action''wait''seconds'0.5},
                {'action''hotkey''keys': ['ctrl''s']},
                {'action''wait''seconds'1},
                {'action''type''locator': {'type''coords''value': (300300)}, 'text''test_output.txt'},
                {'action''hotkey''keys': ['enter']}
            ]
        },
        {
'name''测试关闭应用',
'steps': [
                {'action''hotkey''keys': ['alt''f4']},
                {'action''wait''seconds'1},
                {'action''assert_image''image''notepad_window.png''confidence'0.7'timeout'2}
            ]
        }
    ]
}


# ==================== 使用示例 ====================
if __name__ == '__main__':
# 保存配置文件
with open('test_config.json''w', encoding='utf-8'as f:
        json.dump(TEST_CONFIG, f, ensure_ascii=False, indent=2)

# 创建测试框架实例
    test_framework = AutoTestFramework('test_config.json')

# 运行所有测试
    results = test_framework.run_all_tests()

# 检查结果
    passed = sum(1for r in results if r['status'] == 'PASS')
    failed = sum(1for r in results if r['status'] == 'FAIL')

    print(f"\n测试完成!通过: {passed}, 失败: {failed}")

框架特性

  • 配置驱动:支持 JSON 配置文件管理测试用例
  • 双模式定位:支持坐标定位和图像识别定位
  • 自动降级:OpenCV 不可用时自动降级为精确匹配
  • 报告生成:自动生成 JSON 和文本格式测试报告
  • 错误截图:测试失败时自动截取屏幕
  • 日志记录:详细的日志记录便于排查问题
  • 灵活断言:支持图像存在断言和位置断言

测试用例配置说明

  • name: 测试用例名称
  • steps: 测试步骤列表
    • action: 操作类型(click/type/wait/hotkey/assert_image/assert_position)
    • locator: 元素定位器(坐标或图像路径)
    • text: 输入文本(type操作)
    • keys: 快捷键列表(hotkey操作)
    • seconds: 等待时间(wait操作)
    • image: 断言图像路径(assert_image操作)
    • confidence: 图像匹配置信度

案例2:多显示器控制

注意:PyAutoGUI 官方仅支持主显示器操作。要实现多显示器控制,需要借助第三方库 screeninfo 来获取显示器信息,再结合 PyAutoGUI 的全局坐标进行操作。

import pyautogui
from screeninfo import get_monitors

# 获取所有显示器信息
monitors = get_monitors()
print(f"检测到 {len(monitors)} 个显示器")

for i, monitor in enumerate(monitors):
    print(f"显示器 {i+1}{monitor.name} "
f"位置=({monitor.x}{monitor.y}) "
f"分辨率={monitor.width}x{monitor.height}")

# 在指定显示器上操作
defclick_on_monitor(monitor_index, x, y):
"""
    在指定显示器的相对坐标处点击
    :param monitor_index: 显示器索引(从0开始)
    :param x: 显示器内的相对X坐标
    :param y: 显示器内的相对Y坐标
    """

    monitor = monitors[monitor_index]
    absolute_x = monitor.x + x
    absolute_y = monitor.y + y
    pyautogui.click(absolute_x, absolute_y)

# 在第二个显示器的(100, 100)位置点击
if len(monitors) > 1:
    click_on_monitor(1100100)
else:
    print("当前只有一个显示器")

# 截取指定显示器的画面
defscreenshot_monitor(monitor_index):
    monitor = monitors[monitor_index]
return pyautogui.screenshot(
        region=(monitor.x, monitor.y, monitor.width, monitor.height)
    )

安装 screeninfo

pip install screeninfo

案例3:图像识别与自动化

注意

  • confidence 参数需要安装 OpenCV 支持:pip install opencv-python
  • 未安装 OpenCV 时,代码会自动降级为精确匹配模式
import pyautogui
import time
from datetime import datetime

defcheck_in():
# 打开浏览器
    pyautogui.hotkey('win''r')
    pyautogui.write('chrome')
    pyautogui.press('enter')
    time.sleep(3)

# 访问打卡页面
    pyautogui.click(30050)  # 点击地址栏
    pyautogui.write('https://example.com/checkin')
    pyautogui.press('enter')
    time.sleep(5)

# 点击打卡按钮
try:
# 尝试使用 confidence(需要 OpenCV),失败则降级为精确匹配
try:
            checkin_button = pyautogui.locateCenterOnScreen(
'checkin_button.png', confidence=0.8
            )
except NotImplementedError:
# 未安装 OpenCV,使用精确匹配
            checkin_button = pyautogui.locateCenterOnScreen(
'checkin_button.png'
            )
        pyautogui.click(checkin_button)
        print(f"打卡成功!时间: {datetime.now()}")
except pyautogui.ImageNotFoundException:
        print("未找到打卡按钮,请检查页面")

# 设置定时打卡(每天9点)
print("定时打卡程序已启动,每天9:00 自动打卡...")
whileTrue:
    now = datetime.now()
if now.hour == 9and now.minute == 0:
        check_in()
        time.sleep(61)  # 避免重复打卡
    time.sleep(30)  # 每30秒检查一次

案例4:GUI自动化工作流

import pyautogui
import time

classAutomationWorkflow:
def__init__(self):
        self.steps = []

defadd_step(self, action, params=None):
        self.steps.append({'action': action, 'params': params or {}})

defexecute(self):
for step in self.steps:
            action = step['action']
            params = step['params']

if action == 'click':
                pyautogui.click(**params)
elif action == 'type':
                text = params.get('text''')
                interval = params.get('interval'0.0)
                pyautogui.write(text, interval=interval)
elif action == 'hotkey':
                keys = params.get('keys', [])
                pyautogui.hotkey(*keys)
elif action == 'wait':
                seconds = params.get('seconds'1)
                time.sleep(seconds)
elif action == 'move':
                pyautogui.moveTo(**params)
elif action == 'drag':
                pyautogui.dragTo(**params)

            time.sleep(0.5)

# 创建工作流:打开记事本,输入文字,保存文件
workflow = AutomationWorkflow()
workflow.add_step('hotkey', {'keys': ['win''r']})
workflow.add_step('type', {'text''notepad'})
workflow.add_step('hotkey', {'keys': ['enter']})
workflow.add_step('wait', {'seconds'2})
workflow.add_step('type', {'text''Hello from PyAutoGUI!''interval'0.1})
workflow.add_step('hotkey', {'keys': ['ctrl''s']})
workflow.add_step('wait', {'seconds'1})
workflow.add_step('type', {'text''automation.txt'})
workflow.add_step('hotkey', {'keys': ['enter']})

# 执行工作流
print("3秒后开始执行工作流...")
time.sleep(3)
workflow.execute()
print("工作流执行完成!")

六、注意事项与最佳实践

6.1 安全设置

import pyautogui

# 启用失败安全(移动鼠标到屏幕左上角可中断脚本,默认 True)
pyautogui.FAILSAFE = True

# 设置全局操作间隔,防止操作过快(秒)
pyautogui.PAUSE = 0.5

# 设置鼠标移动最小持续时间(秒),小于该值则瞬时移动
pyautogui.MINIMUM_DURATION = 0.1

# 捕获失败安全异常
try:
# 你的自动化代码
    pyautogui.moveTo(100100)
except pyautogui.FailSafeException:
    print("脚本被用户中断(鼠标移到了左上角)")

6.2 性能优化

import pyautogui

# 只截取需要的区域,提高截图速度
screenshot = pyautogui.screenshot(region=(00800600))

# 使用置信度参数平衡速度与准确性(需要 OpenCV)
# confidence 越低,匹配越快但可能误匹配
try:
    location = pyautogui.locateOnScreen('image.png', confidence=0.7)
except pyautogui.ImageNotFoundException:
pass
except NotImplementedError:
# 未安装 OpenCV 时回退到精确匹配
try:
        location = pyautogui.locateOnScreen('image.png')
except pyautogui.ImageNotFoundException:
pass

# 使用灰度匹配加快速度(需要 OpenCV)
try:
    location = pyautogui.locateOnScreen('image.png', grayscale=True)
except pyautogui.ImageNotFoundException:
pass
except NotImplementedError:
pass

# 减少不必要的操作,批量处理
# 避免频繁调用 pyautogui 函数

6.3 常见问题

1. 图像识别失败

  • 确保截图清晰,分辨率与当前屏幕一致
  • 调整 confidence 参数(需要 OpenCV)
  • 使用 grayscale=True 提高匹配速度和鲁棒性
  • 限定搜索区域(region 参数),减少计算量
  • 注意:PyAutoGUI 0.9.41+ 版本找不到图像时抛出 ImageNotFoundException,而非返回 None

2. confidence 参数报错 NotImplementedError

  • 原因:未安装 OpenCV,confidence 和 grayscale 参数需要 OpenCV 支持

  • 解决方法 1:安装 OpenCV

    pip install opencv-python
  • 解决方法 2:代码中添加降级处理,不使用 confidence 参数

  • 解决方法 3:使用 try-except NotImplementedError 捕获异常并回退

3. 坐标不准确

  • 使用 pyautogui.position() 实时获取精确坐标
  • 考虑屏幕缩放比例(DPI 缩放)的影响
  • 优先使用图像识别定位,而非硬编码坐标

4. 权限问题

  • macOS 需要在「系统设置→隐私与安全性→辅助功能」中授权
  • Linux 可能需要安装 scrotpython3-xlib 等依赖
  • Windows 一般无需额外权限

5. 中文输入问题

  • pyautogui.write() 仅支持英文输入

  • 中文输入建议使用 pyperclip 复制粘贴:

    import pyperclip
    import pyautogui

    pyperclip.copy('你好,世界')  # 复制中文到剪贴板
    pyautogui.hotkey('ctrl''v')  # 粘贴

6. 多显示器问题

  • PyAutoGUI 默认只支持主显示器
  • 使用 screeninfo 库获取多显示器信息
  • 通过全局坐标在副显示器上操作

七、总结

PyAutoGUI 是一个功能强大的自动化工具,可以帮助我们完成各种重复性任务。从简单的鼠标键盘控制到复杂的图像识别和工作流自动化,PyAutoGUI 都能胜任。

学习路径建议:

  1. 掌握基础操作(鼠标、键盘、截图)
  2. 学习图像识别技术与异常处理
  3. 尝试编写简单的自动化脚本
  4. 构建完整的自动化工作流
  5. 结合其他库(OpenCV、pygetwindow 等)扩展功能

扩展资源:

  • 官方文档:https://pyautogui.readthedocs.io
  • 图像识别引擎:OpenCV、PIL/Pillow
  • 窗口控制:pygetwindow
  • 网页自动化:Selenium、Playwright

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-07-03 09:18:45 HTTP/2.0 GET : https://f.mffb.com.cn/a/501344.html
  2. 运行时间 : 0.741745s [ 吞吐率:1.35req/s ] 内存消耗:5,070.98kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=23761441c7e67d2daecd4ed53f87911b
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000626s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000753s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.032161s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.030627s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000838s ]
  6. SELECT * FROM `set` [ RunTime:0.006886s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000609s ]
  8. SELECT * FROM `article` WHERE `id` = 501344 LIMIT 1 [ RunTime:0.218304s ]
  9. UPDATE `article` SET `lasttime` = 1783041526 WHERE `id` = 501344 [ RunTime:0.057522s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.003346s ]
  11. SELECT * FROM `article` WHERE `id` < 501344 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000563s ]
  12. SELECT * FROM `article` WHERE `id` > 501344 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.003000s ]
  13. SELECT * FROM `article` WHERE `id` < 501344 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.002869s ]
  14. SELECT * FROM `article` WHERE `id` < 501344 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.008980s ]
  15. SELECT * FROM `article` WHERE `id` < 501344 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.305267s ]
0.743474s