羣裏接收消息時,使用廣播,但須要刷新頁面才能接收到廣播內容。 - 輪詢: 定時每秒刷新一次,當羣不活躍時,羣裏的每一個客戶端都在刷新,對服務端壓力太大。 - 長輪詢:客戶端連服務端,服務端一直不斷開,也不回消息。夯住請求(Web微信,WebQQ), 假設夯住60s,60s後統一斷開,而後客戶端和服務端鏈接失敗。而後緊接着再發送一次請求。至關於每分鐘發送一次請求。 夯住不動只要有一我的發送消息,馬上斷開帶着新信息返回。只要消息來了就返回斷開,這樣就實時接收消息。 - 無消息,超時以後斷開,客戶端當即發送請求; - 有消息,當即返回 輪詢和長輪詢利用的是http協議,這種請求是單向的,目前長輪詢使用普遍。 - WebSocket 相比輪詢和長輪詢更好,客戶端和服務端不斷開,客戶端和服務端能夠相互接收消息。可是不是全部的瀏覽器都支持。目前還未大批量使用,之後是趨勢。 1. 顯示二維碼 打開微信網頁微信二維碼登陸時,未掃碼登陸時二維碼登陸頁面和微信服務端一直在長輪詢狀態。 當手機掃碼時,手機向微信服務端發送請求,直接拿到結果給微信網頁端,頁面登陸狀態改變。 二維碼本質是圖片,每次刷新頁面圖片都不一樣,每次後綴都不一樣。 向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&_=1504151392313(其中1504151392313是時間戳) 發送請求獲取響應window.QRLogin.code = 200; window.QRLogin.uuid = "wdLetLlNoQ==",二維碼隨機字符串uuid:"wdLetLlNoQ==",根據uuid建立二維碼。 src="https://login.weixin.qq.com/qrcode/wdLetLlNoQ==" src="https://login.weixin.qq.com/qrcode/YeOkCQK4FQ==" - 獲取uuid - 根據uuid建立二維碼 發送消息: post_data = { "BaseRequest": { 'xxx': 123123123123, form to msg: 中文 'xxx': 123123123123, } } # requests.post(json=post_data,headers={'cotnen':'json'}) # requests.post(data=json.dumps(post_data),headers={'cotnen':'json'}) # requests.post(data=json.dumps(post_data,ensure_ascii=False),headers={'cotnen':'json'})
開發web微信html
- 打開wechat, 查看登陸頁面,猜測: 手機、web、微信服務器 - 二維碼 - 掃碼 - 肯定登陸 - 登陸cookie 200, redirict_url: ticket - 憑證cookie - 初始化: 最近信息 - 顯示頭像 由於跨域頭像沒法顯示顯示: 咱們的本身寫的網站 http://127.0.0.1: 瀏覽器上保存這個網站http://127.0.0.1相關cookie 訪問咱們本身的網站的圖片時,攜帶咱們本身的cookie <img src='http://127.0.0.1' /> 本身寫的網站訪問微信的圖片時,攜帶着咱們網站的cookie,這就跨域了,不能帶着咱們本地的cookie去。 <img src='http://wx.qq.com.....' /> <img src='http://wx.qq.com.....' /> GET請求,get請求沒有請求體,只有請求頭 請求頭:url: http://wx.qq.com..... cookie: xxxx, # 沒有微信的cookie referer: http://127.0.0.1... **** # 由於是本身寫的網站訪問微信圖片,referer默認當着當前url,微信能夠經過referer阻攔訪問,一樣cookie也能夠阻止訪問 因此不直接向微信發消息獲取頭像,上面是瀏覽器發的消息,無法僞造請求頭請求體cookie。能夠向咱們後臺本身發,由於python的requests模塊能夠僞造這些信息。 <img src='http://127.0.0.1/img' /> v= requests.get(...,cookie,headers) python的requests模塊經過獲取cookie,請求體信息。獲取微信頭像數據信息,而後再訪問本地信息從而顯示頭像。 - 顯示全部聯繫人 ... - 發消息 current_user = req.session['INIT_DICT']['User']['UserName'] # session初始化,User.UserName to = req.POST.get('to') # @dfb23e0da382f51746575a038323834a msg = req.POST.get('msg')# asdfasdfasdf # session Ticket # session Cookie ticket_dict = req.session['TICKED_DICT'] ctime = int(time.time()*1000) post_data = { "BaseRequest":{ "DeviceID": "e384757757885382", 'Sid': ticket_dict['wxsid'], 'Uin': ticket_dict['wxuin'], 'Skey': ticket_dict['skey'], }, "Msg":{ "ClientMsgId":ctime, "LocalID":ctime, "FromUserName": current_user, "ToUserName":to, "Content": msg, "Type": 1 }, "Scene": 0 } url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?pass_ticket={0}".format(ticket_dict['pass_ticket']) # res = requests.post(url=url,json=post_data) # application/json,json.dumps(post_data) # res = requests.post(url=url,data=json.dumps(post_data),headers={'Content-Type': "application/json"}) # application/json,json.dumps(post_data) res = requests.post(url=url,data=json.dumps(post_data,ensure_ascii=False).encode('utf-8'),headers={'Content-Type': "application/json"}) # application/json,json.dumps(post_data) print(res.text) - 收消息 見代碼
總結: python
a. 分析Http請求 - 請求方式 - URL - 瀏覽器看到數據的二種形式 Form Data? # form表單數據類型,request.post中取 { k: 1, k: 「fds」, k: [11,2,3,4], k: {K:}, # 不能傳字典,傳字典只能把字典的key傳到後臺,發字典的時候須要轉爲字符串類型 } request payload? # json數據類型,整個數據當成字符串發到後臺。request.body中取 { k: 1, k: 「fds」, k: [11,2,3,4], k: {K:}, } requests.post() - 請求頭:(爬網站進不去時,下面五個設置注意下,大部分能夠爬取了) user-agent: 當前用戶使用的設備,知乎爬蟲須要帶user-agent。 Referer: "xxx" content-type: application/json, host cookie關鍵,cookie依附在請求頭中 b. 代理 封IP時,代理設置
具體代碼以下:jquery
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login.html$', views.login), url(r'^check_login.html$', views.check_login), url(r'^index.html$', views.index), url(r'^avatar.html$', views.avatar), url(r'^contact_list.html$', views.contact_list), url(r'^send_msg.html$', views.send_msg), url(r'^get_msg.html$', views.get_msg), ]
from django.shortcuts import render,HttpResponse import requests import time import re import json def ticket(html): from bs4 import BeautifulSoup ret = {} soup = BeautifulSoup(html,'html.parser') for tag in soup.find(name='error').find_all(): ret[tag.name] = tag.text return ret def login(req): if req.method == 'GET': uuid_time = int(time.time() * 1000) base_uuid_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}" uuid_url =base_uuid_url.format(uuid_time) r1 = requests.get(uuid_url) result = re.findall('= "(.*)";',r1.text) uuid = result[0] req.session['UUID_TIME'] = uuid_time req.session['UUID'] = uuid return render(req,'login.html',{'uuid':uuid}) def check_login(req): response = {'code': 408,'data':None} ctime = int(time.time()*1000) # base_login_url = "https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid={0}&tip=0&r=-735595472&_={1}" base_login_url = "https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid={0}&tip=0&r=-735595472&_={1}" # "https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=AbPhQTMl9w==&tip=0&r=-736896961&_=1503975440649" login_url = base_login_url.format(req.session['UUID'],ctime) r1 = requests.get(login_url) if 'window.code=408' in r1.text: # 無人掃碼 response['code'] = 408 elif 'window.code=201' in r1.text: # 掃碼,返回頭像 response['code'] = 201 response['data'] = re.findall("window.userAvatar = '(.*)';",r1.text)[0] elif 'window.code=200' in r1.text: # 掃碼,並確認登陸 req.session['LOGIN_COOKIE'] = r1.cookies.get_dict() base_redirect_url = re.findall('redirect_uri="(.*)";',r1.text)[0] redirect_url = base_redirect_url + '&fun=new&version=v2' # 獲取憑證 r2 = requests.get(redirect_url) ticket_dict = ticket(r2.text) req.session['TICKED_DICT'] = ticket_dict req.session['TICKED_COOKIE'] = r2.cookies.get_dict() # 初始化,獲取最近聯繫人信息:工做號 post_data = { "BaseRequest":{ "DeviceID": "e384757757885382", 'Sid': ticket_dict['wxsid'], 'Uin': ticket_dict['wxuin'], 'Skey': ticket_dict['skey'], } } print('初始化開始...') # 用戶初始化,講最近聯繫人我的信息放在session中 init_url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-740036701&pass_ticket={0}".format(ticket_dict['pass_ticket']) r3 = requests.post( url=init_url, json=post_data ) r3.encoding = 'utf-8' init_dict = json.loads(r3.text) req.session['INIT_DICT'] = init_dict response['code'] = 200 return HttpResponse(json.dumps(response)) def avatar(req): prev = req.GET.get('prev') # /cgi-bin/mmwebwx-bin/webwxgeticon?seq=602427528 username = req.GET.get('username') # @fb736164312cbcdb9abe746d81e24835 skey = req.GET.get('skey') # @crypt_2ccf8ab9_4414c9f723cbe6e9caca48b7deceff93 img_url = "https://wx.qq.com{0}&username={1}&skey={2}".format(prev,username,skey) cookies= {} cookies.update(req.session['LOGIN_COOKIE']) cookies.update(req.session['TICKED_COOKIE']) print(img_url) res = requests.get(img_url,cookies=cookies,headers={'Content-Type': 'image/jpeg'}) return HttpResponse(res.content) def index(req): """顯示最近聯繫人""" # https://wx.qq.com return render(req,'index.html') def contact_list(req): """ 獲取全部聯繫人 :param req: :return: """ ctime = int(time.time()*1000) base_url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?lang=zh_CN&r={0}&seq=0&skey={1}" url = base_url.format(ctime,req.session['TICKED_DICT']['skey']) cookies = {} cookies.update(req.session['LOGIN_COOKIE']) cookies.update(req.session['TICKED_COOKIE']) r1 = requests.get(url,cookies=cookies) r1.encoding = 'utf-8' user_list = json.loads(r1.text) return render(req, 'contact_list.html',{'user_list':user_list}) def send_msg(req): """ 發送消息 :param req: :return: """ current_user = req.session['INIT_DICT']['User']['UserName'] # session初始化,User.UserName to = req.POST.get('to') # @dfb23e0da382f51746575a038323834a msg = req.POST.get('msg')# asdfasdfasdf # session Ticket # session Cookie ticket_dict = req.session['TICKED_DICT'] ctime = int(time.time()*1000) post_data = { "BaseRequest":{ "DeviceID": "e384757757885382", 'Sid': ticket_dict['wxsid'], 'Uin': ticket_dict['wxuin'], 'Skey': ticket_dict['skey'], }, "Msg":{ "ClientMsgId":ctime, "LocalID":ctime, "FromUserName": current_user, "ToUserName":to, "Content": msg, "Type": 1 }, "Scene": 0 } url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?pass_ticket={0}".format(ticket_dict['pass_ticket']) # res = requests.post(url=url,json=post_data) # application/json,json.dumps(post_data) # res = requests.post(url=url,data=json.dumps(post_data),headers={'Content-Type': "application/json"}) # application/json,json.dumps(post_data) res = requests.post(url=url,data=json.dumps(post_data,ensure_ascii=False).encode('utf-8'),headers={'Content-Type': "application/json"}) # application/json,json.dumps(post_data) print(res.text) return HttpResponse('...') def get_msg(req): """ 長輪詢獲取消息 :param req: :return: """ # 檢查是否有消息到來 ctime = int(time.time()*1000) ticket_dict = req.session['TICKED_DICT'] check_msg_url = "https://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck" cookies = {} cookies.update(req.session['LOGIN_COOKIE']) cookies.update(req.session['TICKED_COOKIE']) synckey_dict = req.session['INIT_DICT']['SyncKey'] synckey_list = [] for item in synckey_dict['List']: tmp = "%s_%s" %(item['Key'],item['Val']) synckey_list.append(tmp) synckey = "|".join(synckey_list) r1 = requests.get( url=check_msg_url, params={ 'r': ctime, "deviceid": "e384757757885382", 'sid': ticket_dict['wxsid'], 'uin': ticket_dict['wxuin'], 'skey': ticket_dict['skey'], '_': ctime, 'synckey': synckey }, cookies=cookies ) print(r1.text) if '{retcode:"0",selector:"0"}' in r1.text: return HttpResponse('...') # 有消息,獲取消息 base_get_msg_url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync?sid={0}&skey={1}&lang=zh_CN&pass_ticket={2}" get_msg_url = base_get_msg_url.format(ticket_dict['wxsid'],ticket_dict['skey'],ticket_dict['pass_ticket']) post_data = { "BaseRequest":{ "DeviceID": "e384757757885382", 'Sid': ticket_dict['wxsid'], 'Uin': ticket_dict['wxuin'], 'Skey': ticket_dict['skey'], }, 'SyncKey': req.session['INIT_DICT']['SyncKey'] } r2 = requests.post( url = get_msg_url, json=post_data, cookies=cookies ) r2.encoding = 'utf-8' # 接受到消息: 消息,synckey msg_dict = json.loads(r2.text) print(msg_dict) for msg in msg_dict['AddMsgList']: print('您有新消息到來:',msg['Content']) init_dict = req.session['INIT_DICT'] init_dict['SyncKey'] = msg_dict['SyncKey'] req.session['INIT_DICT'] = init_dict return HttpResponse('...')
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div> <img style="height: 400px;width: 400px;" id="img" src="https://login.weixin.qq.com/qrcode/{{uuid}}"> </div> <script src="/static/jquery-1.12.4.js"></script> <script> $(function () { checkLogin(); }); function checkLogin() { $.ajax({ url: '/check_login.html', type: 'get', data: {}, dataType: 'JSON', success:function (arg) { if(arg.code == 408){ checkLogin(); }else if(arg.code == 201){ $('#img').attr('src',arg.data); checkLogin(); }else { location.href = "/index.html" } } }) } </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>我的信息</h1> <img src="/avatar.html?prev={{ request.session.INIT_DICT.User.HeadImgUrl }}"> <h2>{{ request.session.INIT_DICT.User.NickName }}</h2> <h1>最近聯繫人</h1> <ul> {% for user in request.session.INIT_DICT.ContactList %} <li><img src="/avatar.html?prev={{ user.HeadImgUrl }}"> {{ user.UserName }} {{ user.NickName }}</li> {% endfor %} </ul> <a href="/contact_list.html">更多聯繫人</a> <h1>公衆號信息</h1> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>發送消息</h1> <input placeholder="接受者" id="to" /> <input placeholder="消息內容" id="msg" /> <input type="button" value="發送" onclick="sendMsg();" /> <h1>用戶列表({{ user_list.MemberCount }})</h1> {% for user in user_list.MemberList %} <div username="{{ user.UserName }}"> {# <img style="width: 50px;height: 50px;" src="/avatar.html?prev={{ user.HeadImgUrl }}"><span>{{ user.NickName }}</span>#} <span>{{ user.NickName }}</span> </div> {% endfor %} <script src="/static/jquery-1.12.4.js"></script> <script> $(function () { getMsg(); }); function getMsg() { $.ajax({ url: '/get_msg.html', type: 'GET', success:function (arg) { //console.log(arg); getMsg(); } }) } function sendMsg() { $.ajax({ url: '/send_msg.html', type: "POST", data: {'to': $('#to').val(), 'msg': $('#msg').val()}, success:function (arg) { alert(arg); } }) } </script> </body> </html>