python實現QQ機器人(自己主動登錄,獲取群消息,發送群消息)
一次偶然的機會我看見了一個群裏的一個QQ號總是依據你所發的消息自己主動回復,當時非常感覺到奇妙。我知道能夠模擬登錄站點,沒想到居然也能模擬登錄QQ,首先自己想到的就是怎樣實現模擬登錄PC端的QQ, 開始研究了下,發現QQ所發送的包都非常難理解。
於是就轉到了網頁版的QQ,由於在網頁裏能夠捕獲全部的請求從而更好的實現模擬功能!
首先第一步就是打開網頁版的qq。打開瀏覽器的開發人員模式 這裏能夠監控全部的請求!
打開登錄界面的的時候
會捕捉到一個GET請求
https://ssl.ptlogin2.qq.com/check?uin=10588690&appid=1003903&js_ver=10080&js_type=0&login_sig=YW1ZUUsIU*7FepsR1blgEgcSVWeHCrNVVquTT1LZ0paOxZ-6xHtypEqNGoo-VELQ&u1=http%3A%2F%2Fweb2.qq.com%2Floginproxy.html&r=0.5928007187321782
當中uin是你的QQ號
返回值是 ptui_checkVC(‘1‘,‘AAr4bdjMeh2hEa77PTuoHhqMTxbRqOp3‘,‘\x00\x00\x00\x00\x00\xa1\x92\x12‘);
當中1表示須要驗證碼 另一種返回值 ptui_checkVC(‘0‘,‘!LJV‘,‘\x00\x00\x00\x00\x00\xa1\x92\x12‘) 這樣的表示是不須要的驗證碼的
def CheckVerify(self,uin): check="https://ssl.ptlogin2.qq.com/check?uin={uin}&appid=1003903&js_ver=10080&js_type=0&login_sig=YPD0P*wu2n8vW1OS2f7VfzvPf3Ku5vnkP4nzImF0GybR02fsKZdjGYB7f9R7nQRn&u1=http%3A%2F%2Fweb2.qq.com%2Floginproxy.html&r=0.8179273759014904" check=check.replace(‘{uin}‘,uin) pattern=re.compile("ptui_checkVC\(‘(.*)‘,‘(.*)‘,‘(.*)‘\);") result=self.Get(check) checked= pattern.search(result).groups() print ‘Step1: CheckVerify‘ return checked
獲取驗證碼的方法
def GetVerify(self): #url = ‘https://ssl.captcha.qq.com/getimage?&uin=‘+str(self.QQ)+‘&aid=1002101&0.45644426648505‘ + str(random.randint(10,99)) verify="https://ssl.captcha.qq.com/getimage?aid=1003903&r=0.6472875226754695&uin={QQ}&cap_cd=aSD-ZVcNEcozlZUurhNYhp-MBHf4hjbJ" verify=verify.replace(‘{QQ}‘,self.QQ) path= r"c:/verify/1.jpg" #data = urllib.urlretrieve(url,path) data = urllib2.urlopen(verify) localPic =open(r"c:/verify/1.jpg",‘wb‘) localPic.write(data.read()) localPic.close() data.close()
輸入username和password 還有驗證碼後發送一個GET請求
https://ssl.ptlogin2.qq.com/login?
u=10588690&p=AB80CD3B6429D9660878E93058DD78BD&verifycode=TEYX&webqq_type=10&remember_uin=1&login2qq=1&aid=1003903&u1=http%3A%2F%2Fweb2.qq.com%2Floginproxy.html%3Flogin2qq%3D1%26webqq_type%3D10&h=1&ptredirect=0&ptlang=2052&daid=164&from_ui=1&pttype=1&dumy=&fp=loginerroralert&action=6-14-296574&mibao_css=m_webqq&t=1&g=1&js_type=0&js_ver=10080&login_sig=YW1ZUUsIU*7FepsR1blgEgcSVWeHCrNVVquTT1LZ0paOxZ-6xHtypEqNGoo-VELQ&pt_uistyle=5
這裏面u代表賬號 p代表password password是經過一定算法加密的 verify是驗證碼
加密算法例如以下
def PasswordSecret(self,password,v1,v2,md5=True): if md5==True: password=self.PCMd5(password).upper() length=len(password) temp=‘‘ for i in range(0,length,2): temp+=r‘\x‘+password[i:i+2] return self.PCMd5(self.PCMd5(self.hex2asc(temp)+self.hex2asc(v2)).upper()+v1).upper() #md5加密函數 def PCMd5(self,s): h=hashlib.md5() h.update(s) return h.hexdigest() #16進制轉字符 def hex2asc(self,s): _str="".join(s.split(r‘\x‘)) length=len(_str) data=‘‘ for i in range(0,length,2): data+=chr(int(_str[i:i+2],16)) return data
然後是登錄部分代碼
def Login(self,uin,pwd): #獲取參數 cheked=self.CheckVerify(uin) #加密password #pwd=self.PasswordSecret(pwd,cheked[1],cheked[2]) #pwd=self.PasswordSecret(pwd,r‘AAST‘,r‘\x00\x00\x00\x00\x00\xa1\x92\x12‘) loginurl="https://ssl.ptlogin2.qq.com/login?u={uin}&p={pwd}&verifycode={verify}&webqq_type=10&remember_uin=1&login2qq=1&aid=1003903&u1=http%3A%2F%2Fweb2.qq.com%2Floginproxy.html%3Flogin2qq%3D1%26webqq_type%3D10&h=1&ptredirect=0&ptlang=2052&daid=164&from_ui=1&pttype=1&dumy=&fp=loginerroralert&action=4-30-135914&mibao_css=m_webqq&t=1&g=1&js_type=0&js_ver=10080&login_sig=YPD0P*wu2n8vW1OS2f7VfzvPf3Ku5vnkP4nzImF0GybR02fsKZdjGYB7f9R7nQRn&pt_uistyle=5" loginurl=loginurl.replace(‘{uin}‘,uin) #loginurl=loginurl.replace(‘{pwd}‘,pwd) #loginurl=loginurl.replace(‘{verify}‘,cheked[1]) #result=Get(loginurl) if(cheked[0]=="1"): #下載驗證碼 self.GetVerify() image = Image.open(r"c:/verify/1.jpg") image.show() code=raw_input("verifycode:").strip() loginurl=loginurl.replace(‘{verify}‘,code.upper()) pwd=self.PasswordSecret(pwd,r‘‘+code.upper(),cheked[2]) #pwd=self.PasswordSecret(pwd,cheked[1],cheked[2]) else: loginurl=loginurl.replace(‘{verify}‘,cheked[1]) pwd=self.PasswordSecret(pwd,cheked[1],cheked[2]) loginurl=loginurl.replace(‘{pwd}‘,pwd) result=self.Get(loginurl,‘ssl.ptlogin2.qq.com‘,‘https://ui.ptlogin2.qq.com/cgi-bin/login?daid=164&target=self&style=5&mibao_css=m_webqq&appid=1003903&enable_qlogin=0&no_verifyimg=1&s_url=http%3A%2F%2Fweb2.qq.com%2Floginproxy.html&f_url=loginerroralert&strong_login=1&login_state=10&t=20140514001‘,None) print ‘Step2: Login‘ pattern=re.compile("ptuiCB\(‘(.*)‘,‘(.*)‘,‘(.*)‘,‘(.*)‘,‘(.*)‘,\s‘(.*)‘\);") ret= pattern.search(result).groups() #獲取必要的cookie 否則第二次登陸會出錯 self.Get(ret[2]) print ‘Step3: GetCookie‘ for c in self.cj: if c.name=="ptwebqq": self.ptwebqq=c.value return result
登錄成功後server會返回一串json數據
ptuiCB(‘0‘,‘0‘,‘http://ptlogin4.web2.qq.com/check_sig?pttype=1&uin=10588690&service=login&nodirect=0&ptsig=*ZwU0pfTmYP93Fbdt6uSzbbODzlZ0EY9g25PDge5zZU_&s_url=http%3A%2F%2Fweb2.qq.com%2Floginproxy.html%3Flogin2qq%3D1%26webqq_type%3D10&f_url=&ptlang=2052&ptredirect=100&aid=1003903&daid=164&j_later=0&low_login_hour=0®master=0&pt_login_type=1&pt_aid=0&pt_aaid=0&pt_light=0‘,‘0‘,‘登錄成功!
‘, ‘小竹‘);
第一個為0 就表示登錄成功了 ,可是這並沒有真正的登錄成功
上面的返回值中的url是用來獲取一個關鍵cookie的 那就是ptwebqq
然後進行第二次登錄,這次才是真正的登錄
http://d.web2.qq.com/channel/login2
請求例如以下
當中的ptwebqq就是剛才我們從cookie中獲取的
這部分代碼是
def Login2(self): try: url="http://d.web2.qq.com/channel/login2" postdata="r=%7B%22status%22%3A%22online%22%2C%22ptwebqq%22%3A%22{$ptwebqq}%22%2C%22passwd_sig%22%3A%22%22%2C%22clientid%22%3A%22{$clientid}%22%2C%22psessionid%22%3Anull%7D&clientid={$clientid}&psessionid=null" postdata=postdata.replace("{$ptwebqq}",self.ptwebqq) postdata=postdata.replace("{$clientid}",str(self.clientid)) print ‘Step4: Login2‘ result=self.Post(url,postdata,QQRobot.HOST[0],QQRobot.REFERER[0],QQRobot.ORIGIN[0]) retjson=json.loads(result) retjson=retjson["result"] return retjson except Exception,e: print "Login2 error "+str(e)
第二次登陸成功後會返回一個
‘‘‘{"retcode":0,
"result":{
"uin":10588690,
"cip":1707901841,
"index":1075,
"port":59571,
"status":"online",
"vfwebqq":"c043f1f6ce5c3b76a4603ab60082668bef2dde0b987808f728e2071eb7c164eeb30fcd85c31018d2",
"psessionid":"8368046764001d636f6e6e7365727665725f77656271714031302e3133392e372e31363000006cb000001ae1036200a192126d0000000a40356c593742635175316d00000028c043f1f6ce5c3b76a4603ab60082668bef2dde0b987808f728e2071eb7c164eeb30fcd85c31018d2",
"user_state":0,
"f":0
}
}‘‘‘
這種數據結構 當中0表示登陸成功
須要把這寫數據保存下來 後面進行操作須要
登陸成功後我們就能夠拉去群列表了
#獲取群列表信息 def GetGroupNameList(self,vfwebqq): try: url="http://s.web2.qq.com/api/get_group_name_list_mask2" postdata="r=%7B%22vfwebqq%22%3A%22{$vfwebqq}%22%7D" postdata=postdata.replace("{$vfwebqq}",vfwebqq) ret=self.Post(url,postdata,QQRobot.HOST[1],QQRobot.REFERER[1],QQRobot.ORIGIN[1]) print ‘Step5: GetGroupList‘ retjson=json.loads(ret) retjson=retjson["result"] self.grouplist=retjson for group in self.grouplist[‘gnamelist‘]: print group["code"],group["name"] except Exception,e: print "GetGroupNameList error"+str(e)
#獲取群成員信息 def GetGroupInfo(self,gcode,vfwebqq): try: url="http://s.web2.qq.com/api/get_group_info_ext2?gcode={$gcode}&cb=undefined&vfwebqq={$vfwebqq}&t=1402069438458" url=url.replace("{$vfwebqq}",vfwebqq) url=url.replace("{$gcode}",str(gcode)) ret=self.Get(url,QQRobot.HOST[1],QQRobot.REFERER[1],None) print "Step6: GetGroupInfo" retjson=json.loads(ret) retjson=retjson["result"] self.groupuserlist=retjson except Exception,e: print "GetGroupInfo error"+str(e)
#發送群消息 def SendGroupMsg(self,groupid,msg,psessionid): try: #msg=u">:"+msg #msg=msg.strip() #urlmsg=quote(msg.encode(‘utf8‘)) #把普通字符串包裹起來 stype="%5C%22{content}%5C%22" temp="" part="" for c in msg: if type(c) is types.ListType: part=quote(str(c).strip().encode(‘utf8‘))+"%2C" #part=part.replace("%20","") part=part.replace("%27","%5C%22") #把 ‘ 換為 \" part=part.replace("u","") #把 u 換為 空 temp+=part else: temp+=stype.replace("{content}",quote(c.encode(‘utf8‘)))+"%2C" temp=temp[0:len(temp)-3] #urlmsg="%5C%228%5C%22"#"%5B%5C%22face%5C%22%2C13%5D" urlmsg=temp url="http://d.web2.qq.com/channel/send_qun_msg2" msg_id = 77860003 #postdata="r=%7B%22group_uin%22%3A{$group_uin}%2C%22content%22%3A%22%5B%5C%22{$msg}%5C%22%2C%5C%22%5C%22%2C%5B%5C%22font%5C%22%2C%7B%5C%22name%5C%22%3A%5C%22%E5%AE%8B%E4%BD%93%5C%22%2C%5C%22size%5C%22%3A%5C%2210%5C%22%2C%5C%22style%5C%22%3A%5B0%2C0%2C0%5D%2C%5C%22color%5C%22%3A%5C%22000000%5C%22%7D%5D%5D%22%2C%22msg_id%22%3A{$msg_id}%2C%22clientid%22%3A%22{$clientid}%22%2C%22psessionid%22%3A%22{$psessionid}%22%7D&clientid={$clientid}&psessionid={$psessionid}" #表情 #postdata="r=%7B%22group_uin%22%3A{$group_uin}%2C%22content%22%3A%22%5B{$msg}%2C%5C%22%5C%5Cn%5C%22%2C%5B%5C%22font%5C%22%2C%7B%5C%22name%5C%22%3A%5C%22%E5%AE%8B%E4%BD%93%5C%22%2C%5C%22size%5C%22%3A%5C%2210%5C%22%2C%5C%22style%5C%22%3A%5B0%2C0%2C0%5D%2C%5C%22color%5C%22%3A%5C%22000000%5C%22%7D%5D%5D%22%2C%22msg_id%22%3A{$msg_id}%2C%22clientid%22%3A%22{$clientid}%22%2C%22psessionid%22%3A%22{$psessionid}%22%7D&clientid={$clientid}&psessionid={$psessionid}" #字符 postdata="r=%7B%22group_uin%22%3A{$group_uin}%2C%22content%22%3A%22%5B{$msg}%2C%5C%22%5C%22%2C%5B%5C%22font%5C%22%2C%7B%5C%22name%5C%22%3A%5C%22%E5%AE%8B%E4%BD%93%5C%22%2C%5C%22size%5C%22%3A%5C%2210%5C%22%2C%5C%22style%5C%22%3A%5B0%2C0%2C0%5D%2C%5C%22color%5C%22%3A%5C%22000000%5C%22%7D%5D%5D%22%2C%22msg_id%22%3A{$msg_id}%2C%22clientid%22%3A%22{$clientid}%22%2C%22psessionid%22%3A%22{$psessionid}%22%7D&clientid={$clientid}&psessionid={$psessionid}" postdata=postdata.replace("{$group_uin}",str(groupid)) postdata=postdata.replace("{$psessionid}",psessionid) postdata=postdata.replace("{$clientid}",str(self.clientid)) postdata=postdata.replace("{$msg_id}",str(msg_id)) postdata=postdata.replace("{$msg}",urlmsg) self.Post(url,postdata,QQRobot.HOST[0],QQRobot.REFERER[0],QQRobot.ORIGIN[0]) except Exception,e: print "SendGroupMsg error"+str(e) #print "send msg: "+str(msg)
心跳就是每隔一定時間向server請求一次 證明自己還在!
#心跳包 def HeartBreak(self,psessionid): url="http://d.web2.qq.com/channel/poll2" postdata="r=%7B%22clientid%22%3A%22{$clientid}%22%2C%22psessionid%22%3A%22{$psessionid}%22%2C%22key%22%3A0%2C%22ids%22%3A%5B%5D%7D&clientid={$clientid}&psessionid={$psessionid}" postdata=postdata.replace("{$clientid}",str(self.clientid)) postdata=postdata.replace("{$psessionid}",psessionid) while True: #每隔2秒發送心跳包 ret=self.Post(url,postdata,QQRobot.HOST[0],QQRobot.REFERER[0],QQRobot.ORIGIN[0]) try: retjson=json.loads(ret) retjson=retjson["result"] retjson=retjson[0] #print "heartbreak" if(retjson["poll_type"]=="group_message"): msg=retjson["value"] self.ProcessMsg(msg) except Exception,e: #print "HeartBreak error "+str(e) pass time.sleep(2)
項目下載地址
http://download.csdn.net/detail/zhujunxxxxx/7663953
明天繼續更新。
。。。。
。
歡迎大家關註我的個人站點 http://www.zhujuncoding.com
python實現QQ機器人(自己主動登錄,獲取群消息,發送群消息)