0%

Python-计算机自动化

Python 进行自动化处理相关的库。如:

  • pyautogui:Python 的 GUI 自动化处理工具,Python 代码版的按键精灵
  • key_board:检测按键
  • APScheduler:Advanced Python Scheduler:基于 Quartz 的 Python 定时任务框架,轻量而功能强大的进程内任务调度器
  • sched:【标准库】事件调度器
  • watchdog: 管理文件系统事件的 API 和 shell 工具,用于监视文件系统变动。
  • pywinauto:PC 端自动化工具,Windows 应用程序的自动化
  • pywin32:Python 访问 WindowsAPI 的常量、接口、线程以及 COM 机制等等。
  • when-changed:文件变动监控
  • nox:自动化任务
    • tox:自动化任务

概述

定时任务

  • Linux 的 crontab
  • time.sleep():用于简单的测试
  • schedule 模块阻塞式的,一般死循环来定义,需并行要用多线程
  • threadingTimer
  • python-crontab:linux 可用,不跨平台
  • celery:可用于任务调度
  • APSchedulerJava 风格,apscheduler.schedulers.blocking.BlockingScheduler,这样一长串,呕。,woc,真香

PyAutoGUI

pyautogui:Python 的 GUI 自动化处理工具,Python 代码版的按键精灵

全局设置

依赖

  • python3-Xlib
  • 截屏需要:sudo pacman -S scrot

保护措施 -FAILSAFE

  • 默认pyautogui.FAILSAFE = True:pyautogui 在运行过程中,将鼠标光标放在屏幕左上角,会报 pyautogui.FailSafeException 异常。
  • 有需要就鼠标光标移到屏幕左上角,要禁用就 pyautogui.FAILSAFE=False

操作延迟 -PAUSE

  • 默认每步操作间间隔为 0.1 秒,即延迟时间。
  • 可设置 pyautogui.PAUSE = 0,无间隔。
    • 注:改为 0 后跑得飞快,得注意“安全”。
  • 所有的 PyAutoGUI 函数在延迟完成前都处于阻塞状态(block)。

说明

  • 灵活结合 time.sleep()

屏幕信息

屏幕位置使用 X 和 Y 轴的笛卡尔坐标系。原点 (0,0) 在左上角,分别向右、向下增大。
如果屏幕像素是 1920×1080,那么右下角的坐标是 (1919, 1079)

1
2
3
## 坐标系的原点是左上角,x 轴向右增大,y 轴向下增大
screen_w, screen_h = pyautogui.size() # 1920,1080
pyautogui.onScreen(x,y) # bool,点是否在屏幕上

鼠标 -mouse

当前鼠标位置

1
now_mouse_x, now_mouse_y = pyautogui.position()  #当前鼠标位置

移动 -moveTo

  • 默认的持续时间 pyautogui.MINIMUM_DURATION 是 0.1 秒,如果你设置的时间比默认值还短,那么就会瞬间执行。
1
2
3
4
5
6
7
8
9
10
11
pyautogui.moveTo(100,200)
pyautogui.moveTo(None,200) #None 指当前鼠标的值
# 相对移动,鼠标向下移动 10 像素
pyautogui.moveRel(None, 10)
#以缓动/渐变函数让鼠标 2 秒后移动到(100,200)
pyautogui.moveTo(100,200,duration=2,tween=pyautogui.easeInOutQuad)
# 用 num_seconds 秒的时间把光标的 X 轴(水平)坐标移动 xOffset,
# Y 轴(竖直)坐标向下移动 yOffset。
xOffset, yOffset = 50, 100
# 相对移动
pyautogui.moveRel(xOffset, yOffset, duration=num_seconds)

鼠标拖拽

  • dragTo,dragRel 类似于 moveTo,moveRel,只是是按下去的拖拽
  • button 可取 left,middle,right
1
2
pyautogui.dragTo(100,200,button='left') #按着左键,拖拽到(100,200)
pyautogui.dragRel(300,400,2,button="right") #按着左键,花 2 秒,拖拽到相对位置

点击 -click

1
2
3
4
5
6
pyautogui.click()  # 单击
pyautogui.doubleClick() # 双击
pyautogui.rightClick() # 右击
pyautogui.click(100,200,2,0,'right')
# x=100,y=200,clicks=2,interval=0,button='right'
#坐标,点击次数,每两次间间隔,点击的鼠标键,left/middle/right

