1. 获取二维码
  app.py

import re
import time
import requests
from flask import Flask,render_template


app = Flask(__name__)
app.secret_key = '1231sdfasdf'
@app.route('/login')
def login():
# 1529982725262
# 15299828432250135
ctime = 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)

rep = requests.get(
url=qcode_url
)
# print(rep.text) # window.QRLogin.code = 200; window.QRLogin.uuid = "gb8UuMBZyA==";
qcode = re.findall('uuid = "(.*)";',rep.text)[0]
session['qcode'] = qcode
return render_template('login.html',qcode = qcode)
if __name__ == '__main__': app.run()

login.html

<body>
    <div style="width: 200px;margin: 0 auto;">
        <h1 style="text-align: center;">扫码登录</h1>
        <img style="width: 200px;height: 200px;" src="https://login.weixin.qq.com/qrcode/{{qcode}}" alt="">
    </div>
</body>

2. 检查登录:扫码

1、登陆的时候需要长连接,一直保持opening的状态

2、因为第一次访问页面的时间仍然需要用到,不能用g(一次请求己结束),选择用session获取的方式

from bs4 import BeautifulSoup

def xml_parse(text):
result = {}
soup = BeautifulSoup(text,'html.parser')
tag_list = soup.find(name='error').find_all()
for tag in tag_list:
result[tag.name] = tag.text
return result

@app.route('/check/login') def check_login(): qcode = session['qcode'] ctime = int(time.time() * 1000) check_login_url = 'https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid={0}&tip=0&r=-976036168&_={1}'.format(qcode,ctime) rep = requests.get( url=check_login_url ) result = {'code': 408} if 'window.code=408' in rep.text: # 用户未扫码 result['code'] = 408 elif 'window.code=201' in rep.text: # 用户扫码,获取头像 result['code'] = 201 result['avatar'] = re.findall("window.userAvatar = '(.*)';",rep.text)[0] elif 'window.code=200' in rep.text: # 用户确认登录 redirect_uri = re.findall('window.redirect_uri="(.*)";',rep.text)[0] print(redirect_uri) #https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=ASEHe9Kr5Hq0PITHG1dXEBS8@qrticket_0&uuid=gfbq6fFg9Q==&lang=zh_CN&scan=1529986929&fun=new&version=v2 # https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=ATEkrWXwLgR3QjDuYsx-dpzN@qrticket_0&uuid=obFFB7YwVA==&lang=zh_CN&scan=1529986454 redirect_uri = redirect_uri + "&fun=new&version=v2" ru = requests.get(url=redirect_uri) # <error><ret>0</ret><message></message><skey>@crypt_ac8812af_0ffde1190007c7c044bc31ae51407c45</skey><wxsid>fRwfacRtjRFpEIwt</wxsid><wxuin>1062220661</wxuin><pass_ticket>0M1plebTzNQ%2FKaSIfTfk65laCSXUWmjpxvJEerZSnBaEDjNIyOafaQLtpQBhnCDa</pass_ticket><isgrayscale>1</isgrayscale></error> ticket_dict = xml_parse(ru.text) session['ticket_dict'] = ticket_dict result['code'] = 200 return jsonify(result)

获取头像201,登录认证200

<body>
    <div style="width: 200px;margin: 0 auto;">
        <h1 style="text-align: center;">扫码登录</h1>
        <img id="userAvatar" style="width: 200px;height: 200px;" src="https://login.weixin.qq.com/qrcode/{{qcode}}" alt="">
    </div>

    <script src="https://cdn.bootcss.com/jquery/3.3.0/jquery.min.js"></script>
    <script>
        $(function () {
            checkLogin();
        });
        function checkLogin() {
            $.ajax({
                url:'/check/login',
                method:'GET',
                dataType:'json',
                success:function (arg) {
                    console.log(arg);
                    checkLogin();
                    if(arg.code === 408){
                        checkLogin();
                    }else if(arg.code === 201){
                        $('#userAvatar').attr('src',arg.avatar);
                        checkLogin();
                    }else if(arg.code === 200){
                        location.href = "/index"
                    }
                }
            })
        }
    </script>
</body>

3. 检查登录:确认登录
  同上
4. 获取凭证:redirect_uri
  同上

"""
import re
data = 'window.QRLogin.code = 200; window.QRLogin.uuid = "gb8UuMBZyA==";'
ret = re.findall('uuid = "(.*)";',data)[0]
print(ret)
"""

from bs4 import BeautifulSoup

def xml_parse(text):
    result = {}
    soup = BeautifulSoup(text,'html.parser')
    tag_list = soup.find(name='error').find_all()
    for tag in tag_list:
        result[tag.name] = tag.text
    return result

