web微信
1.扫码获取头像
当你打开web微信的时候,因为http是无状态的,web微信如何实时的获取用户的扫码动作?
那么这里用到的是长轮询的方式。
from flask import Flask,request,redirect,render_template,session,jsonify import time import requests import re from bs4 import BeautifulSoup import json app =Flask(__name__) app.secret_key='adfa12da' @app.route('/login',methods=['GET',"POST"]) def login(): ''' 扫码获取头像 :return: ''' if request.method=='GET': ctime = str(int(time.time()*1000)) qcode_url = 'https://login.wx.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=zh_CN&_={0}'.format(ctime) ret = requests.get(qcode_url) qcode = re.findall('uuid = "(.*)";',ret.text)[0] session['qcode'] = qcode session['login_cookies'] = ret.cookies.get_dict() return render_template('login.html',qcode=qcode) else: pass ####Html######### <img id="img" src="https://login.weixin.qq.com/qrcode/{{qcode}}">
2.点击登录
a.当扫码成功后获取到了web头像,依旧要执行长轮询,我们需要做的是获取发起长轮询的url,
b.将登录成功的cookies保存在session里。
c.模仿浏览器自动访问并且挂起。当登录成功后,状态码为200,跳转到主页面
@app.route('/check_login',methods=['GET','POST']) def check_login(): ''' 检测是否已经登录. url:https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=AcjVHfghyA==&tip=0&r=-557419402&_=1529565723654 :return: ''' if request.method=='GET': response= {'code':408} qcode = session.get('qcode') ctime = str(int(time.time()*1000)) check_url = 'https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid={0}&tip=0&r=-557419402&_={1}'.format(qcode,ctime) ret = requests.get(url=check_url) print(ret.text) if 'code=201' in ret.text: src = re.findall("userAvatar = '(.*)';",ret.text)[0] response['code']=201 response['src'] = src elif 'code=200' in ret.text: src = re.findall('redirect_uri="(.*);',ret.text)[0] response['code']=200 response['src']=src ret = requests.get(url=src+'&fun=new&version=v2&lang=zh_CN') xml_dic = xml_parser(ret.text) session['xml_dic']=xml_dic session['pass_cookies'] = ret.cookies.get_dict() print(xml_dic) return jsonify(response) ####js代码#### #不断的发送长轮询 当返回的状态码为200的时候跳转页面 $(function () { check_login(); }); function check_login() { $.ajax({ url:'/check_login', type:'GET', datatype:'JSON', success:function (arg) { if (arg.code == 201){ $('#img').attr('src',arg.src); check_login(); } else if (arg.code == 200){ location.href='/index' }else { check_login(); } } }) }
3.获取个人信息和联系人列表
a.当登录成功后,进行获取个人的信息,存放在session里方便后面的使用。
b.获取联系人列表在前端展示.
@app.route('/index',methods=['GET',"POST"]) def index(): pass_ticket =session['xml_dic'].get('pass_ticket') inni_dic={ 'BaseRequest':{ 'DeviceID':"e901523268059245", 'Sid':session['xml_dic'].get('wxsid'), 'Skey':session['xml_dic'].get('skey'), 'Uin':session['xml_dic'].get('wxuin'), } } inni_msg = requests.post( url ='https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-581032482&lang=zh_CN&pass_ticket={0}'.format(pass_ticket), json=inni_dic ) inni_msg.encoding='utf-8' user_dict = inni_msg.json() session['user_info'] = user_dict['User'] session['SyncKey'] = user_dict['SyncKey'] ctime= str(int(time.time()*1000)) contact_url= 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?lang=zh_CN&pass_ticket={0}&r={1}&seq=0&skey={2}' contact_url = contact_url.format(pass_ticket,ctime,session['xml_dic'].get('skey')) pass_cookies = session.get('pass_cookies') contact_list = requests.get( url=contact_url, cookies = pass_cookies, ) contact_list.encoding = 'utf-8' result_list = contact_list.json() return render_template('index.html',user_dict=user_dict,result_list=result_list) ####前端代码#### <body> <h1>Welcome {{user_dict.User.NickName}}!!</h1> <img src="/get_img" alt=""> <ul> {%for row in user_dict.ContactList%} <li>{{row.NickName}}</li> {%endfor%} </ul> <div> <h1>全部联系人</h1> <ul> {% for item in result_list.MemberList%} <li>{{item.UserName}}===={{item.NickName}}</li> {%endfor%} </ul> </div> </body>
4.登录成功后获取头像
#<img src="/get_img" alt=""> @app.route('/get_img',methods=['GET','POST']) def get_img(): # 需要不断的试cookies # 登录以后的操作一般都需要cookies, 一般cookies是拿登录成功后的 # 特殊的需要将cookies成功之后才能进行登录,访问的时候就记录cookies # headers的重要参数: Referer host User_Agent user_info = session.get('user_info') pass_cookies = session.get('pass_cookies') header_url = "https://wx2.qq.com"+user_info['HeadImgUrl'] ret = requests.get( url = header_url, cookies = pass_cookies, headers= { 'Content-Type': 'image/jpeg', } ) return ret.content
5.发送信息
登录成功后,拿到了联系人列表,其中返回的信息里面的MemberList有每个人的NickName.
用Ajax给他人发送消息
@app.route('/send_msg',methods=['GET','POST']) def send_msg(): if request.method=='GET': return 'what are you 弄啥呢' else: data =request.form to = data.get('to') content = data.get('content') print(to,content) user = session['user_info']['UserName'] pass_ticket =session['xml_dic'].get('pass_ticket') ctime= str(int(time.time()*1000)) send_url ='https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?lang=zh_CN&pass_ticket={0}'.format(pass_ticket) send_dict = { 'BaseRequest':{ 'DeviceID': "e901523268059245", 'Sid': session['xml_dic'].get('wxsid'), 'Skey': session['xml_dic'].get('skey'), 'Uin': session['xml_dic'].get('wxuin'), }, 'Msg':{'ClientMsgId':ctime, 'LocalID':ctime, 'FromUserName':user, 'ToUserName':to, 'Type':1, 'Content':content, }, 'Scene':0,} ret = requests.post( url=send_url, data=bytes(json.dumps(send_dict,ensure_ascii=False),encoding='utf-8'), ) return ret.text ###前端代码#### <body> <form id="fm"> <input type="text" name="to" placeholder="联系人"> <input type="text" name="content" placeholder="内容"> <button onclick="send_msg();">提交</button> </form> function send_msg() { $.ajax({ url:'/send_msg', data:$('#fm').serialize(), type:'POST', dataType:'JSON', success:function (arg) { if (arg.BaseResponse.Ret == 0){ console.log('发送成功'); $('#fm').find(':input').val('') } } }) }
json.dumps需要注意的问题
json.dumps需要注意的问题
6.接收信息
接收消息也是不断的向服务端发送请求,检查是否有人发送消息
@app.route('/get_msg',methods=['GET','POST']) def get_msg(): user_init_list = session.get('SyncKey').get('List') syn_list=[] for item in user_init_list: temp = '%s_%s'%(item['Key'],item['Val']) syn_list.append(temp) syn_list_str = '|'.join(syn_list) get_url = 'https://webpush.wx2.qq.com/cgi-bin/mmwebwx-bin/synccheck' ctime = str(int(time.time() * 1000)) syn_dict={ 'r':ctime, 'DeviceID': "e901523268059245", 'Sid': session['xml_dic'].get('wxsid'), 'Skey': session['xml_dic'].get('skey'), 'Uin': session['xml_dic'].get('wxuin'), 'synckey':syn_list_str } all_cookies={} all_cookies.update(session['login_cookies']) all_cookies.update(session['pass_cookies']) response = requests.get( url=get_url,params=syn_dict,cookies=all_cookies ) print(response.text) if 'selector:"2"' in response.text: data = { 'BaseRequest': { 'DeviceID': "e901523268059245", 'Sid': session['xml_dic'].get('wxsid'), 'Skey': session['xml_dic'].get('skey'), 'Uin': session['xml_dic'].get('wxuin'), }, 'synckey': session.get('SyncKey'), 'rr': ctime, } url = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsync?sid={0}&skey={1}'.format(session['xml_dic'].get('wxsid'), session['xml_dic'].get('skey'), ) response_msg = requests.post( url=url,json=data ) response_msg.encoding = 'utf-8' response_msg_dic = response_msg.json() session['SyncKey'] = response_msg_dic['SyncKey'] for item in response_msg_dic: print(item['Content'],'=========>',item['FromUserName'],'========',item['ToUserName']) return 'pass' #######前端####### $(function () { get_msg(); }); function get_msg() { $.ajax({ url:'/get_msg', type:'GET', success:function (arg) { if (arg){ get_msg(); } } }) }
轮询
轮询:
浏览器每一段时间(2s)向浏览器发一次请求,检查用户是否扫码.
浏览器发的url主要是检验用户是否扫码,只要扫码就返回头像
缺点:不断的向服务器发请求,服务器的内存压力大, 效率低,存在时间差
长轮询:
浏览器在像web微信服务器发请求的时候,没有立即断开而是挂在那里,一直连接着,
最多连接30秒,自动断开后,又马上挂起一个请求
只要用户一扫码服务器就可以马上检测到.
高性能
高性能相关:
开进程->单线程
本质:
浏览器本质:1.socket客户端遵循HTTP协议
2.http协议建立在tcp协议之上
3.规定了发送的时候的数据格式
4.短连接,无状态
协程
协程是'微线程',真实是不存在的,是程序员人为创造出来并控制程序先执行段代码,再执行某段代码.
-如果遇到非IO请求就切换,性能低.
-如果遇到IO请求切换,性能高.能够实现并发(IO等待的过程,再去干其他的事)
IO多路复用
select: 内部循环监听多个socket对象是否发生状态的变化 最多1024个
poll: 内部循环监听多个socket对象是否发生状态的变化
epoll: 回调的方式 通过硬件和操作系统