按下与松开

1
2
3
4
5
6
7
8
#  鼠标左键按下再松开
pyautogui.mouseDown(); pyautogui.mouseUp()
pyautogui.mouseDown(100,200,button='left')
pyautogui.mouseUp(100,200,button='middle')
# 按下鼠标右键
pyautogui.mouseDown(button='right')
# 移动到(100, 200)位置,然后松开鼠标右键
pyautogui.mouseUp(button='right', x=100, y=200)

滚轮滚动 -scroll

1
2
3
pyautogui.scroll(5,100,200) # 5:滚动的格数,正数滚轮向前,页面向上,负数页面向下
# 移动到(100, 100)位置再向下滚动 10 格
pyautogui.scroll(-10, x=100, y=100)
  • 在 OS X 和 Linux 平台上,PyAutoGUI 还可以用 hscroll() 实现水平滚动
1
2
3
4
#  向右滚动 10 格
pyautogui.hscroll(10)
# 向左滚动 10 格
pyautogui.hscroll(-10)

键盘操作 keyboard

所有按键

1
2
3
4
5
6
7
8
>>> pyautogui.KEYBOARD_KEYS

['\t', '\n', '\r', ' ',
'!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'{', '|', '}', '~', 'accept', 'add', 'alt', 'altleft', 'altright', 'apps', 'backspace', 'browserback', 'browserfavorites', 'browserforward', 'browserhome', 'browserrefresh', 'browsersearch', 'browserstop', 'capslock', 'clear', 'convert', 'ctrl', 'ctrlleft', 'ctrlright', 'decimal', 'del', 'delete', 'divide', 'down', 'end', 'enter', 'esc', 'escape', 'execute', 'f1', 'f10', 'f11', 'f12', 'f13', 'f14', 'f15', 'f16', 'f17', 'f18', 'f19', 'f2', 'f20', 'f21', 'f22', 'f23', 'f24', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'final', 'fn', 'hanguel', 'hangul', 'hanja', 'help', 'home', 'insert', 'junja', 'kana', 'kanji', 'launchapp1', 'launchapp2', 'launchmail', 'launchmediaselect', 'left', 'modechange', 'multiply', 'nexttrack', 'nonconvert', 'num0', 'num1', 'num2', 'num3', 'num4', 'num5', 'num6', 'num7', 'num8', 'num9', 'numlock', 'pagedown', 'pageup', 'pause', 'pgdn', 'pgup', 'playpause', 'prevtrack', 'print', 'printscreen', 'prntscrn', 'prtsc', 'prtscr', 'return', 'right', 'scrolllock', 'select', 'separator', 'shift', 'shiftleft', 'shiftright', 'sleep', 'space', 'stop', 'subtract', 'tab', 'up', 'volumedown', 'volumemute', 'volumeup', 'win', 'winleft', 'winright', 'yen', 'command', 'option', 'optionleft', 'optionright']

按键

1
2
3
4
5
6
7
8
9
10
11
12
13
pyautogui.press('esc')
pyautogui.keyDown(key_name)
pyautogui.keyUp(key_name)
pyautogui.press(['left','left','left']) # 按键列表,一个个按
pyautogui.hotkey('ctrl','c') # hotkey,一起按
pyautogui.keyDown('ctrl')
pyautogui.press('c')
pyautogui.keyUp('ctrl')
# 每次键入的时间间隔
secs_between_keys = 0.1
pyautogui.typewrite('Hello world!\n', interval=secs_between_keys)
pyautogui.typewrite(['a', 'b', 'c', 'left', 'backspace', 'enter', 'f1'], interval=secs_between_keys)
pyautogui.keyUp('shift')

typewrite()

1
2
# 单个字符输入
pyautogui.typewrite('Hello world!', interval=0.25)

消息弹窗 MessageBox

1
2
3
4
5
6
7
8
9
10
11
12
13
## 输出
pyautogui.alert('helloworld')
pyautogui.confirm('ok or cancel?')
pyautogui.confirm('Enter option.', buttons=['A', 'B', 'C'])
pyautogui.prompt('What is your name?')
pyautogui.password('enter your password')
pyautogui.alert(text='这个消息弹窗是文字+OK 按钮',title='标题 1’,button='ok')
pyautogui.confirm('这个消息弹窗是文字+OK+Cancel 按钮','标题 2',['OK',cancel])
# ok cancel
pyautogui.prompt('这个消息弹窗是让用户输入字符串,单击 OK','标题 3',defalut='')
# 输入的值,无输入返回 None
样式同`prompt()`,用于输入密码,消息用`*`表示。带 OK 和 Cancel 按钮。用户点击 OK 按钮返回输入的文字,点击 Cancel 按钮返回`None`。
pyautogui.password(text='', title='', default='', mask='*')

截屏与找图 -screenshot

截屏

1
2
3
4
5
6
7
8
9
10
11
12
13
pyautogui.screenshot()
pyautogui.screenshot('aa.png')
im = pyautogui.screenshot(region=(0, 0, 300 ,400),'aa.png')
# 截图
im1 = pyautogui.screenshot()
im1.save('my_screenshot.png')
im2 = pyautogui.screenshot('my_screenshot2.png')
button7location = pyautogui.locateOnScreen('button.png') # returns (left, top, width, height) of matching region
# (1416, 562, 50, 41)

buttonx, buttony = pyautogui.center(button7location)
(1441, 582)
pyautogui.click(buttonx, buttony) # 点击之

找图

1920-1080 的屏幕,定位耗时需 1-2 秒,不足以定制游戏脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
location1=pyautogui.locateOnScreen('aa.png')
# 左上角的 x,y,宽度,高度
pyautogui.center(location1) #中心坐标
# 搜图
imagePlace = pyautogui.locateOnScreen('C:/Users/chen/Desktop/test.png') # returns (left, top, width, height) of matching region
buttonx, buttony = pyautogui.center(imagePClace)
pyautogui.click(buttonx, buttony) # clicks the center of where the button was found
buttonx, buttony = pyautogui.locateCenterOnScreen('button.png') # returns (x, y) of matching region
>>> buttonx, buttony
(1441, 582)
>>> pyautogui.click(buttonx, buttony)
for i in range(10):
imagePlace = pyautogui.locateOnScreen('C:/Users/chen/Desktop/chen33.png') # returns (left, top, width, height) of matching region
buttonx, buttony = imagePlace[0]-2,imagePlace[1]-2
pyautogui.click(buttonx, buttony)
pyautogui.press('enter')

locateAllOnScreen() 函数会寻找所有相似图片,返回一个生成器:

  • locateOnScreen(image, grayscale=False):返回找到的第一个截图 Image 对象在屏幕上的坐标 (left, top, width, height),如果没找到返回 None
  • locateCenterOnScreen(image, grayscale=False):返回找到的第一个截图 Image 对象在屏幕上的中心坐标 (x, y),如果没找到返回 None
  • locateAllOnScreen(image, grayscale=False):返回找到的所有相同截图 Image 对象在屏幕上的坐标 (left, top, width, height) 的生成器
  • locate(needleImage, haystackImage, grayscale=False):返回找到的第一个截图 Image 对象在 haystackImage 里面的坐标 (left, top, width, height),如果没找到返回 None
  • locateAll(needleImage, haystackImage, grayscale=False):返回找到的所有相同截图 Image 对象在 haystackImage 里面的坐标 (left, top, width, height) 的生成器
  • 两个 locateAll* 函数都可以用 for 循环和 list() 输出:
    如果没找到图片会返回 None
  • 灰度值定位,约能提速 30%,导致假阳性(false-positive)匹配,默认为 False
1
2
import pyautogui
button7location = pyautogui.locateOnScreen('pyautogui/calc7key.png', grayscale=True)

位置点像素值

1
2
3
4
5
# RGB
img = pyautogui.screenshot()
img.getpixel((100,200)) # 获取 RGB 像素值
pyautogui.pixel(100, 200) # 或直接取
pyautogui.pixelMatchesColor(100, 200, (255, 255, 255),tolerance=10) # True or False,tolerance:红绿蓝 3 种颜色的误差范围

keyboard

keyboard:实现键盘事件的监听、模拟和控制

提供了一套简单而强大的API,使开发者能够轻松实现键盘事件的捕获、按键模拟和热键设置。与PyAutoGUI相比,keyboard更专注于键盘操作,具有以下特点:

优势:

  • 跨平台支持(Windows、Linux、macOS)
  • 低级键盘事件的精确控制
  • 支持全局热键绑定
  • 键盘事件的录制和回放
  • 轻量级,安装简单
  • 支持非ASCII键盘布局

局限:

  • 在某些Linux系统上需要root权限
  • 不支持图形界面自动化(需配合其他库使用)
  • macOS上部分功能受限

基本使用

1
2
pip install keyboard
# 注意:在Linux系统上,可能需要使用sudo权限运行Python脚本。
1
2
3
4
5
6
7
8
9
10
import keyboard  # using module keyboard
while True: # making a loop
try: # used try so that if user pressed other than the given key error will not be shown
if keyboard.is_pressed('q'): # if key 'q' is pressed
print('You Pressed A Key!')
break # finishing the loop
else:
pass
except:
break # if user pressed a key other than the given key the loop will break

按键检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import keyboard
import time

def on_key_event(e):
print(f'按键: {e.name}')
print(f'事件类型: {"按下" if e.event_type == "down" else "释放"}')
print(f'扫描码: {e.scan_code}')
print('---')

# 注册回调函数
keyboard.hook(on_key_event)

# 保持程序运行
print('开始监听键盘事件(按Esc退出)...')
keyboard.wait('esc')



import keyboard

def on_press(event):
print(f'Key {event.name} pressed')

# 监听键盘事件
keyboard.on_press(on_press)

# 保持程序运行,直到按下Ctrl+C
keyboard.wait('ctrl+c')

模拟输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import keyboard
import time

# 模拟单个按键
keyboard.press_and_release('a')

# 模拟组合键
keyboard.press_and_release('ctrl+c')

# 模拟按键序列
keyboard.write('Hello, World!', delay=0.1)
# 模拟按下Ctrl+C组合键
keyboard.send('ctrl+c')

# 模拟输入文本
keyboard.send('Hello, world!')

# 模拟按下Enter键
keyboard.send('enter')
# 模拟按住和释放
keyboard.press('shift')
keyboard.write('python')
keyboard.release('shift')

热键组合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import keyboard
import time

def on_triggered():
print('触发了热键组合!')

# 注册热键
keyboard.add_hotkey('ctrl+shift+a', on_triggered)

# 使用装饰器注册热键
@keyboard.add_hotkey('ctrl+shift+p')
def another_handler():
print('另一个热键被触发!')

print('热键已注册,按Esc退出...')
keyboard.wait('esc')

录制与回放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import keyboard
import time

def record_macro():
print('开始录制,按Esc停止...')
recorded = keyboard.record(until='esc')

print('录制完成!按空格回放,按q退出...')
while True:
if keyboard.is_pressed('space'):
keyboard.play(recorded)
elif keyboard.is_pressed('q'):
break
time.sleep(0.1)

record_macro()

键盘映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import keyboard
from threading import Thread

def remap_key(original, new):
keyboard.remap_key(original, new)

def start_remapping():
# 将Caps Lock映射为Esc
remap_key('caps lock', 'esc')

# 保持映射运行
keyboard.wait()

# 在新线程中启动映射
Thread(target=start_remapping, daemon=True).start()

综合使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import keyboard
import time
from datetime import datetime

class TextAutomation:
def __init__(self):
self.templates = {
'datetime': self._get_datetime,
'signature': lambda: '\n\nBest regards,\nPython Developer',
'code': lambda: '```python\n# Your code here\n```'
}
self._setup_hotkeys()

def _get_datetime(self):
return datetime.now().strftime('%Y-%m-%d %H:%M:%S')

def _setup_hotkeys(self):
for key, template in self.templates.items():
keyboard.add_hotkey(f'ctrl+alt+{key[0]}',
lambda t=template: keyboard.write(t()))

def run(self):
print('文本自动化工具已启动...')
print('使用以下快捷键:')
print('Ctrl+Alt+D - 插入日期时间')
print('Ctrl+Alt+S - 插入签名')
print('Ctrl+Alt+C - 插入代码块')
print('按Esc退出')
keyboard.wait('esc')

if __name__ == '__main__':
automation = TextAutomation()
automation.run()

注意事项

在使用keyboard库进行桌面自动化开发时,请注意以下几点:

  1. 权限使用
  • 在需要管理员权限的系统上,明确告知用户权限要求
  • 避免在无权限的情况下执行敏感操作
  1. 异常处理
  • 合理处理键盘事件异常
  • 提供清晰的错误提示
  • 实现优雅的退出机制
  1. 性能考虑
  • 避免过于频繁的键盘操作
  • 合理设置延时,防止系统过载
  • 注意内存使用,避免内存泄漏
  1. 安全性
  • 不要在键盘监听中记录敏感信息
  • 避免自动化操作影响系统安全
  • 提供紧急停止机制

APScheduler

APScheduler:Advanced Python Scheduler:基于 Quartz 的 Python 定时任务框架,轻量而功能强大的进程内任务调度器

  • 定制性高(triggers):时间间隔(Interval)、周期性时间(Date)、cron 形式(Linux crontab)
  • 后台存储(job stores):Memory、SQLAlchemy、Redis、MongoDB 等
  • sched:标准库
    • 有毫秒级的时间差(APScheduler是1ms内)
  • schedule模块的局限性包括:单线程阻塞导致长任务阻塞其他任务、无任务持久化机制、缺乏错误重试、并发控制和分布式能力
  • apscheduler(支持持久化和多调度器,适合中等复杂度场景)、celery(分布式任务队列,适合高并发和复杂任务)、系统cron(稳定但管理分散,适合独立脚本)以及threading/multiprocessing(灵活但需自行实现调度逻辑)

架构

组件:

  • triggers:触发器:每个 job 都有触发器,包含它的调度逻辑
  • job stores:作业存储:默认在内存中,序列化后可保存到其他地方,如 redis
  • executors:执行器:处理作业的运行,将 job 中的可调用部分提交给线程或进程池来实现,job 完成后,执行器会通知调度器
    • ThreadPool、ProcessPool
  • schedulers:调度器:将其他组件绑定在一起,对使用者提供接口通常只有一个调度器 scheduler 在程序中运行。
    • Add、Modify、Remove·
1
2
3
4
5
6
7
BlockingScheduler # 调度器是程序中唯一要运行的东西
BackgroundScheduler # 后台运行
AsyncIOScheduler # asyncio
GeventScheduler # gevent
TornadoScheduler # Tornado
TwistedScheduler # Twister
QTScheduler # QT

使用

1
pip instal apscheduler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import time
import datetime
from apscheduler.schedulers.background import BackgroundScheduler

def task_test():
print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime()))