v = "<error><ret>0</ret><message></message><skey>@crypt_ac8812af_0ffde1190007c7c044bc31ae51407c45</skey><wxsid>fRwfacRtjRFpEIwt</wxsid><wxuin>1062220661</wxuin><pass_ticket>0M1plebTzNQ%2FKaSIfTfk65laCSXUWmjpxvJEerZSnBaEDjNIyOafaQLtpQBhnCDa</pass_ticket><isgrayscale>1</isgrayscale></error>"
result = xml_parse(v)

# print(result)

print(round(1.4))

5. 信息初始化:
- 联系人
- 公众号

6. 获取所有联系人

@app.route('/index')
def index():
    pass_ticket = session['ticket_dict']['pass_ticket']
    init_url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-979112921&lang=zh_CN&pass_ticket={0}".format(pass_ticket)

    rep = requests.post(
        url=init_url,
        json={
            'BaseRequest':{
                'DeviceID':"e700290354098676",
                'Sid':session['ticket_dict']['wxsid'],
                'Skey':session['ticket_dict']['skey'],
                'Uin':session['ticket_dict']['wxuin'],
            }
        }
    )
    rep.encoding = 'utf-8'

    init_user_dict = rep.json()
    print(init_user_dict)

    return render_template('index.html',init_user_dict=init_user_dict)

index.html

<body>
    <h1>欢迎使用Web微信:{{init_user_dict.User.NickName}}</h1>

    <h3>最近联系人</h3>
    <ul>
        {% for row in init_user_dict.ContactList %}
            <li>{{row.NickName}}</li>
        {% endfor %}
        <li><a href="#">查看所有联系人</a></li>
    </ul>

    <h3>最近公众号</h3>
    {% for item in init_user_dict.MPSubscribeMsgList %}
        <div>
            <h3>{{item.NickName}}</h3>
            <ul>
                {% for msg in item.MPArticleList %}
                <li><a href="{{msg.Url}}">{{msg.Title}}</a></li>
                {% endfor %}
            </ul>
        </div>
    {% endfor %}

</body>

7. 获取联系人列表

1、

@app.route('/contact/list')
def contack_list():
    """
    获取联系人列表
    :return:
    """
    # https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?lang=zh_CN&pass_ticket=Q4pDjGjjdjOnFwfHS5I3XFzFc4ApHkTaKzlCOdh34uTVavWegV%252BUky37VviDufnO&r=1530064956758&seq=0&skey=@crypt_2ccf8ab9_fbbb31c98b1a1c12b4ec707678dd336e
    # GET
    ctime = int(time.time() * 1000)
    pass_ticket = session['ticket_dict']['pass_ticket']
    skey = session['ticket_dict']['skey']
    contact_url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?lang=zh_CN&pass_ticket={0}&r={1}&seq=0&skey={2}".format(pass_ticket,ctime,skey)

    res = requests.get(
        url=contact_url,
        cookies=session['ticket_cookies']
    )
    res.encoding = 'utf-8'
    user_list = res.json()

    return render_template('contact_list.html',user_list=user_list)

contact_list.html

<body>
    <h1>联系人列表</h1>
    <ul>
        {% for user in user_list.MemberList %}
            <li>
                <img style="height: 50px;width: 50px;" src="/get_img?prev={{user.HeadImgUrl}}">
                <span>用户名:{{user.NickName}} 唯一标识:{{user.UserName}}</span>
            </li>
        {% endfor %}

    </ul>
</body>

在获取用户图片

@app.route('/get_img')
def get_img():
    prev = request.args.get('prev') # /cgi-bin/mmwebwx-bin/webwxgeticon?seq=686005187
    username = request.args.get('username') # @9c4df5e041eb06725a410a3d9d580877e229066895b3e91d44a7af8be37e0e5b
    skey = request.args.get('skey') # @crypt_ac8812af_a5601beadce3211cdb4fd3663d08ab52

    head_img_url = "https://wx.qq.com{0}&username={1}&skey={2}".format(prev,username,skey)

    rep = requests.get(
        url=head_img_url,
        cookies=session['ticket_cookies']
    )

    return rep.content

8. 发送消息

@app.route('/send/msg',methods=['GET','POST'])
def send_msg():
    if request.method == "GET":
        return render_template('send_msg.html')

    ctime = int(time.time() * 1000)
    from_user = request.form.get('fromUser')
    to_user = request.form.get('toUser')
    content = request.form.get('content')

    data_dict = {
        'BaseRequest':{
                'DeviceID':"e700290354098676",
                'Sid':session['ticket_dict']['wxsid'],
                'Skey':session['ticket_dict']['skey'],
                'Uin':session['ticket_dict']['wxuin'],
        },
        'Msg':{
            'ClientMsgId':ctime,
            'Content':content,
            'FromUserName':from_user,
            'LocalID':ctime,
            'ToUserName':to_user,
            'Type':1
        },
        'Scene':0
    }

    msg_url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?lang=zh_CN&pass_ticket={0}".format(session['ticket_dict']['pass_ticket'])
    rep = requests.post(
        url=msg_url,
        data=bytes(json.dumps(data_dict,ensure_ascii=False),encoding='utf-8')
    )

    print(rep)

    return "发送成功"

