首先简单说下实现需要的一些模块和基本的思路
1、web smartQQ 提交请求的时候,部分数据需要通过加密后提交,基本上是使用mqcomm.js和mq.js进行加密,
2、使用python,由于不想自己重新实现加密的过程,选择使用了Pyv8的模块,利用web qq 本身的js文件获取加密的函数。
第一部分:登陆webQQ
登陆之前,需要获得相关的verify和puin主要是用于加密密码,verifysession则是用于登陆使用
Vcdic = getCheckVC(uin,login_sig)
def getCheckVC(u,login_sig): #主要是获得puin,verity #设置提交的数据 # #ReUrl:是Get请求的地址 ReUrl='https://ssl.ptlogin2.qq.com/check' #出来uin和login_sig以后其它的都是网页抓取的,貌似是固定的值 checkData=[('uin',u), ('appid','501004106'), ('js_ver','10092'), ('js_type','0'), ('login_sig',login_sig), ('u1','http://w.qq.com/proxy.html'), ('r',0.70823077801615), ] checkData = urllib.urlencode(checkData) ReUrl +='?' + checkData req = urllib2.Request(ReUrl,headers=headersBase) ret = urllib2.urlopen(req) strVc=ret.read() #n:指示是否需要验证码 #--下面的几个参数均是用于密码的加密 #verify: #puin: #versession: VcList = re.findall(r'\(.+\)',strVc) #这里通过的是字典的方式获取,用json处理的时候因为字符的问题报错 vcdic = dict() vctup ='' exec('vctup ='+VcList[0]) vcdic['n']=vctup[0] vcdic['verify']=vctup[1] vcdic['puin'] = vctup[2] vcdic['versession'] = vctup[3] #返回一个包含上面参数的字典 if vcdic['n'] !='0': print u'需要验证码 ,等会再连' sys.exit() #否则设置下面的值 global verify,verifysession,puin verify = vcdic['verify'] verifysession = vcdic['versession'] pstr = vcdic['puin'] #需要对puin进行解码,不然传入pyv8模块进行处理的时候会出现问题,导致加密的密码值不正确 puin = pstr.decode('latin1')
然后获得了puin和verify的值,然后进行加密QQ密码
def getpass(apwd,averify,apuin,aString): n=apwd puin=apuin verify=averify puin = getStr(puin) #参考mqcomm.js的加密过程进行加密 n=md55(n,aString) j = hexchar2bin(n,aString) h = md55(j+puin,aString) h = h+verify g = md55(h,aString) return g
最后获得加密后的g
登陆webQQ 主要有三部分:
一、通过Get的方式向logUrl ='https://ssl.ptlogin2.qq.com/login'提交数据
主要是u:你的QQ号 p:加密过后的密码,pt_verifysession:上面获得的verify
二、通过上一步获得返回的数据的ptLogUrl,访问该url,目的是获取完整的cookie
三、获取一些用于后面进行发送消息,接受消息及获得好友列表之类需要的一些值
def logWebqq(u,p,verifycode,pt_verifysession_v1,login_sig): global opener,ptwebqq,vfwebqq,psessionid,uin logUrl ='https://ssl.ptlogin2.qq.com/login' logData =[ ('u',u), ('p',p), ('verifycode',verifycode), ('webqq_type','10'), ('remember_uin','1'), ('login2qq','1'), ('aid','501004106'), ('u1','http://w.qq.com/proxy.html?login2qq=1&webqq_type=10'), ('h','1'), ('ptredirect','0'), ('ptlang','2052'), ('daid','164'), ('from_ui','1'), ('pttype','1'), ('dumy',''), ('fp','loginerroralert'), ('action','0-11-12521'), ('mibao_css','m_webqq'), ('t','1'), ('g','1'), ('js_type','0'), ('js_ver','10092'), ('login_sig',login_sig), ('pt_vcode_v1','0'), ('pt_verifysession_v1',pt_verifysession_v1), ] logData = urllib.urlencode(logData) #Get的方式进行第一次登录,获取初步的cookie的一些值 logUrl +='?'+logData req = urllib2.Request(logUrl,headers=headersBase) logtext = opener.open(req) logtext =logtext.read() #获取logtext里面的url,这个url是为下面获取完整cookie进行使用 #获取ptwebqq的值 k = re.findall('ptuiCB(.+);',logtext) logmess ='' exec('logmess ='+k[0]) ptLogUrl = logmess[2] #主要是用于进一步获取cookie的值,这里才是真正的登录 opener.open(ptLogUrl) #从cookie获取ptwebqq的值 for i in ck: if i.name == 'ptwebqq': ptwebqq = i.value break #Get方式获取http://s.web2.qq.com/api/getvfwebqq数据 #sWebHeader:在头添加相关的信息 #sData:提交的数据 sWebHeader = headersBase sWebHeader['Referer'] = 'http://s.web2.qq.com/proxy.html?v=20130916001&callback=1&id=1' sData =[ ('ptwebqq','%s' % ptwebqq), ('clientid','53999199'), ('psessionid',''), ('t',t), ] sData = urllib.urlencode(sData) sDataUrl = 'http://s.web2.qq.com/api/getvfwebqq' + '?'+sData req = urllib2.Request(sDataUrl,headers=sWebHeader) r = opener.open(req) dataReq = r.read() #获取vfwebqq的值,用于后面进行好友列表的请求 dataReq = json.loads(dataReq) vfwebqq = dataReq['result']['vfwebqq'] #Post获取:http://d.web2.qq.com/channel/login2的数据 #这里使用和上面一样的header dWebData = [('r',"""{"ptwebqq":"%s","clientid":53999199,"psessionid":"","status":"online"}""" % ptwebqq)] dWebData = urllib.urlencode(dWebData) req = urllib2.Request('http://d.web2.qq.com/channel/login2',headers=sWebHeader,data=dWebData) #获得pseesionid的值 r = opener.open(req) r = r.read() dWebDict ={} exec('dWebDict = '+r) psessionid = dWebDict['result']['psessionid'] #uin = dWebDict['result']['uin'] vfwebqq =vfwebqq or dWebDict['result']['vfwebqq']
获取好友列表,这里要注意请求头,基本上都是从网页上面抓取的。
#这里只是做了一个简单的测试,实际上拆分为返回好友列表和查询nickname会比较好些
#获取好友的情况列表,返回指定nickname的uin,用于发送消息使用 def getFriendUin(nickname): #获取好友的列表 #global ptwebqq uinStr = str(uin) hash = hashValue(uinStr,ptwebqq) friendHeaders = headersBase #修改请求头的内容 friendHeaders['Origin'] = 'http://s.web2.qq.com' friendHeaders['Host'] = 's.web2.qq.com' friendHeaders['Referer'] = 'http://s.web2.qq.com/proxy.html?v=20130916001&callback=1&id=1' #friendHeaders['Connection'] = 'keep-alive' friendUrl = 'http://s.web2.qq.com/api/get_user_friends2' friendStr = """{"vfwebqq":"%s","hash":"%s"}""" % (vfwebqq,hash) friendData =[('r',friendStr)] friendData = urllib.urlencode(friendData) freq = urllib2.Request(friendUrl,headers=friendHeaders) friendUin = opener.open(freq,data=friendData) friendUin2 = friendUin.read() #使用json处理数据 friendUin = json.loads(friendUin2) #循环查找对应nickname的uin 用于到时发生消息 for i in friendUin['result']['info']: if i['nick'] == nickname: nickuin = i['uin'] print nickuin return str(nickuin)
获取消息比较简单,主要是注意请求头,
def getMessage(): #ptwebqq,psessionid均为前面获取的值 #构建pollHeaders请求头 #Post方式提交数据 #获取消息的URL:http://d.web2.qq.com/channel/poll2 #重新获取 #global opener pollUrl = 'http://d.web2.qq.com/channel/poll2' pollHeaders = headersBase pollHeaders['Origin'] = 'http://d.web2.qq.com' pollHeaders['Host'] = 'd.web2.qq.com' pollStr = """{"ptwebqq":"%s","clientid":53999199,"psessionid":"%s","key":""}""" %(ptwebqq,psessionid) pollData = [('r',pollStr)] pollData = urllib.urlencode(pollData) #Post:发送消息的headers,和消息提交的数据 #poST:获取好友的列表:http://s.web2.qq.com/api/get_user_friends2 SmessHeaders = headersBase SmessHeaders['Origin'] = 'http://d.web2.qq.com' SmessHeaders['Host'] = 'd.web2.qq.com' SmessHeaders['Referer'] = 'http://d.web2.qq.com/proxy.html?v=20130916001&callback=1&id=2' #'http://s.web2.qq.com/proxy.html?v=20130916001&callback=1&id=1' req = urllib2.Request(pollUrl,headers=pollHeaders) #opener.open(req,data=pollData) print u"获取聊天内容中....." print u'Start--------------------------------------------------------' try: r = opener.open(req,data=pollData) context = r.read() #print context print u'--------------华丽的分割线-------------------------------' print context context = json.loads(context) print u'\t',context['result'][0]['value']['content'][1] print u'\n'*1 print u'-------'*20 time.sleep(0.5) #主要有两种错误一种是超时导致的,一种是消息太长了 无法获取到 except KeyError,e: print u"获取出错" pass except Exception,e: print e pass
发送消息,这个比较麻烦,在发送消息的时候需要先获取消息,并且不能收到消息。
这里只实现发消息的函数
#发送消息的URL:http://d.web2.qq.com/channel/send_buddy_msg2 def sendMessage(nickuin,mess): sendHeaders = headersBase sendHeaders['Accept'] = r"*/*" sendHeaders['Origin'] = 'http://d.web2.qq.com' sendHeaders['Host'] = 'd.web2.qq.com' sendHeaders['Referer'] = 'http://d.web2.qq.com/proxy.html?v=20130916001&callback=1&id=2' #sendHeaders['Connection'] = 'keep-alive' #sendHeaders['Content-Length']= 583+len(mess) #sendHeaders['Content-Type'] = 'application/x-www-form-urlencoded' sendUrl = 'http://d.web2.qq.com/channel/send_buddy_msg2' #这个是发送数据用的 toQQ = nickuin content = mess #----------------生成随机的msg_id聊天窗口标识--------------------------------------- z = 0 #q = time.time() q = int(time.time()*1000) b = 10**3 c = 10**4 q = (q - q % b) / b q = q % c * c z +=1 msg_id = str(q + z) print msg_id #―――――――――――――――――――――――――――――-------------------------------------------------------- #发生消息格式 messStr = r"""{"to":%s,"content":"[\"%s\\n\",[\"font\",{\"name\":\"宋体\",\"size\":10,\"style\":[0,0,0],\"color\":\"000000\"}]]","face":591,"clientid":53999199,"msg_id":%s,"psessionid":"%s"}""" % (toQQ,content,msg_id,psessionid) smessData =[('r',messStr)] smessData = urllib.urlencode(smessData) sendReq = urllib2.Request(sendUrl,headers=sendHeaders) sendMess = opener.open(sendReq,data=smessData) print sendMess.read()
最后 附上源码