使用Python + win32api实现简单自动鼠标点击

使用tkinter设计GUI界面并用pyinstaller打包

不知不觉肝阴阳师也快一年了,对这游戏真是又爱又恨,最近刚刚发布了PC版,突然很想尝试着写个脚本挂机,话不多说进入正题。

基础模拟点击

简单的鼠标操作

游戏挂机脚本,无非就是自动移动鼠标,自动点击,进行重复操作,所以,第一步就是如何控制鼠标

1

2

3

4

5

6

7

8

9

10

11

12

13

14import win32api

import time

def move_click(x, y, t=0): # 移动鼠标并点击左键

win32api.SetCursorPos((x, y)) # 设置鼠标位置(x, y)

win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN |

win32con.MOUSEEVENTF_LEFTUP, x, y, 0, 0) # 点击鼠标左键

if t == 0:

time.sleep(random.random()*2+1) # sleep一下

else:

time.sleep(t)

return 0

# 测试

move_click(30, 30)

当然,再后续操作中你可能需要获取屏幕分辨率,我只打算让脚本能在自己电脑上跑就满足了,所以没有实现适配不同分辨率

1

2def resolution(): # 获取屏幕分辨率

return win32api.GetSystemMetrics(0), win32api.GetSystemMetrics(1)

值得注意的是,一定要在管理员权限下的cmd中运行,否则点击无效

这个时候,你已经可以写个while循环,不停地点击屏幕上不同的几个点了,最基础的挂机脚本就实现了

使用PIL识别图像

我们肯定不满足于机械式的连续点击,万一被封号呢…

所以需要识别图像,再进行点击

首先,就需要定位到阴阳师的窗口

1

2

3

4

5

6

7

8

9

10import win32gui

def get_window_info(): # 获取阴阳师窗口信息

wdname = u'阴阳师-网易游戏'

handle = win32gui.FindWindow(0, wdname) # 获取窗口句柄

if handle == 0:

# text.insert('end', '小轩提示:请打开PC端阴阳师\n')

# text.see('end') # 自动显示底部

return None

else:

return win32gui.GetWindowRect(handle)

get_window_info()函数返回阴阳师窗口信息(x1, y1, x2, y2),(x1, y1)是窗口左上角的坐标,(x2, y2)是窗口右下角的坐标,代码中的text可以暂时忽略,这在后续GUI界面中用于输出提示信息。

下面使用PIL获取游戏截图

1

2

3

4

5

6

7

8

9

10

11

12def get_posx(x, window_size): # 返回x相对坐标

return (window_size[2] - window_size[0]) * x / 870

def get_posy(y, window_size): # 返回y相对坐标

return (window_size[3] - window_size[1]) * y / 520

topx, topy = window_size[0], window_size[1]

img_ready = ImageGrab.grab((topx + get_posx(500, window_size), topy + get_posy(480, window_size),

topx + get_posx(540, window_size), topy + get_posy(500, window_size)))

# 查看图片

im_ready.show()

考虑到窗口大小不同,位置会有所偏移,这里使用屏幕上点的相对位置

获取到关键位置的截图之后,计算图片的hash值

1

2

3

4

5def get_hash(img):

img = img.resize((16, 16), Image.ANTIALIAS).convert('L') # 抗锯齿 灰度

avg = sum(list(img.getdata())) / 256 # 计算像素平均值

s = ''.join(map(lambda i: '0' if i < avg else '1', img.getdata())) # 每个像素进行比对,大于avg为1,反之为0

return ''.join(map(lambda j: '%x' % int(s[j:j+4], 2), range(0, 256, 4)))

将关键位置截图的hash值保存下来,下次脚本运行时,将截图hash值与原始hash值进行比对,判断是否相似。

这里使用汉明距离进行计算,比较hash值中相同位置上不同元素的个数

1

2

3

4

5

6def hamming(hash1, hash2, n=20):

b = False

assert len(hash1) == len(hash2)

if sum(ch1 != ch2 for ch1, ch2 in zip(hash1, hash2)) < n:

b = True

return b

准备工作做完了,下面就可以开心刷御灵了

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

52def yu_ling(window_size):

global is_start

topx, topy = window_size[0], window_size[1]

state = []

while is_start:

# print 'start'

# text.insert('end', 'start')

time.sleep(0.5)

img_ready = ImageGrab.grab((topx + get_posx(750, window_size), topy + get_posy(465, window_size),

topx + get_posx(840, window_size), topy + get_posy(500, window_size)))

if hamming(get_hash(img_ready), ready_hash, 10):

state.append(0)