send_msg.html

@app.route('/send/msg',methods=['GET','POST'])
def send_msg():
    if request.method == "GET":
        return render_template('send_msg.html')

    ctime = int(time.time() * 1000)
    from_user = request.form.get('fromUser')
    to_user = request.form.get('toUser')
    content = request.form.get('content')

    data_dict = {
        'BaseRequest':{
                'DeviceID':"e700290354098676",
                'Sid':session['ticket_dict']['wxsid'],
                'Skey':session['ticket_dict']['skey'],
                'Uin':session['ticket_dict']['wxuin'],
        },
        'Msg':{
            'ClientMsgId':ctime,
            'Content':content,
            'FromUserName':from_user,
            'LocalID':ctime,
            'ToUserName':to_user,
            'Type':1
        },
        'Scene':0
    }

    msg_url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?lang=zh_CN&pass_ticket={0}".format(session['ticket_dict']['pass_ticket'])
    rep = requests.post(
        url=msg_url,
        data=bytes(json.dumps(data_dict,ensure_ascii=False),encoding='utf-8')
    )

    print(rep)

    return "发送成功"

9、使用

  使用发送信息的时候、需要先获取用户当前的UserName,在第一次获取index.html时

 print(init_user_dict)里去网页json解析出来即可使用,注意每次登录的username里的符号都会变化
'User': {
'Uin': 2589030507,
'UserName': '@586d71fe149ae1942bc5bca1f64edd87d385b449850bdc6796ade1a92c9d2933',

10、总结

发送data和json的区别

    关系发送消息:
            data:
                request.post(
                    url='xx',
                    data={'k1':'v1,'k2':'v2'}
                )
                #数据:  POST /  http1.1\r\n....\r\n\r\nk1=v1&k2=v2
                
                
                request.post(
                    url='xx',
                    data=json.dumps({'k1':'v1,'k2':'v2'})
                )
                #数据:  POST /  http1.1\r\n....\r\n\r\n{'k1':'v1,'k2':'v2'}
                
                request.post(
                    url='xx',
                    data=b'asdfasdf'
                )
                #数据:  POST /  http1.1\r\n....\r\n\r\n'asdfasdf'
            json:
                request.post(
                    url='xx',
                    json={'k1':'v1,'k2':'v2'}
                )
                #数据:  POST /  http1.1\r\nContent-type:application/json....\r\n\r\n{'k1':'v1,'k2':'v2'}
    
            问题:
                同时:POST请求发数据
                
                django:获取不到值?request.POST 
                
                发送数据格式:
                    方式一:
                        request.post(
                            url='xx',
                            data={'k1':'v1,'k2':'v2'}
                        )
                        #数据:  POST /  http1.1\r\nContent-type:urlencode-form.......\r\n\r\nk1=v1&k2=v2
        
                        
                        request.POST必然可以获取到值。
                            - content-type: urlencode-form
                            - 数据格式:k1=v1&k2=v2
    
                    方式二:
                        request.post(
                            url='xx',
                            json={'k1':'v1,'k2':'v2'}
                        )
                        #数据:  POST /  http1.1\r\nContent-type:application/json....\r\n\r\n{'k1':'v1,'k2':'v2'}
                        request.body 
                            字节 = {'k1':'v1,'k2':'v2'}
                            字节转换字符串
                            反序列化字符串 -> 字典 
                        
                        request.POST必然不可以获取到值。
                            - content-type: urlencode-form
                            - 数据格式:k1=v1&k2=v2
                        
                
            知识点:
                chrome->
                    Form Data:
                        phone=861513125555&password=12312312312&oneMonth=1
                        
                        reqeusts.post(
                            url=url,
                            data={
                                phone:123123123123,
                                password:asdfasdf
                            }
                        )
                    
                    Request Payload:
                        {"BaseRequest":{"Uin":981579400,"Sid":"zWvteTWqBop4heoT","Skey":"@crypt_2ccf8ab9_a710cf413c932e201987599558063c8e","DeviceID":"e358217921593270"},"Msg":{"Type":1,"Content":"test","FromUserName":"@60eef3f2d212721fda0aae891115aa7a","ToUserName":"@@6a5403f510a3192454ed1afebd78ec6033d5057c9038d7b943b201f0a74987d4","LocalID":"15300708105840758","ClientMsgId":"15300708105840758"},"Scene":0}
                    
                        reqeusts.post(
                            url=url,
                            json={
                                phone:123123123123,
                                password:asdfasdf
                            }
                        )
                        
                        reqeusts.post(
                            url=url,
                            data=bytes(json.dumps({
                                phone:123123123123,
                                password:asdfasdf
                            }),encoding=utf-8)
                        )
    
    
                firefox:
                    表单数据:
                        
                    JSON:
                        
    
    
        目标:练习分析Http请求能力