PyAutoGUI钢琴块脚本像素检测偶发OSError异常不稳定运行求助
故障原因
该报错是pyautogui底层依赖的pyscreeze库在Windows平台的已知偶发问题:你的脚本死循环内会高频调用pyautogui.pixel()接口,每次调用都要申请、释放Windows设备上下文(DC)资源,高频率下容易出现资源释放竞争失败,最终抛出OSError。
修复方案
- 方案1:添加异常捕获重试(改造成本最低,无需修改原有逻辑)
给像素读取逻辑加重试机制,遇到释放失败的错误自动重试,示例修改代码如下:
from pyautogui import * import pyautogui import time import keyboard import random import win32api, win32con def click(x, y): win32api.SetCursorPos((x, y)) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0) time.sleep(0.05) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0) # 封装带重试的安全像素读取函数 def get_pixel_r_safe(x: int, y: int) -> int: max_retry = 3 for _ in range(max_retry): try: return pyautogui.pixel(x, y)[0] except OSError: time.sleep(0.001) return 255 # 重试失败默认返回非黑色,避免误点击 while keyboard.is_pressed('q') == False: if get_pixel_r_safe(480, 280) == 0: click(480, 280) if get_pixel_r_safe(550, 280) == 0: click(550, 280) if get_pixel_r_safe(620, 280) == 0: click(620, 280) if get_pixel_r_safe(688, 280) == 0: click(688, 280)
- 方案2:批量截图读取像素(性能更优,降低资源消耗)
每次循环仅截取所有检测点所在的1行区域,单次截图就能拿到所有点的像素值,大幅减少DC资源申请释放的次数,从根源降低报错概率:
while keyboard.is_pressed('q') == False: # 仅截取检测点所在的水平行:x范围480~688,y固定280,区域高1像素 line_img = pyautogui.screenshot(region=(480, 280, 209, 1)) if line_img.getpixel((0, 0))[0] == 0: click(480, 280) if line_img.getpixel((70, 0))[0] == 0: # 550 - 480 = 70 click(550, 280) if line_img.getpixel((140, 0))[0] == 0: # 620 - 480 = 140 click(620, 280) if line_img.getpixel((208, 0))[0] == 0: # 688 - 480 = 208 click(688, 280)
- 方案3:用win32api原生接口读像素(最稳定,彻底绕开pyscreeze缺陷)
直接调用Windows原生API实现像素读取,不存在DC释放失败的问题,适配高频调用场景。
内容的提问来源于stack exchange,提问作者History Project
相关产品推荐
相关产品推荐

