前兩天接觸了一個有趣的python模塊——itchat,這個模塊可以非常方便的操作微信,今天就來使用這個模塊來實現微信遠程控制。
環境准備
itchat模塊不是python標准模塊(內置模塊),是一個第三方模塊,需要下載安裝,我們可以在命令行中輸入如下命令安裝:
> pip install itchat --user
注意:pip工具是安裝python時連帶安裝的,不清楚的可以回去看前面環境搭建的教程,或者評論提問。
安裝完后,嘗試導入模塊
>>> import itchat
>>>
沒有報錯,表明安裝成功!
itchat
現在來簡單介紹itchat的用法,僅僅是簡單了解,為實戰做鋪墊,如果需要詳細學習itchat可以問問度娘。
下面這個簡單的例子幾乎囊括了我們要用到itchat的功能
import itchat # 導入itchat模塊
@itchat.msg_register('Text')
def simple(msg):
src = msg['FromUserName'] # 消息的發送方
dst = msg['ToUserName'] # 消息的接受方
text = msg['Text'] # 文本消息
# 字符串可以用+號拼接到一起
send_msg = '從'+src+'到'+dst+':' + text
# 發送消息給文件傳輸助手,消息內容為字符串send_msg
itchat.send(send_msg, 'filehelper')
if __name__ == '__main__':
itchat.auto_login()
itchat.run()
很多地方看注釋就可以懂,但還是有必要解釋一下一些我們沒接觸過的東西。
__name__
首先是,__name__
這個變量。
當文件被當作模塊導入的時候,也就是被import的時候,在這個文件中,這個__name__
變量就是這個文件的名字(不包括后綴.py
),而當這個文件被當作程序直接運行的時候,也就是運行python 文件名.py
的時候,這個變量的值為__main__
,所以if __name__ == '__main__':
的意思是,當文件被執行的時候,運行if后的代碼塊。
為什么會有這樣的做法?
python的很多模塊很多是可以直接當作程序運行的,一般運行的代碼是測試什么的,使用這樣的寫法可以區分當前模塊是作為程序運行,還是作為模塊被導入。
而我在這里這么寫,只是把這個if代碼塊當作程序的入口,看起來更清晰罷了。
消息處理
先忽略上面的函數定義,直接從if代碼塊入手。
運行itchat.auto_login()
,不久之后彈出一個二維碼,使用手機微信掃描二維碼,並確認登錄,程序就完成了微信的登錄。
然后的itchat.run()
里面是一個無限循環(可以在命令行按下Ctrl+C強制結束程序),每當微信有消息的時候,itchat就會調用已注冊的消息處理函數來處理對應的消息。
這個消息處理函數就是我們前面定義的simple函數,這個函數帶有一個裝飾器@itchat.msg_register('Text')
,這個裝飾器就是把simple函數注冊成消息處理函數,參數Text
表示只處理文本消息。
也就是說,每當有文本消息時,itchat.run()里面的代碼就會調用simple函數,並傳入一個參數msg,這個msg其實是一個字典,包含了這則文本消息的發送方id(FromUserName),接受方id(ToUserName),消息內容(Text)等。
發送消息
我們可以通過itchat.send函數,發送消息,這個函數要用到兩個參數,第一個是消息的內容,字符串類型;第二個參數是接受方id(這並不是微信昵稱),filehelper
是一個特殊的id,它指的是微信的文件傳輸助手(登錄后,手機會話窗會出現),就是下圖這個東東:
(⊙ˍ⊙)
我的是英文的,不要介意。不過英文也不是filehelper,這我就不知道了,反正中文顯示的應該是“文件傳輸助手”。
我們的項目就是用手機發送命令給這個文件傳輸助手,讓程序跟據不同的命令來操作電腦。
開始工作
我們創建先一個目錄,目錄名字就叫做WeChatController
,當然,同學們可以起別的名字。這個目錄下面存放的就是我們的python代碼。
流程
介紹一下程序的基本流程:我們設定一些特定的消息內容做為指令,程序接受到發往消息助手的指令的時候,根據不同的指令做出不同的操作,比如發送消息內容為help
,然后我們的程序把幫助信息發回消息助手。
用圖可以這樣表示:
開始編寫代碼
現在開始編寫我們的代碼,在我們創建目錄下新建一個文件命名為main.py
(其它名字也可以)。
先寫好程序的基本框架:
import itchat
@itchat.msg_register('Text')
def handler(msg):
dst = msg['ToUserName'] # 消息接受方
text = msg['Text']
if dst == 'filehelper':
# 發往消息助手的消息就是我們的命令
pass
if __name__ == "__main__":
itchat.auto_login()
itchat.run()
我們只需要一個消息處理函數,就是這個handler,變量text就是我們的消息內容,也是我們的指令,我們根據不同的指令實現不同的操作,下面來實現幫助指令,收到這個指令,程序就把幫助信息發到文件助手,我們就可以在手機上看到了。
實現幫助指令
我們把幫助指令的名字命名為help
,我們只需要判斷收到的消息text是否為字符串'help'
即可,核心代碼如下
---略---
if dst == 'filehelper':
# 發往消息助手的消息就是我們的命令
if text == 'help':
help_msg = '現在支持的命令有:\n help 獲取幫助信息\n'
itchat.send(help_msg, 'filehelper')
---略---
(。・∀・)ノ
這沒什么難度,用if語句判斷一下text的內容,如果是help
就把幫助信息發給消息助手,同學們可以試着運行下,掃碼登錄后用手機向文件助手發送消息help
,可以看到結果。
實現退出程序
程序的運行是個死循環,雖然可以強制終止,但總有那么一點麻煩,如果把終止程序也做成一個指令就好了 ヾ(≧∇≦*)ゝ
好!現在就來實現這個指令!
然而,程序怎么終止?
可以用到python內置的sys模塊有一個函數叫做exit,這個函數可以不帶參數直接調用,所以我們只需要在合適的位置調用這個函數就可以了。
我們給這個指令取個名字,就叫做logout
,中文是登出的意思,登出了程序不就意味着結束了嗎,下面是關鍵代碼(不要忘了導入sys模塊):
---略---
if dst == 'filehelper':
# 發往消息助手的消息就是我們的命令
if text == 'help':
help_msg = '現在支持的命令有:\nhelp 獲取幫助信息\n'
help_msg += 'logout 退出程序\n'
itchat.send(help_msg, 'filehelper')
elif text == 'logout':
itchat.send('退出程序...', 'filehelper')
sys.exit()
---略---
補充:字符串可以用+號拼接到一起,help_msg += 'logout 退出程序\n'
也就是給help_msg的尾部拼接字符串'logout 退出程序\n'
,\n
是換行符,這些在前面的教程中可能沒有提到,關於字符串的操作,下一節詳細介紹。
讀者們運行程序,發送退出指令后會發現,程序報錯,命令行會輸出像下面這樣的信息:
r = replyFn(msg)
File "main.py", line 18, in handler
sys.exit()
SystemExit
除此之外,程序並沒有退出。在這里解釋一下,其實sys.exit()只是發送了一個終止的信號,itchat捕抓了這個信號,不給這個信號發送給系統,阻止我們非正常退出。
嗯,非正常。
不必慌張!itchat提供了一個logout函數讓我們退出微信登錄,itchat退出了微信登錄就自然地退出程序了,讓我們修改程序如下:
---略---
elif text == 'logout':
itchat.send('退出程序...', 'filehelper')
itchat.logout()
---略---
實現執行系統命令
說好的遠程控制電腦,怎么可以什么都干不了?
下面就來實現真正的控制電腦的操作! ヾ(≧∇≦*)ゝ
我們來實現給定指令運行系統命令!
什么是系統命令?就是命令行里運行的那些咯 (ˉ^ ̄~) 切~~
呃,這個……我們不能像遠程桌面那樣直接使用鼠標和鍵盤操作,水平還不足(其實是我不會 ( ̄_, ̄ )
),就實現這個吧。
這個功能對於新手來說,也是有一定的難度的,要多加注意!
<( ̄︶ ̄)↗[GO!]
subprocess模塊
要執行系統命令,可以用到subprocess模塊,這個模塊也是python內置的,我們只需要使用其中一個check_output函數就可以了,這個函數有挺多參數的,但是必要的參數不多,這里我給出我們的用法:
try:
result = subprocess.check_output(cmd, shell=True,timeout=2)
result = result.decode()
except:
itchat.send('發生錯誤了!','filehelper')
這里接觸到后面教程的內容,這里先解釋一下,這個try是一個代碼塊,會捕捉程序中發生的錯誤(稱為異常),如果捕捉到異常就會執行except代碼塊,同學們先用着先就好了。
上面的函數調用中,cmd就是我們要執行的系統命令的字符串,shell=True
不用管,timeout表示超時時間,單位為秒,超時會發生異常,其次,程序的運行輸出會做為返回值返回。
這個返回值是字節串類型的,這是python的一種基本數據類型,之前的教程沒有提到,不過不重要,我們只要知道使用result.decode()
就可以把字節串解碼成字符串就夠了。
從消息中分離命令
至於要執行的命令怎么獲取,又要接觸到后面的知識了,字符串、數字、列表等類型的值都是一個 “對象”,對象都有自己的 “方法”,方法其實就是函數,不過這種函數的調用方式不太一樣:
對象.方法名()
這有點像調用模塊內的函數一樣,其實上面的result.decode()
就是調用了一個方法。
我們要用到字符串的一個方法:split
split用於分割字符串,指定一個分隔符,字符串就會以這個分隔符從左往右分割成多個字符串,然后一列表返回,比如以空格分割'hello world'
:
>>> 'hello world!'.split(' ')
['hello', 'world!']
>>>
我們還可以指定一個參數表明最大分割的次數。注意!是分割的次數,不是分割成多少個字符串!
>>> '1 2 3 4'.split(' ', 1)
['1', '2 3 4']
>>>
那么,我們只需要在發送消息的時候用以下格式:
指令 其它內容
(。・∀・)ノ゙
然后使用split分割開來就可以了!
改動程序框架
在這里我們順便改變一下程序的基本框架,下面說下原因。
我們每次添加命令都要改動help指令的輸出(help_msg),有點麻煩,不如我們把每個指令的處理過程寫成一個函數,然后在函數的開頭加上字符串,這個字符串會成為函數對象(沒錯!函數也是一種對象)的__doc__
屬性,之后我們的help指令把所有處理函數的這個屬性作為幫助信息發給文件助手就可以了!
對此,我干脆來了一個大“整改”,我們把每個指令的處理函數都定義在文件handlers.py
中,然后main.py
的程序框架就是這樣的:
import itchat
import handlers # 導入我們寫的模塊
@itchat.msg_register('Text')
def handler(msg):
dst = msg['ToUserName']
text = msg['Text']
if dst == 'filehelper':
text = text.split(' ', 1) # 以空格分開
cmd = text[0] # 指令的名字
if hasattr(handlers, 'do_'+cmd):
func = getattr(handlers, 'do_'+cmd)
func(text)
if __name__ == '__main__':
itchat.auto_login()
itchat.run()
上面又出現了新的函數,hasattr和getattr。
hasattr用來查詢某個對象是否有某個屬性,第一個參數就是這個對象,這里是我們自己寫的handlers模塊,也就是handlers.py,模塊也是對象哦!,第二個參數就是屬性,這里查找的屬性就是我們的處理函數,我們的處理函數統一命名為下面這種格式:
do_指令名
例:
do_help
do_logout
getattr用來獲取對象的屬性,利用這個函數,我們就可以獲取相應的函數賦值給變量func,然后調用func(text)
。
與此同時,我們在handlers.py定義的help指令處理函數如下:
def do_help(arg):
'輸出幫助信息'
# 這個arg參數占位用
obj = globals() # 獲取全局命名空間
help_msg = '支持的指令有:\n'
for name in obj:
# 遍歷所有對象
if name.startswith('do_'):
# startswith方法用於判斷字符串是否以所給字符串開頭
command = name.strip('do_') # 去除字符串中的'do_'
help_msg += command + ' ' + obj[name].__doc__ + '\n'
itchat.send(help_msg, 'filehelper')
上面的代碼用上了兩個新的字符串對象的方法,看看注釋就好了。
讀者們自己加上do_logout函數吧,這里就不多羅嗦了(記得導入itchat模塊!!!)
最終實現
做了這么多鋪墊,可能大家也忘了我們要干什么了。我們要實現執行系統命令!
現在我們只需在handlers.py文件中添加一個函數就好了:
def do_exec(arg):
'執行系統命令'
cmd = arg[1] # 命令
try:
result = subprocess.check_output(cmd, shell=True, timeout=2)
result = result.decode()
except:
result = '執行命令出錯了!'
itchat.send(result, 'filehelper')
最后不要忘了在handlers.py文件中加上導入相關模塊的語句
運行演示
好了,我們的項目就這樣完成了,現在給出幾張運行時的圖片。
這是程序運行的輸出畫面,此時正在等待掃描二維碼
登錄成功
此時手機發送指令
總結
程序的完整代碼上傳到github上了,地址是:https://github.com/featherL/WeChatController/tree/master
這一節我們不僅把之前幾節的內容綜合起來,做了一個完整的項目,而且了解了很多后面探討的內容。對於這些“超綱”內容,某些地方不太懂也沒關系,等學到了的時候,再回頭看,你會發現是如此的簡單。
如有不懂,歡迎評論,或者郵件聯系我 1343145150@qq.com