move_click(topx + get_posx(740, window_size), topy + get_posy(380, window_size))

text.insert('end', strftime('%H:%M:%S', localtime()) + ' 点击准备\n')

text.see('end') # 自动显示底部

time.sleep(15)

continue

img_success = ImageGrab.grab((topx + get_posx(400, window_size), topy + get_posy(320, window_size),

topx + get_posx(470, window_size), topy + get_posy(400, window_size)))

if hamming(get_hash(img_success), success_hash):

time.sleep(2)

state.append(1)

text.insert('end', strftime('%H:%M:%S', localtime()) + ' 成功%d次\n' % state.count(1))

text.see('end') # 自动显示底部

move_click(topx + get_posx(730, window_size), topy + get_posy(380, window_size))

continue

img_fail = ImageGrab.grab((topx + get_posx(560, window_size), topy + get_posy(340, window_size),

topx + get_posx(610, window_size), topy + get_posy(390, window_size)))

if hamming(get_hash(img_fail), fail_hash):

time.sleep(2)

state.append(2)

text.insert('end', strftime('%H:%M:%S', localtime()) + ' 失败%d次\n' % state.count(2))

text.see('end') # 自动显示底部

move_click(topx + get_posx(720, window_size), topy + get_posy(380, window_size))

continue

img_attack = ImageGrab.grab((topx + get_posx(615, window_size), topy + get_posy(350, window_size),

topx + get_posx(675, window_size), topy + get_posy(375, window_size)))

if hamming(get_hash(img_attack), yu_attack_hash):

move_click(topx + get_posx(670, window_size), topy + get_posy(365, window_size))

text.insert('end', strftime('%H:%M:%S', localtime()) + ' 点击进攻\n')

text.see('end') # 自动显示底部

state.append(3)

if state[-6:] == [3]*6:

text.insert('end', strftime('%H:%M:%S', localtime()) + ' 痴汉券可能不够了\n')

text.see('end') # 自动显示底部

click()

break

continue

至此,我们已经可以通过管理员cmd运行脚本了。

但这样的脚本运行起来比较麻烦,也没有好看的界面。接下来,我们将使用tkinter设计GUI界面,并用pyinstaller打包成.exe文件

GUI

tkinter

tkinter是Python内置的GUI设计界面,对小白来说容易上手,你也可以尝试用pyqt或者wx

关于tkinter可以看一下莫烦教程

首先创建一个窗口,并设置必要信息

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16import tkinter as tk

from icon import img

window = tk.Tk() # 创建一个窗口

window.title('奴良小轩v0.1')

window.geometry('240x480+120+30') # 窗口的位置以及大小

# 设置图标

with open('tmp.ico', 'wb+') as fp:

fp.write(base64.b64decode(img))

window.iconbitmap('tmp.ico')

os.remove('tmp.ico')

# 设置图标

label = tk.Label(window, font=('微软雅黑', 12),

text='请将PC端阴阳师调节与小宝等高') # 显示一段文本

label.pack()

设置图标

默认情况下,窗口图标是红色的TK,想修改则使用.iconbitmap(path)方法,但是,在实际使用踩坑了。因为后面我会使用pyinstaller打包,因为找不到path路径运行程序会报错,找了好久才找到这个错误。

解决方案是先将图标读取并写入ico.py文件,调用.iconbitmap(path)时读取ico.py,代码如下:

1

2

3

4

5

6

7

8import base64

open_icon = open('yaodao.ico', 'rb')

b64str = base64.b64encode(open_icon.read())

open_icon.close()

write_data = "img = '%s'" % b64str

f = open('icon.py', 'w+')

f.write(write_data)

f.close()

功能选择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# Radiobutton #

fun_var = tk.IntVar()

fun_text = ''

def print_selection():

global fun_text

if fun_var.get() == 1:

fun_text = '寮突破'

elif fun_var.get() == 2:

fun_text = '御灵、业原火'

elif fun_var.get() == 3:

fun_text = '魂十队员(未完成)'

elif fun_var.get() == 4:

fun_text = '魂十队长(未完成)'

elif fun_var.get() == 5:

fun_text = '狗粮队员(未完成)'

label.config(text='功能选择: ' + fun_text)

rb1 = tk.Radiobutton(window, text='寮突破', font=('微软雅黑', 10),

variable=fun_var, value=1, command=print_selection)

rb1.place(x=15, y=30)

rb2 = tk.Radiobutton(window, text='御灵、业原火', font=('微软雅黑', 10),

variable=fun_var, value=2, command=print_selection)

rb2.place(x=15, y=60)