if __name__ == '__main__':
schedular = BackgroundScheduler() # 后台
schedular.add_job(task_test,'interval',seconds=2) # 以 interval 间隔性执行

schedular.start()

while True:
print(time.time())
time.sleep(5)

def job_func(text):
print(text)

scheduler = BackgroundScheduler()
# 在 2017-12-13 时刻运行一次 job_func 方法
scheduler .add_job(job_func, 'date', run_date=date(2017, 12, 13), args=['text'])
# 在 2017-12-13 14:00:00 时刻运行一次 job_func 方法
scheduler .add_job(job_func, 'date', run_date=datetime(2017, 12, 13, 14, 0, 0), args=['text'])
# 在 2017-12-13 14:00:01 时刻运行一次 job_func 方法
scheduler .add_job(job_func, 'date', run_date='2017-12-13 14:00:01', args=['text'])

scheduler.start()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
def job_func(text):
print(text)
# date,指定时间运行一次
scheduler.add_job(job_func,
'date',
run_date=datetime.datetime(2020,1,1,14,0,0)
# run_date='2020-1-1 14:00:00'
args=['test'])
# interval,固定时间
scheduler.add_job(job_func,
'interval',
minutes=2,
start_date='2017-12-13 14:00:01' , # datetime or str
end_date='2017-12-13 14:00:10') # datetime or str
weeks,days,hours,minutes,seconds
# cron,触发器,特定时间周期触发,与 Linux crontab 格式兼容
# 在每年 1-3、7-9 月份中的每个星期一、二中的 00:00, 01:00, 02:00 和 03:00 执行 job_func 任务
scheduler.add_job(job_func, 'cron', month='1-3,7-9',day='0, tue', hour='0-3')
sc.add_job(status_monitor, 'cron', day_of_week='*', hour='*', minute='*', second='5', id='status_monitor')
# 每分钟的第5秒
sc.add_job(monitor, 'cron', day_of_week='*', hour='*', minute='*', second='5', id='monitor')
year 年 int or str 4 位数字
month 月 int or str 1-12
day 日 int or str 1-31
week 周 int or str 1-53
day_of_week 星期几 int or str 0-6 或 mon,tue,wed,thu,fri,sat,sun
hour 时 int or str 0-23
minute 分 int or str 0-59
second 秒 int or str 0-59

