• requests+django+bs4实现一个web微信的功能


    前言:

      今天我们利用requests模块+django+bs4浏览器来实现一个web微信的基本功能,主要实现的功能如下

      a、实现返回二维码

      b、实现手机扫码后二维码变成变成头像

      c、实现手机点击登陆成功显示微信的最近联系人

      d、实现显示所有的联系人

      e、实现发送消息

      

      下面我们就开始实现上述的功能,在看这篇博客的之前,读者朋友需要去了解一下长轮询的知识,因为wei微信的登陆就用到了长轮询,首先我们先把web登陆的流程梳理一下,然后在实现我们的功能

    一、web微信登陆分析

    1、web微信二维码分析

     a、首先拿到url,这个请求是get请求

    https://login.wx2.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage&fun=new&lang=zh_&_=1555510256420
    

      

    这个url很好构建,只有1555510256420这个参数需要我们认为生成,其他他就是时间戳*1000,然后取整,生成的方法如下

    t = int(time.time() * 1000)
    

      

    b、分析这个url的返回值

    c、查看网页的源代码,看下这个二维码到底是什么

    看下img标签的src属性,有没有注意到,src的这一段字符串oaKKJgJRhA==,是我们返回二维码的url返回的字符串,所以我们就可以拼接出来二维码这个图片的src的地址

    https://login.weixin.qq.com/qrcode/oaKKJgJRhA==  
    

      

    2、等待用户扫码

    这里就用到了一个长轮询,如果客户一直没有扫码,则会hang住,等待客户的扫码

    a、先来分析一下url

    https://login.wx2.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=oeoQNe1EiA==&tip=1&r=-732967182&_=1555511127069
    

     

    这个url有2个地方需要我们来构建

     第一个参数就上一步返回的字符串,第二个参数就是一个还是一个时间戳

    b、在来看下这个url返回了什么

     只有一个状态码408

     结论:如果url的返回的code为408,则表示等待用户扫码

    3、web微信显示头像分析

    手机扫码后,二维码变成头像

    a、先来分析url

    https://login.wx2.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=Qfn4ldhuNQ==&tip=1&r=-732688468&_=1555510848123
    

      

    这个url和上面的url一样,所以我们知道,第一步返回的字符串非常重要,所以我们要把这段字符串放在session中

    b、在来看下url的返回值

    这里返回了一段字符串,code为201,后面那一段字符串是头像的地址

     c、我们在来看下html中的img标签的src的地址

     结论:返回201,则证明用户已经扫码成功

    4、web微信登陆分析

    a、首先url还是之前的url,这里就不做分析

    https://login.wx2.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=YacjFJrAfA==&tip=0&r=-733737113&_=1555511755717
    

      

    b、看下这次请求的返回值

    这里有一个跳转的url,也就是当我们点击登陆后,会跳转到这个url

    这里还有一个返回码是200

    结论:状态码返回200,则证明登陆成功

    5、分析web微信的跳转url

    a、分析一下这次请求的返回值

    <error><ret>0</ret><message></message><skey>@crypt_90b16895_59f7cbfc1c217310b90558af662ea9c7</skey><wxsid>VP1xxDiAiU5Xz8gN</wxsid><wxuin>1632086000</wxuin><pass_ticket>w%2BIW73Y3XXFLqfA%2BBworrfgKu5aRlyXW%2F57wtMPYwrP%2BWnDW3ieWQ8jBmUUTbawy</pass_ticket><isgrayscale>1</isgrayscale></error>
    

      

    这个返回值非常重要,我们后面登陆后需要做的操作都需要这里的信息。所以这个信息我们也要组合一下放在session后,方便的后面的请求使用

    6、web微信显示最近联系人流程分析

    访问为跳转url后,拿到返回值信息,web微信又会发送一个post请求,获取最近联系人信息

     a、先看下url,这里url就需要用到上面跳转url的返回值的信息来拼接

    https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-733594626&pass_ticket=w%2BIW73Y3XXFLqfA%2BBworrfgKu5aRlyXW%2F57wtMPYwrP%2BWnDW3ieWQ8jBmUUTbawy
    

      

     b、这个请求的返回值就是最近联系人

     c、我们就可以把这些数据渲染到html页面就可以了

    7、web微信显示全部联系人

     点击这里,就会显示全部联系人

    a、分析一下url

    https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?pass_ticket=w%2BIW73Y3XXFLqfA%2BBworrfgKu5aRlyXW%2F57wtMPYwrP%2BWnDW3ieWQ8jBmUUTbawy&r=1555511910553&seq=0&skey=@crypt_90b16895_59f7cbfc1c217310b90558af662ea9c7
    

      

    我们完全可以根据session中的数据拼接这个字符串

    b、这次请求的返回信息就是所有的联系人

    8、web发送消息

     发送消息是一个post的请求

     a、先来分析url

    https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?pass_ticket=w%2BIW73Y3XXFLqfA%2BBworrfgKu5aRlyXW%2F57wtMPYwrP%2BWnDW3ieWQ8jBmUUTbawy
    

      

    我们可以通过session中的数据拼接出这个url

    b、在来看下这次post携带的请求体,我们完全可以通过session中的数据拼接出这个请求体

     c、分析msg这个信息

    第一条是时间戳

    第二条是发送的内容

    第三条发送者的微信id

    第四条也是时间戳

    第五条是接受者的微信id

    二、我们的代码实现

    通过上面的分析,我相信大家对web微信的请求已经非常了解了,下面我们使用requests+bs4+djangon来实现一个建议的web微信

    1、首先看下登陆的html,重点看下我的注释

     2、进入views文件,看下返回二维码的视图函数,我们注意到,前面的html需要q_code这个变量来渲染img标签的src的路径,显示二维码

     3、然后后看下等待用户扫码的后台逻辑

    4、看下前端处理408返回码的逻辑

     5、在来看下用户扫码后的后台逻辑

     6、在看下前端收到201的返回值处理逻辑,首先修改二维码的地址为头像的地址,然后再次发送一次请求,等待用户点击确认

     7、在看下后端处理用户点击登陆的逻辑

     8、在看下前端处理200请求的逻辑,会跳转到一个最近联系人的页面

    9、我们在看下这个url对应的视图函数,这个视图函数是返回最近联系人的函数,需要携带规定的请求体,这些请求体已经被存储到session中

     10、在看下index.html这个页面,这个数据结构比较简单,大家自己自己抓包看

     11、我们再看下查所有人联系人

     12、看下对应的视图函数,拼接url,然后把返回值返回给前端

     13、前端渲染数据即可

     14、在看发送信息的前端页面

    15、再看下后端的处理逻辑,主要是拼接url和处理中文的信息

    def sendmsg(request):
        if request.method == "GET":
            return render(request,"sendmsg.html")
    
        else:
            from_user = request.POST.get("from_user")
            to_user = request.POST.get("to_user")
            content = request.POST.get("content")
    
            data_dict = {
                "BaseRequest":{
                    "DeviceID":"e461335461567419",
                    "Sid":request.session["temp_dict"].get("wxsid"),
                    "Skey":request.session["temp_dict"].get("skey"),
                    "Uin":request.session["temp_dict"].get("wxuin")
                },
                "Msg":{
                    "ClientMsgId":int(time.time() * 1000),
                    "Content":content,
                    "FromUserName":from_user,
                    "LocalID":int(time.time() * 1000),
                    "ToUserName":to_user,
                    "Type":1
                },
            "Scene":0
            }
            rep = requests.post(
                url= "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?lang=zh_CN&pass_ticket={p}".format(p = request.session["temp_dict"]["pass_ticket"]),
    
                # 1、方式1,处理不了中文,由于json的问题
                # json=data_dict
    
                # 2、方式2,解决了json处理不了中文的问题,但是微信用的解码是不是常见的解码方式,所以还是处理不了中文
                # data = json.dumps(
                #     data_dict,
                #     ensure_ascii=False
                # )
    
                # 3、方式3,直接发送二进制文件,就可以解决发送中文的问题
                data = bytes(json.dumps(
                    data_dict,
                    ensure_ascii=False
                ),encoding="utf-8")
            )
            print(rep.text)
    
            return HttpResponse("success")
    

      

     三、整体的后端代码

    from django.shortcuts import render
    from django.shortcuts import HttpResponse
    from django.shortcuts import redirect
    import requests
    import re
    # Create your views here.
    
    import time
    def login(request):
        if request.method.lower() == "get":
            t = int(time.time() * 1000)
            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&_={t}"
            res = requests.get(url=url)
            # window.QRLogin.code = 200;
            # window.QRLogin.uuid = "oc86pbX-hQ==";
            re_obj = re.compile('= "(.*==)";$')
            q_code = re_obj.findall(res.text)[0]
            request.session["q_code"] = q_code
            return render(request,"login.html",locals())
    
    import json
    import re
    from bs4 import BeautifulSoup
    # BeautifulSoup还可以处理xml文档
    def checklogin(request):
        if request.method.lower() == "get":
            res_dict = {"code":408,"img":None,"url":None}
            code = request.session["q_code"]
            t = int(time.time() * 1000)
            url = "https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid={code}&tip=0&r=-131537270&_={t}".format(code = code,t = t)
            rep = requests.get(url=url)
            if "window.code=408;" in rep.text:
                return HttpResponse(json.dumps(res_dict))
    
            elif "window.code=201;" in rep.text:
                # 扫码成功
                obj = re.compile("window.userAvatar = '(.*)';")
                src = obj.findall(rep.text)[0]
                res_dict["code"] = 201
                res_dict["img"] = src
    
                return HttpResponse(json.dumps(res_dict))
            elif "window.code=200;" in rep.text:
                # 确定登陆
                obj = re.compile('window.redirect_uri="(.*)";')
                url = obj.findall(rep.text)[0]
                res_dict["code"] = 200
                res_dict["url"] = url
                new = requests.get(url = url + "&fun=new&version=v2&lang=zh_CN")
                script_obj = BeautifulSoup(new.text,"html.parser")
                temp_dict = {}
                for tag in script_obj.find(name="error"):
                    temp_dict[tag.name] = tag.text
    
                request.session["temp_dict"] = temp_dict
                request.session["cookies"] = new.cookies.get_dict()
                return HttpResponse(json.dumps(res_dict))
            else:
                pass
                return HttpResponse("haha")
    
    
    def index(request):
        url = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-221192329&pass_ticket={t}".format(t = request.session["temp_dict"].get("pass_ticket"))
        init = requests.post(
            url=url,
            json={
                "BaseRequest":{
                    "DeviceID":"e701447882725714",
                    "Sid":request.session["temp_dict"].get("wxsid"),
                    "Skey":request.session["temp_dict"].get("skey"),
                    "Uin":request.session["temp_dict"].get("wxuin")
                }
            }
        )
        init.encoding = "utf-8"
        init_user_dict = init.json()
        return render(request,"index.html",locals())
    
    
    def contact(request):
        t = int(time.time() * 1000)
        rep = requests.get(
            url = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?lang=zh_CN&pass_ticket={p}&r={t}&seq=0&skey={s}".format(t = t,
                                                                                                                                  p = request.session["temp_dict"]["pass_ticket"],
                                                                                                                                  s = request.session["temp_dict"]["skey"]),
            cookies = request.session["cookies"]
        )
        rep.encoding = "utf-8"
        user_list= rep.json()
    
        return render(request,"contact.html",locals())
    
    
    def avator(request):
        # print(request.GET.get("prev"))
        # print(request.GET.get("username"))
        # print(request.GET.get("skey"))
        url = "https://wx2.qq.com{p}&username={u}&skey={s}".format(p = request.GET.get("prev"),
                                                                       u = request.GET.get("username"),
                                                                       s = request.GET.get("skey")
                                                                       )
        img = requests.get(
            url = url,
            cookies = request.session["cookies"]
        )
        print(url)
        return img.content
    
    
    def sendmsg(request):
        if request.method == "GET":
            return render(request,"sendmsg.html")
    
        else:
            from_user = request.POST.get("from_user")
            to_user = request.POST.get("to_user")
            content = request.POST.get("content")
    
            data_dict = {
                "BaseRequest":{
                    "DeviceID":"e461335461567419",
                    "Sid":request.session["temp_dict"].get("wxsid"),
                    "Skey":request.session["temp_dict"].get("skey"),
                    "Uin":request.session["temp_dict"].get("wxuin")
                },
                "Msg":{
                    "ClientMsgId":int(time.time() * 1000),
                    "Content":content,
                    "FromUserName":from_user,
                    "LocalID":int(time.time() * 1000),
                    "ToUserName":to_user,
                    "Type":1
                },
            "Scene":0
            }
            rep = requests.post(
                url= "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?lang=zh_CN&pass_ticket={p}".format(p = request.session["temp_dict"]["pass_ticket"]),
    
                # 1、方式1,处理不了中文,由于json的问题
                # json=data_dict
    
                # 2、方式2,解决了json处理不了中文的问题,但是微信用的解码是不是常见的解码方式,所以还是处理不了中文
                # data = json.dumps(
                #     data_dict,
                #     ensure_ascii=False
                # )
    
                # 3、方式3,直接发送二进制文件,就可以解决发送中文的问题
                data = bytes(json.dumps(
                    data_dict,
                    ensure_ascii=False
                ),encoding="utf-8")
            )
            print(rep.text)
    
            return HttpResponse("success")
    

      

     相信如果大家看懂我前面分析web微信的逻辑,看懂应该不成问题。如果有不清楚的,请评论留言,感谢大家关注,谢谢!

  • 相关阅读:
    jQuery 入门 -- 事件 事件绑定与事件委托
    原生js实现视差风格音乐播放器
    jQuery 入门
    一些开放的免费接口【已失效】
    php mysqli操作数据库
    DOM 相关
    面向对象
    对象
    博客园添加鼠标点击特效
    正则表达式
  • 原文地址:https://www.cnblogs.com/bainianminguo/p/10727084.html
Copyright © 2020-2023  润新知