基于flask的web微信

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}}">
获取web微信头像

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>
View Code

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
View Code

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();
                }
            }
        })
    }
View Code

轮询

轮询:
浏览器每一段时间(2s)向浏览器发一次请求,检查用户是否扫码.
浏览器发的url主要是检验用户是否扫码,只要扫码就返回头像

缺点:不断的向服务器发请求,服务器的内存压力大, 效率低,存在时间差


长轮询:
浏览器在像web微信服务器发请求的时候,没有立即断开而是挂在那里,一直连接着,
最多连接30秒,自动断开后,又马上挂起一个请求
只要用户一扫码服务器就可以马上检测到.

高性能

  高性能相关:
开进程->单线程
本质:
浏览器本质:1.socket客户端遵循HTTP协议
2.http协议建立在tcp协议之上
3.规定了发送的时候的数据格式
4.短连接,无状态

协程

协程是'微线程',真实是不存在的,是程序员人为创造出来并控制程序先执行段代码,再执行某段代码.
-如果遇到非IO请求就切换,性能低.
-如果遇到IO请求切换,性能高.能够实现并发(IO等待的过程,再去干其他的事)

IO多路复用

select: 内部循环监听多个socket对象是否发生状态的变化 最多1024个
poll: 内部循环监听多个socket对象是否发生状态的变化
epoll: 回调的方式 通过硬件和操作系统
原文地址:https://www.cnblogs.com/chenxuming/p/9276392.html