start_date 最早开始时间(包含) datetime or str
end_date 最晚结束时间(包含) datetime or str
timezone 指定时区 datetime.tzinfo or str
分 minute[0-59]时 hour[0-23]日 day[1-31]月 month[1-12]周 week[0-7],0 或代表星期日

大于最小有效值的字段默认为*,小于最小有效值的默认为最小值。
参数表达式
* 字段的任一个值
*/a 匹配每递增 a 后的值,如 hour 的*/5,为 05101520
a/b 从 a 开始,匹配每递增 b 后的值,如 hour 的 2/9,匹配 2,11,20
a-b a 到 b 之间的取值,2-5,匹配 2,3,4,5
a-b/c a 到 b 间递增 c 的值,包括 a,不一定包括 b,如 1-20/5,匹配 1,6,11,16
xth y 匹配 y 在当月的第 x 次,如 3rd fri,指当月的第三个周五
last x 匹配 x 在当月的最后一次,如 last fri,当月的最后一个周五
last 匹配当月的最后一天
x,y,z 匹配多个表达式的组合

遇到的问题

  • 问题:Timezone offset does not match system offset: 0 != 28800
  • 解决方案
1
2
# 因为系统时区和代码运行时区不一样导致,需在初始化的时候指定上海的时区
scheduler = BlockingScheduler(timezone="Asia/Shanghai")

