• 基于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: 回调的方式 通过硬件和操作系统
  • 相关阅读:
    树的直径
    POJ3264 Balanced Lineup
    [mock]10月11日
    POJ1062 昂贵的聘礼
    POJ3295 Tautology
    [topcoder]TopographicalImage
    POJ1753 Flip Game
    [leetcode]Copy List with Random Pointer
    [leetcode]Candy
    [leetcode]Gas Station
  • 原文地址:https://www.cnblogs.com/chenxuming/p/9276392.html
Copyright © 2020-2023  润新知