rb3 = tk.Radiobutton(window, text='魂十队员', font=('微软雅黑', 10),

variable=fun_var, value=3, command=print_selection)

rb3.place(x=15, y=90)

rb4 = tk.Radiobutton(window, text='魂十队长', font=('微软雅黑', 10),

variable=fun_var, value=4, command=print_selection)

rb4.place(x=15, y=120)

rb5 = tk.Radiobutton(window, text='狗粮队员', font=('微软雅黑', 10),

variable=fun_var, value=5, command=print_selection)

rb5.place(x=15, y=150)

# Radiobutton #

开始按钮start_mission()中定义了每一个功能所要执行的函数,注意的是,独立功能需要放在一个线程中执行,不然界面会被阻塞卡死

全局变量is_start用来控制功能的执行与停止

click()函数用来改变按钮显示以及锁定功能选择

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

59

60

61

62

63

64

65

66

67

68

69

70# button start#

rb_list = [rb1, rb2, rb3, rb4, rb5]

button_var = tk.StringVar()

button_var.set('开始')

is_click = False

def start_mission():

global is_start

if fun_var.get() == 1:

text.insert('end', strftime('%H:%M:%S', localtime()) + ' 开始执行寮突破\n')

text.see('end') # 自动显示底部

window_size = get_window_info()

if window_size: # 打开了阴阳师

window.geometry('240x480+%d+%d' % (window_size[0]-240, window_size[1]))

is_start = True

thread1 = threading.Thread(target=liao_tupo, args=(window_size,))

thread1.start()

elif fun_var.get() == 2:

text.insert('end', strftime('%H:%M:%S', localtime()) + ' 开始执行御灵、业原火\n')

text.see('end') # 自动显示底部

window_size = get_window_info()

if window_size: # 打开了阴阳师

window.geometry('240x480+%d+%d' % (window_size[0] - 240, window_size[1]))

is_start = True

thread2 = threading.Thread(target=yu_ling, args=(window_size,))

thread2.start()

elif fun_var.get() == 3:

text.insert('end', strftime('%H:%M:%S', localtime()) + ' 魂十队员功能未开发\n')

text.see('end') # 自动显示底部

elif fun_var.get() == 4:

text.insert('end', strftime('%H:%M:%S', localtime()) + ' 魂十队长功能未开发\n')

text.see('end') # 自动显示底部

elif fun_var.get() == 5:

text.insert('end', strftime('%H:%M:%S', localtime()) + ' 狗粮队员功能未开发\n')

text.see('end') # 自动显示底部

def stop_mission():

global is_start

is_start = False

text.insert('end', strftime('%H:%M:%S', localtime()) + ' 停止执行\n')

text.see('end') # 自动显示底部

def click():

global is_click

if not is_click:

is_click = True

button_var.set('停止')

label.config(text=fun_text + ' 已经开始')

for rb in rb_list: # 将选项锁定

rb.config(state='disabled')

button_adjust.config(state='disabled')

start_mission()

else:

is_click = False

button_var.set('开始')

label.config(text=fun_text + ' 已经停止')

for rb in rb_list:

rb.config(state='active')

button_adjust.config(state='active')

stop_mission()

button = tk.Button(window, textvariable=button_var, width=10,

height=1, command=click)

button.place(x=140, y=60)

# button start#

文本显示1

2

3

4import ScrolledText

text = ScrolledText.ScrolledText(window, width=29, height=17) # 滚动输出文本框

# text = tk.Text(window, width=29, height=17) # 输出文本框

text.place(x=15, y=180)

注意的一点是,再每次输出文本的时候希望自动显示低端,这时需要在insert之后执行text.see('end')

Pyinstaller打包1pyinstaller -F -w -i ./yaodao.ico ./tk_gui.py

-F表示输出单文件exe

-w表示不显示命令行

-i设置图标

更多参数设置详见这里

至此全部搞定,打开exe时记得右键管理员权限打开

Have Fun!

下一篇,简单碎片登记系统(鸽了)

