0%

python写一个快速搜索脚本

出于我是面向搜索引擎编程的选手的缘故,在平时的工作中深感搜索不够便捷,于是想实现一下利用快捷键来搜索剪切板上的内容。于是用上平时划水的时间,用python写了一个在后台运行的脚本。
本着尽量简洁的想法,我会尽量减少额外的python库的引入,也就是尽量减少pip操作,一来是依赖太多不方便别人配置,二来是封装好的库大多功能冗余。
下面讲讲实现。代码放在这里

首先从最核心的功能开始,也就是我最迫切想要的功能,快捷键搜索。网上很多实现都是通过pyhook这个包,但是其实没有必要,因为python自带的包里面就已经支持了对windows api的调用。
明确一下需求,我们需要:读写剪切板打开搜索引擎添加快捷键这三个核心功能。于是:

1
2
3
import win32clipboard as w
import win32con
import webbrowser

然后我们可以愉快地写函数了,首先从剪切板获得数据:

1
2
3
4
5
6
7
8
9
10
11
12
def get_text():
'''
@description: read from clipbroad
@param {None}
@return: str
'''
w.OpenClipboard()
#CF_TEXT will cause a wrong code while clipping Chinese characters
t = w.GetClipboardData(win32con.CF_UNICODETEXT)
w .CloseClipboard()
print(type(t))
return t

获得数据后,只需要把数据添加上搜索引擎的url格式,这里以百度为例:

1
2
keyword = get_text()
webbrowser.open("http://www.baidu.com/s?wd="+keyword)

这样初步任务就完成了,接下来是要设置快捷键,这也是相对麻烦的一点,因为不想使用pyhook。于是参考了一篇博客,是用的双线程 + Win32 api的方法,比较old school也比较简洁。
首先引入必要的库,来实现多线程以及Windows api的操作:

1
2
3
import ctypes
import ctypes.wintypes
import threading

接下来的思路就是创建两个独立循环的线程,一个负责注册快捷键以及不停监听键盘按键,另一个负责运行我们上面写好的函数。Windows中的user32.dll包含了注册热键的函数RegisterHotKey()以及解注册函数UnregisterHotKey(),利用这些我们可以绑定我们想要的按键组合。利用ctypes.wintypes.MSG()我们可以获得按键信息。要注意的是为了防止下次运行程序时出问题,我们要在程序结束的时候对绑定的热键进行解绑。
于是建立一个类继承自Thread线程类:

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
class Hotkey(threading.Thread):  #Create Thread.threading Class

def run(self):
'''
@description: define hotkey and keep listening
@param {None}
@return: None
'''
user32 = ctypes.windll.user32 # load user32.dll
global EXIT
global RUN
hkid1 = 99 # hotkey id for identify
hkid2 = 100
user32.UnregisterHotKey(None, hkid1)# Unregister HotKey, otherwise next time register will fail
user32.UnregisterHotKey(None, hkid2)

if not user32.RegisterHotKey(None, hkid1, win32con.MOD_ALT, 66): # alt+b for search
print ("Unable to register id", hkid1) # if fail to register

if not user32.RegisterHotKey(None, hkid2, win32con.MOD_ALT, 67): # alt+c for exit
print ("Unable to register id", hkid2)

# listening for hotkey press
try:
msg = ctypes.wintypes.MSG()

while True:
if user32.GetMessageA(ctypes.byref(msg), None, 0, 0) != 0:

if msg.message == win32con.WM_HOTKEY:
if msg.wParam == hkid1:

RUN = True
elif msg.wParam == hkid2:

EXIT=True
return

user32.TranslateMessage(ctypes.byref(msg))
user32.DispatchMessageA(ctypes.byref(msg))

finally:
user32.UnregisterHotKey(None, hkid1)#Unregister HotKey, otherwise next time register will fail
user32.UnregisterHotKey(None, hkid2)

之后在主线程里,我们判断另一个线程传来的全局信号,决定是要执行搜索还是退出程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
hotkey = Hotkey()
hotkey.start()

while(True):
time.sleep(0.1)
if RUN==True:
keyword = get_text()
webbrowser.open("http://www.baidu.com/s?wd="+keyword)
RUN=False

elif EXIT==True:
# end
exit(0)

到此,大致功能就完成啦,为了在Windows下也能方便地运行python,我们把.py文件改成.pyw后缀,就可以直接双击运行而不需要在命令行执行了。用任务管理器我们就可以看到程序在后台欢快地运行着了。但是不看还好,一看吓一跳啊,这CPU占用率4%,比我开着的VS2015和Chrome还要高,这这这这可咋办,线程优化我不懂啊。本来以为是个很硬核的问题,但其实用脚趾头想也能知道问题的根源就是那两个死循环。无解吗?当然不是,甚至解法极其简单。想想每个死循环就是每时每刻都在占用着CPU资源,我们想办法让它歇一歇,资源不就让出来了吗。那怎么让出资源呢?聪明的学过操作系统的朋友可能就想到了,sleep()啊,python自带的time模块里就有这个功能,我们在每个循环里加入一次time.sleep(0.1),本来还担心0.1s的休息可能作用不大,结果一看后台发现CPU占用直接降到正常水平。
至此,就已经可以满足我个人的操作需求了。其实利用上面提到的这些功能,不难去做一些更有意思的东西和其他一些个性化的骚操作,比如游戏外挂
后续,可能我会接着实现一些UI上的以及加入其他一些option比如选择默认搜索引擎之类的features,然后再想想怎么可以让别人也能方便地使用这个脚本,是打包成exe,或者是用c#重写一遍,当然这是后话了