sched

sched:【标准库】事件调度器

  • 定时执行程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import sched
import time
from datetime import datetime
# 初始化 sched 模块的 scheduler 类
# 第一个参数是一个可以返回时间戳的函数,第二个参数可以在定时未到达之前阻塞。
schedule = sched.scheduler(time.time, time.sleep)
def test(inc):
print(f'helloworld!{time.time()}')
schedule.enter(inc, 0, test, (inc,))

def main(delay=60):
# delay 间隔时间,优先级,触发的函数,函数的参数
schedule.enterabs(time.time()+5, 0, test, (delay,))
# schedule.enter(0, 0, test, (delay,))
schedule.run()

# 一秒输出一次
main(1)

watchdog

watchdog: 管理文件系统事件的 API 和 shell 工具,用于监视文件系统变动。

  • Watchdog 是一个用于监控文件系统事件的 Python 库。它提供了跨平台的 API 来监视文件系统更改,如文件的创建、修改、删除等事件。这个库在开发过程中特别有用,可以用于实现自动化构建、热重载、文件同步等功能。Watchdog 的事件处理系统设计灵活,支持自定义处理器和过滤器。
1
pip install watchdog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

class MyHandler(FileSystemEventHandler):
def on_created(self, event):
if not event.is_directory:
print(f"Hey, {event.src_path} has been created!")