python自动操作脚本_Python实现自动挂机脚本 | 沐雨浥尘相关推荐

  1. python如何做脚本_Python实现自动挂机脚本(基础篇)

    不知不觉肝阴阳师也快一年了,对这游戏真是又爱又恨,最近刚刚发布了PC版,突然很想尝试着写个脚本挂机,话不多说进入正题. 简单的鼠标操作 游戏挂机脚本,无非就是自动移动鼠标,自动点击,进行重复操作,所以 ...

  2. python日常实用小脚本-Python实现自动挂机脚本 | 沐雨浥尘

    使用Python + win32api实现简单自动鼠标点击 使用tkinter设计GUI界面并用pyinstaller打包 不知不觉肝阴阳师也快一年了,对这游戏真是又爱又恨,最近刚刚发布了PC版,突然 ...

  3. python 股票自动交易从零开始_Python股票自动交易从零开始

    课程目录 1.Python股票自动交易从零开始~第一集~简介(Av24528809,P1).mp4 2.Python股票自动交易从零开始~第二集~安装工具包(Av24528809,P2).mp4 3. ...

  4. python自动备份手机_python实现自动备份windows应用数据

    开发这个功能主要是刚开始要备份几台windows服务器的用户数据,后来写到最后就变成了一个数据备份通用工具了,程序可以根据配置文件的配置进行目录数据的备份,只要指定备份的目录,程序会自动调用系统win ...

  5. python 操作oracle 执行脚本_python、abaqus执行脚本路径

    python中获取执行脚本路径方法 1.sys.path[0]:获取执行脚本目录绝对路径 #每次执行脚本时,python会将执行脚本目录加入PYTHONPATH环境变量中(sys.path获取) #! ...

  6. 如何在python中表示微分_Python实现自动微分(Automatic Differentiation)

    什么是自动微分 自动微分(Automatic Differentiation)是什么?微分是函数在某一处的导数值,自动微分就是使用计算机程序自动求解函数在某一处的导数值.自动微分可用于计算神经网络反向 ...

  7. python 魔兽世界升级脚本_Python 实现数据库更新脚本的生成方法

    我在工作的时候,在测试环境下使用的数据库跟生产环境的数据库不一致,当我们的测试环境下的数据库完成测试准备更新到生产环境上的数据库时候,需要准备更新脚本,真是一不小心没记下来就会忘了改了哪里,哪里添加了 ...

  8. python字典操作添加_Python字典常见操作实例小结【定义、添加、删除、遍历】

    本文实例总结了python字典常见操作.分享给大家供大家参考,具体如下: 简单的字典: 字典就是键值对key-value组合. #字典 键值对组合 alien_0 ={'color':'green', ...

  9. python实现app自动签到器_Python实现自动签到脚本功能

    实训课期间忙里偷闲的学习了python的selenium包,唯一一点不好是要自己去查英文文档,明摆着欺负我这种英语不好的,想着用谷歌翻译一下,代码也给我翻译了,不知道是几个意思. 大二的时候就让我们做 ...

最新文章

  1. “新一代人工智能”研究的三大重点方向
  2. SQLAlchemy_定义(一对一/一对多/多对多)关系
  3. 【uva 1617】Laptop(算法效率--贪心,2种理解)
  4. [笔记]极大极小过程的alpha-beta剪枝不可与记忆化搜索一起使用
  5. php合成或者创建gif动画
  6. PHP 怎么随机获取数组里面的值
  7. MySQL查询指定数据库中所有记录不为空的表
  8. 赤手空拳如何成就百万富翁? 赤手空拳如何成就百万富翁?——网络营销之七(第四招:百度文库+)...
  9. php商城项目开发视频_ThinkPHP开发大型商城项目实战视频_ThinkPHP商城开发案例
  10. 烽火通信 c语言 笔试,C语言嵌入式笔试题目及参考答案-烽火通信.doc
  11. DAS、NAS、SAN、ISCSI的区别
  12. Gartner点将分布式文件存储,浪潮存储缘何一鸣惊人?
  13. ag-grid在Vue项目中的基本使用
  14. 2017年总结2018年展望
  15. ARM汇编之kile环境
  16. 抽象基类与接口,共性与个性的选择!(区别)
  17. 移动笔试计算机知识,移动笔试知识点之--计算机类-数据库系统概论复习资料
  18. matlab(simulink)里怎么求一个波形的动态平均值
  19. 【按键精灵学习记录】以DOTA2人机为例
  20. App Store上架流程/苹果app发布流程

热门文章

  1. Codeforces 图论板刷总结(更新中)
  2. 用Python分析经纬度数据
  3. arduino智能浇花系统_基于Arduino单片机的智能浇花器
  4. win10用账户登录计算机,Win10支持两种账户登录,一种是本地账户,另一种是Microsoft账户...
  5. Python全栈开发【基础-09】深浅拷贝+while循环
  6. java版林地府邸种子_我的世界林地府邸地图种子代码分享
  7. GVRP和VTP的比较与区别
  8. 六度拓扑(www.6dtop.com)正式开源啦~~~(V1.0)
  9. Android studio打包之 BuildVariants
  10. 爱奇艺笔试题之成长值计算