// ==UserScript== // @name 微信公众号粉丝查看增强工具 // @namespace https://mp.weixin.qq.com/cgi-bin/user_tag?action=get_all_data // @version 0.1.12 // @description 微信公众号粉丝查看增强工具,可查看用户关注的精确时间(精确到时分秒),可导出用户数据,增加用户所在城市,解除跳转页数限制。 // @author Wilson // @match https://mp.weixin.qq.com/cgi-bin/user_tag?action=get_all_data* // @icon https://img.icons8.com/fluent/48/000000/user-male-circle.png // @require https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js // @run-at document-start // @grant GM_xmlhttpRequest // @grant unsafeWindow // @downloadURL none // ==/UserScript== (function() { 'use strict'; //用户数据,记录每次变更的数据 unsafeWindow.userData = []; unsafeWindow.allUserData = []; //请求响应时间 var lastRequestTime = 0, lastResponseTime = 0; //添加关注信息 function addFllowInfo(userList) { for (let i in userList) { //添加关注时间 let user = userList[i]; user.user_openid = user.user_openid || user.id; user.user_create_time = user.user_create_time || user.create_time; let localDate = new Date(user.user_create_time * 1000).toLocaleString(); //console.log(user.user_create_time, user.user_openid); let userName = document.querySelector(".user_info a[data-fakeid=" + user.user_openid + "]"); //console.log(userName, user.user_openid); if (userName) userName.innerHTML += '    (' + localDate + '关注)'; } //绑定页面跳转按钮点击事件 $(".page_go")[0].addEventListener("click", function(e) { //如果手动点击则自动跳转目标页 if (e.isTrusted) { autoGoPage(); } }); //导出按钮 $(".tool_area .pagination").append('导出本页'); let btnstyle = 'background-color: #fff;background-image: linear-gradient(to bottom,#fff 0,#fff 100%);border-color: #e7e7eb;color: #353535;height: 30px; line-height: 30px;padding-left: 10px; padding-right: 10px;width: auto;'; $(".tool_area .pagination").append('导出导出'); } //错误提示 function errorTips(str, delay) { $(".page_tips .inner").html(str); $(".page_tips").show(); setTimeout(function(){ $(".page_tips").hide(); $(".page_tips .inner").html("将浏览器更新为QQ浏览器或Chrome浏览器,以获得更好的体验"); }, delay||3000); } //监听ajax请求 //var lastUserUrl = ""; (function(open) { XMLHttpRequest.prototype.open = function(method, url, async, user, pass) { this.addEventListener("readystatechange", function() { //console.log(this.readyState, this.status, this); if (this.readyState==4 && this.status==200) { //获取用户列表 if (url.indexOf("action=get_user_list") !== -1) { let json = JSON.parse(this.response); //console.log(this.readyState, this.status, json); let userList = json.user_list.user_info_list || []; //等待列表渲染完成添加信息 setTimeout(function(){ addFllowInfo(userList); }, 400); unsafeWindow.userData = userList; //unsafeWindow.wx_user_export_page++; if(unsafeWindow.isExporting) { for(let j in userList) { if(userList[j]) { unsafeWindow.allUserData.push(userList[j]); } } } //记录响应时间(毫秒) lastResponseTime = new Date().getTime(); } //获取用户信息 if (url.indexOf("action=get_fans_info") !== -1) { let json = JSON.parse(this.response); //console.log(this.readyState, this.status, json); let userList = json.user_list.user_info_list || []; if(userList.length > 0) { let userInfo = userList[0]; setTimeout(function(){ $(".rich_buddy.popover").css("border", 0); $(".popover_inner").css("border", "1px solid #C2C2C2").css("width", "355px"); if($(".frm_control_group.location").length === 0){ let area = `
`+userInfo.user_country+` `+userInfo.user_province+` `+userInfo.user_city+`
`; $(".rich_user_info_inner").append(area); } let localDate = new Date(userInfo.user_create_time * 1000).toLocaleString(); $(".tips_global").html(localDate + "关注"); }, 200); } } } /* let userUrl = document.location.origin + url; if (lastUserUrl != userUrl && url.indexOf("ction=get_user_list") !== -1) { console.log(userUrl); GM_xmlhttpRequest({ method: "get", url: userUrl, onload: function(res){ if(res.status === 200){ console.log('sucess') let json = JSON.parse(res.response); let userList = json.user_list.user_info_list || []; addFllowInfo(userList); unsafeWindow.userData = userList; //unsafeWindow.wx_user_export_page++; }else{ console.log('error') console.log(res) } }, onerror : function(err){ console.log('error') console.log(err) } }); //$.getJSON(userUrl, function(data){ //console.log(data); //$.each(data.items, function(i,item){ // }); //}); lastUserUrl = userUrl; } */ }, false); open.call(this, method, url, async, user, pass); }; })(XMLHttpRequest.prototype.open); //导出x页 unsafeWindow.isExporting = false; unsafeWindow.exportNum = ''; unsafeWindow.isStop = 0; unsafeWindow.wx_user_export_all = function() { if(unsafeWindow.isExporting) return false; unsafeWindow.isExporting = true; let total = parseInt($(".page_num label:last").text()-0); let currpage = parseInt($(".page_num label:first").text()-0); //if(currpage > 1){ // errorTips("请先跳转到第1页"); // unsafeWindow.isExporting = false; // return false; //} unsafeWindow.allUserData = unsafeWindow.userData || wx.cgiData.user_list || []; //let count = unsafeWindow.allUserData.length; //let begin_openid = unsafeWindow.allUserData[count-1].id; //let begin_create_time = unsafeWindow.allUserData[count-1].create_time; unsafeWindow.exportNum = parseInt($("#wx_export_num").val()-0); if(unsafeWindow.exportNum <= 0){ errorTips("请填写页数"); unsafeWindow.isExporting = false; return false; } //正在导出中... $(".tool_area").append('
'); let sleep = function (ms) { return new Promise(resolve => setTimeout(resolve, ms)); } let doGetUsers = async function() { let exportedPage = currpage; let fromPage = currpage + 1; for(let i = fromPage; i<=total; i++){ //停止 if(unsafeWindow.isStop == 1) { unsafeWindow.isStop = 0; break; } //超过页数 let maxPage = currpage + (unsafeWindow.exportNum - 1); if(i > maxPage || i > total) break; //记录请求时间(毫秒) lastRequestTime = new Date().getTime(); //console.log('page ' + i, 1111, unsafeWindow.isExporting); $("#wx_export_tips").html("正在导出第"+i+"页,剩余"+(maxPage-i)+"页,请勿关闭页面...    停止"); document.querySelector("a.page_next").click(); /* let userUrl = "https://mp.weixin.qq.com/cgi-bin/user_tag?action=get_user_list&groupid=-2&begin_openid="+begin_openid+"&begin_create_time="+begin_create_time+"&limit=20&offset=20&backfoward=1&token=" + wx.data.t + "&lang=zh_CN&f=json&ajax=1&random="+Math.random(); console.log(2222, userUrl); GM_xmlhttpRequest({ method: "get", url: userUrl, onload: function(res){ if(res.status === 200){ console.log('sucess') console.log(333, res); let json = JSON.parse(res.response); let userList = json.user_list.user_info_list || []; console.log(4444, userList); for(let j in userList) { if(userList[j]) { unsafeWindow.allUserData.push(userList[j]); begin_openid = userList[j].user_openid; begin_create_time = userList[j].user_create_time; } } }else{ console.log('error') console.log(res) } }, onerror : function(err){ console.log('error') console.log(err) } }); */ exportedPage = i; await sleep(2000); //console.log('request-time', lastResponseTime, lastRequestTime, lastResponseTime - lastRequestTime); //如果接口未加载成功延迟2秒重试,最多延迟8秒 if(lastResponseTime - lastRequestTime > 0) ; else await sleep(2000); if(lastResponseTime - lastRequestTime > 0) ; else await sleep(2000); if(lastResponseTime - lastRequestTime > 0) ; else await sleep(2000); } //for end let exportedPageStr = currpage === exportedPage ? "" : "-" + exportedPage; wx_user_export(unsafeWindow.allUserData, "微信公众号粉丝第" + currpage + exportedPageStr + "页"); $("#wx_export_tips").html(""); unsafeWindow.isExporting = false; } //doGetUsers end doGetUsers(); } //导出用户数据 //unsafeWindow.wx_user_export_page = 1; unsafeWindow.wx_user_export = function(data, name) { //console.log(data,6666); let jsonData = data || unsafeWindow.userData; //列标题,逗号隔开,每一个逗号就是隔开一个单元格 let str = `昵称,关注时间,openid,头像图片\n`; for (let i in jsonData) { let user = jsonData[i]; user.user_openid = user.user_openid || user.id; if (!user.user_openid) continue; user.user_create_time = user.user_create_time || user.create_time; let localDate = new Date(user.user_create_time * 1000).toLocaleString(); user.user_name = user.user_name || user.nick_name; user.user_head_img = user.user_head_img || ""; str += `${user.user_name},${localDate},${user.user_openid},${user.user_head_img}\n`; } //encodeURIComponent解决中文乱码 let uri = 'data:text/csv;charset=utf-8,\ufeff' + encodeURIComponent(str); //通过创建a标签实现 var link = document.createElement("a"); link.href = uri; //对下载的文件命名 let currpage = parseInt($(".page_num label:first").text()-0); let today = new Date().toLocaleDateString().replace(/\//g,'-'); link.download = (name || "微信公众号粉丝第" + currpage + "页") + "-" + today + ".csv"; document.body.appendChild(link); link.click(); document.body.removeChild(link); } //http请求 function httpRequest(url, fn, method, data) { method = method || "get"; data = data || ""; GM_xmlhttpRequest({ method: method, url: url, data: data, headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", "referer": "https://mp.weixin.qq.com/cgi-bin/user_tag?action=get_all_data&lang=zh_CN&token="+wx.data.t }, onload: function(res){ if(res.status === 200){ //console.log('sucess',method,url); if(fn) fn(res); }else{ console.log('error',method,url); console.log(res); if(fn) fn(null); } }, onerror : function(err){ console.log('error',method,url); console.log(err); if(fn) fn(null); } }); } //睡眠x毫秒 let sleep = function (ms) { return new Promise(resolve => setTimeout(resolve, ms)); } //自动跳转页面 let autoGoPage = async function() { let currpage = parseInt($(".page_num label:first").text()-0); let go = parseInt($(".goto_area :text").val()-0); if(!go) return; setTimeout(function(){$(".JS_TIPS.page_tips").hide();}, 0); //前进 if ((go - currpage) > 25) { let max = Math.ceil((go-currpage)/25); let begin = currpage+25; for(let i=1;i<=max;i++) { if(begin >= go) begin = go; //console.log(i, begin, go); $(".goto_area :text").val(begin); await sleep(200); $(".page_go")[0].click(); await sleep(2000); begin += 25; } } //后退 if ((currpage - go) > 25) { let max = Math.ceil((currpage-go)/25); let begin = currpage-25; for(let i=1;i<=max;i++) { if(begin <= go) begin = go; //console.log(i, begin, go); $(".goto_area :text").val(begin); await sleep(200); $(".page_go")[0].click(); await sleep(2000); begin -= 25; } } } //第一页用户数据(从js变量获取) window.addEventListener("load", function() { if (wx && wx.cgiData && wx.cgiData.user_list) { let userList = wx.cgiData.user_list || []; addFllowInfo(userList); unsafeWindow.userData = userList; //绑定页面跳转按钮点击事件 $(".page_go")[0].addEventListener("click", function(e) { //如果手动点击则自动跳转目标页 if (e.isTrusted) { autoGoPage(); } }); ////首页获取图片地址 //获取第一个用户的信息 let userInfoUrl = "https://mp.weixin.qq.com/cgi-bin/user_tag?action=get_fans_info"; httpRequest(userInfoUrl, function(res){ if(res && res.response){ let data = JSON.parse(res.response); //console.log(11111,userInfoUrl,data); let userList = data.user_list.user_info_list || []; if(userList.length > 0) { let userInfo = userList[0]; setTimeout(function(){ wx.cgiData.user_list[0].user_head_img = userInfo.user_head_img; }, 500); } } }, "post", "token="+wx.data.t+"&lang=zh_CN&f=json&ajax=1&random="+Math.random()+"&user_openid="+wx.cgiData.user_list[0].id); //获取用户列表 let userListUrl = "https://mp.weixin.qq.com/cgi-bin/user_tag?action=get_user_list&groupid=-2&begin_openid="+wx.cgiData.user_list[0].id+"&begin_create_time="+wx.cgiData.user_list[0].create_time+"&limit=20&offset=0&backfoward=1&token=" + wx.data.t + "&lang=zh_CN&f=json&ajax=1&random="+Math.random(); httpRequest(userListUrl, function(res){ if(res && res.response){ let data = JSON.parse(res.response); //console.log(2222,userListUrl,data); let userList = data.user_list.user_info_list || []; let userMap = {}; if(userList.length > 0) { for (let n in userList) { if(userList[n] && userList[n].user_openid) userMap[userList[n].user_openid] = userList[n].user_head_img; } } setTimeout(function(){ for (let i in wx.cgiData.user_list) { let item = wx.cgiData.user_list[i]; if(userMap[item.id]) item.user_head_img = userMap[item.id]; wx.cgiData.user_list[i] = item; } }, 500); } }, "get"); } }); })();