def on_modified(self, event):
if not event.is_directory:
print(f"Hey, {event.src_path} has been modified")

def on_deleted(self, event):
if not event.is_directory:
print(f"Oops! Someone deleted {event.src_path}!")

def on_moved(self, event):
print(f"Ok... someone moved {event.src_path} to {event.dest_path}")

def watch_directory(path):
event_handler = MyHandler()
observer = Observer()
observer.schedule(event_handler, path, recursive=False)
observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()

if __name__ == "__main__":
watch_directory("./my_directory")

pywinauto

pywinauto:PC 端自动化工具,Windows 应用程序的自动化

1
2
from pywinauto.application import Application
app=Application(backend='uia').start('notepad.exe')

PyWin32

pywin32:Python 访问 WindowsAPI 的常量、接口、线程以及 COM 机制等等。

  • 功能:
    • 组件对象模型
    • Win32 API 调用
    • 注册
    • 事件日志
    • MFC 用户界面
1
pip install pywin32

注册表修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import win32api
import win32con
# 打开
win32api.RegOpenKey(key, # 那几个根项之一,如 win32con.HKEY_USERS
subkey, # 层级式的子项,如 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\TTS_MS_EN-US_ZIRA_11.0\Attributes
reserved, # 必须为 0
sam) # 对打开项的操作,如 win32con.KEY_ALL_ACCESS、win32con.KEY_READ、win32con.KEY_WRITE 等
key = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER,keyname,0,win32con.KEY_ALL_ACCESS)
# 添加项值,项值存在则修改,不存在,则添加
RegSetValueEx(key, # 要设置项的句柄
valueName, # 要设置的项的名乐
reserved, # 保留,可设为 0
type# 项的类型,必须为 win32con.REG_SZ
value) # 要设置的值
# 添加项值
RegCreateKey(key,subKey) # 向注册表中添加项
RegDeleteKey(key,subKey) # 删除注册表中的项
# 查看项值
RegQueryValue(key,subKey) # 读取子项的默认值
RegQueryValueEx(key,valueName) # 读取某一项值
# 删除项值
# 设置值
win32api.RegSetValueEx(key,'Start Page',0,win32con.REG_SZ,page)
# 操作完后需关闭,关闭已打开注册表项的句柄
win32api.RegCloseKey(key)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import win32api
import win32gui
import win32con
print("Hello,world!")
def find_idxSubHandle(pHandle, winClass, index=0):
"""
已知子窗口的窗体类名
寻找第 index 号个同类型的兄弟窗口
"""
assert type(index) == int and index >= 0
handle = win32gui.FindWindowEx(pHandle, 0, winClass, None)
while index > 0:
handle = win32gui.FindWindowEx(pHandle, handle, winClass, None)
index -= 1
return handle
def find_subHandle(pHandle, winClassList):
"""
递归寻找子窗口的句柄
pHandle 是祖父窗口的句柄
winClassList 是各个子窗口的 class 列表,父辈的 list-index 小于子辈
"""
assert type(winClassList) == list
if len(winClassList) == 1:
return find_idxSubHandle(pHandle, winClassList[0][0], winClassList[0][1])
else:
pHandle = find_idxSubHandle(pHandle, winClassList[0][0], winClassList[0][1])
return find_subHandle(pHandle, winClassList[1:])
"""输出 phandle 的所有子控件"""
def p_sub_handle(phandle):
handle = -1
while handle !=0 :
if handle == -1:
handle = 0
handle = win32gui.FindWindowEx(phandle, handle, None, None)
if handle != 0:
className = win32gui.GetClassName(handle)
print(className)
"""
记事本实例
"""
notepadHhandle = win32gui.FindWindow("Notepad", None)
print ("%x" % (notepadHhandle))
editHandle = find_subHandle(notepadHhandle, [("Edit",0)])
print ("%x" % (editHandle))
"""修改 edit 中的值"""
win32api.SendMessage(editHandle, win32con.WM_SETTEXT, 0, "666666")
command_dict = { # [目录的编号, 打开的窗口名]
"open": [3, u"打开"]
}
"""操作菜单"""
menu = win32gui.GetMenu(notepadHhandle)
menu = win32gui.GetSubMenu(menu, 0)
cmd_ID = win32gui.GetMenuItemID(menu, command_dict["open"][0])
if cmd_ID == -1:
print("没有找到相应的菜单")
else:
print ("菜单 id:%x" % (cmd_ID))
win32gui.PostMessage(notepadHhandle, win32con.WM_COMMAND, cmd_ID, 0)

when-changed

when-changed:文件变动监控

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
when-changed [OPTION] FILE COMMAND  # file or dir
Options:
-r Watch recursively
-v Verbose output. Multiple -v options increase the verbosity.
The maximum is 3: -vvv.
-1 Don't re-run command if files changed while command was running
-s Run command immediately at start
-q Run command quietly
when-changed provides the following environment variables:
- WHEN_CHANGED_EVENT: reflects the current event type that occurs.
Could be either:
- file_created
- file_modified
- file_moved
- file_deleted
- WHEN_CHANGED_FILE: provides the full path of the file that has generated the event.

nox

nox:自动化任务

tox:自动化任务

webbrowser

webbrowser:【标准库】:提供一个高层级接口,允许用户显示基于 web 的文档

  • 方便的 web 浏览器控制工具

基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 用这个就够了
webbrowser.open( # 使用默认浏览器显示url
url,
new=0, # 0:尽可能在同型号浏览器打开url,1:尽可能打开的新的浏览器窗口;2:尽可能打开新的浏览器页面(标签)
autoraise=True, # 默认为True,置前窗口
)

# ---备用api,但一般不太用-
# 新开浏览器窗口
webbrowser.open_new(url)

# 新天浏览器页面(标签)
webbrowser.open_new_tab(url)

# 注册name浏览器类型
webbrowser.register(name, constructor, instance=None, *, preferred=False)
# 返回浏览器类型为
webbrowser.get(using=None)
给博主来一杯卡布奇诺