最近在搞微信客户端开发,就找到这个文章,感觉还挺靠谱,希望对大家有帮助
微信公众平台开发之模拟登陆实现群发
近来闲着无聊,正在学习WPF的过程中,刚好手头正有一个WeiXin公众平台开发项目,于是利用了WPF写了一个模拟登陆然后可以进行群发图文或者文字消息的WPF程序供大家参考。
原理:通过程序向WeiXin公众平台post公众平台的账号和密码,登陆进入自己的公众平台账号,然后抓取网页数据获取关注用户列表的每个用户的id(不同于微信消息接口使用文档里的openid),然后程序里利用循环对单个用户进行发送消息,从而达到群发的目的。
注:由于TX公司现在在公众平台上也限制了必须用户主动向公众平台发送消息,公众平台也才能单个向用户发送消息,所以目前群发只能对当天24小时内向公众平台主动发送了消息的用户发送了。
模拟登陆的步骤和代码说明:
-
分析WX公众平台登陆过程利用chrome的审查元素。
如上图在输入框故意输入错误的账号和密码,可以看到form data里红线标注的地方浏览器 通过post方式提交账号和密码,密码是通过MD5进行进行摘要算法了的
-
进行代码编写模拟登陆。c#的代码如下:
public static bool ExecLogin(string name, string pass)//登陆微信公众平台函数
{
bool result = false;
string password = GetMd5Str32(pass).ToUpper();
string padata = "username=" + name + "&pwd=" + password + "&imgcode=&f=json";
string url = "http://mp.weixin.qq.com/cgi-bin/login?lang=zh_CN ";//请求登录的URL
try
{
CookieContainer cc = new CookieContainer();//接收缓存
byte[] byteArray = Encoding.UTF8.GetBytes(padata); // 转化
HttpWebRequest webRequest2 = (HttpWebRequest)WebRequest.Create(url); //新建一个WebRequest对象用来请求或者响应url
webRequest2.CookieContainer = cc; //保存cookie
webRequest2.Method = "POST"; //请求方式是POST
webRequest2.ContentType = "application/x-www-form-urlencoded"; //请求的内容格式为application/x-www-form-urlencoded
webRequest2.ContentLength = byteArray.Length;
webRequest2.Referer = "https://mp.weixin.qq.com/";
Stream newStream = webRequest2.GetRequestStream(); //返回用于将数据写入 Internet 资源的 Stream。
// Send the data.
newStream.Write(byteArray, 0, byteArray.Length); //写入参数
newStream.Close();
HttpWebResponse response2 = (HttpWebResponse)webRequest2.GetResponse();
StreamReader sr2 = new StreamReader(response2.GetResponseStream(), Encoding.Default);
string text2 = sr2.ReadToEnd();
//此处用到了newtonsoft来序列化
LoginInfo.Err = text2;
WeiXinRetInfo retinfo = Newtonsoft.Json.JsonConvert.DeserializeObject<WeiXinRetInfo>(text2);
string token = string.Empty;
if (retinfo.ErrMsg.Length > 0)
{
if (retinfo.ErrMsg.Contains("ok"))
{
token = retinfo.ErrMsg.Split(new char[] { '&' })[2].Split(new char[] { '=' })[1].ToString();//取得token
LoginInfo.LoginCookie = cc;
LoginInfo.CreateDate = DateTime.Now;
LoginInfo.Token = token;
result = true;
}
else
{
result = false;
}
}
}
catch (Exception ex)
{
throw new Exception(ex.StackTrace);
}
return result;
}
登陆成功返回true失败返回false
MD5摘要算法:
static string GetMd5Str32(string str) //MD5摘要算法
{
MD5CryptoServiceProvider md5Hasher = new MD5CryptoServiceProvider();
// Convert the input string to a byte array and compute the hash.
char[] temp = str.ToCharArray();
byte[] buf = new byte[temp.Length];
for (int i = 0; i < temp.Length; i++)
{
buf[i] = (byte)temp[i];
}
byte[] data = md5Hasher.ComputeHash(buf);
// Create a new Stringbuilder to collect the bytes
// and create a string.
StringBuilder sBuilder = new StringBuilder();
// Loop through each byte of the hashed data
// and format each one as a hexadecimal string.
for (int i = 0; i < data.Length; i++)
{
sBuilder.Append(data[i].ToString("x2"));
}
// Return the hexadecimal string.
return sBuilder.ToString();
}
保存登陆成返回信息的类:
public static class LoginInfo//保存登陆后返回的信息
{
/// <summary>
/// 登录后得到的令牌
/// </summary>
public static string Token { get; set; }
/// <summary>
/// 登录后得到的cookie
/// </summary>
public static CookieContainer LoginCookie { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public static DateTime CreateDate { get; set; }
public static string Err { get; set; }
}
登录中LoginInfo.Err返回信息代表意义如下:
"-1":"系统错误。
"-2":"帐号或密码错误
"-3":"密码错误。"
"-4":"不存在该帐户。"
"-5":"访问受限。"
"-6":"需要输入验证码"
"-7":"此帐号已绑定私人微信号,不可用于公众平台登录。"
"-8":"邮箱已存在。"
"-32":"验证码输入错误"
"-200":n="因频繁提交虚假资料,该帐号被拒绝登录。"
"-94":"请使用邮箱登陆。"
"10":"该公众会议号已经过期,无法再登录使用。"
"65201":"65202":"成功登陆,正在跳转..."
"0":n="成功登陆,正在跳转..."
default:"未知的返回。"
-
模拟登陆成功后便保存了登陆时的cookie和token接下来请求后面的网页会用到。接下来我们要做的就是获取关注用户信息列表。获取用户信息的关键是获取用户的fakeid因为在公众平台上我们是通过fakeid唯一标示每个用户,从而向用户发送信息。步骤如下:
1、这次使用get 的方式向公众平台用户管理列表网页发出请求
2、然后微信公众平台会返回一个该链接网页源代码给请求的程序(读者们可以登录自己的微信公众平台,点击用户管理,然后右键查看网页源代码与程序获得数据是一样的)
3、利用正则表达式从返回的网页源代码字符串匹配用户列表所有用户的信息,然后保存下来。
以下是c#源代码:
public static List<SingleGroup> getAllGroupInfo()//获取所有分组数据存储在List里
{
try
{
CookieContainer cookie = null;
string token = null;
cookie = WeiXinLogin.LoginInfo.LoginCookie;//取得cookie
token = WeiXinLogin.LoginInfo.Token;//取得token
/* 1.token此参数为上面的token 2.pagesize此参数为每一页显示的记录条数
3.pageid为当前的页数,4.groupid为微信公众平台的用户分组的组id*/
string Url = "https://mp.weixin.qq.com/cgi-bin/contactmanage?t=user/index&pagesize=10&pageidx=0&type=0&groupid=0&token=" + token + "&lang=zh_CN";
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(Url);//Url为获取用户信息的链接
webRequest.CookieContainer = cookie;
webRequest.ContentType = "text/html; charset=UTF-8";
webRequest.Method = "GET";
webRequest.UserAgent = "Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1";
webRequest.ContentType = "application/x-www-form-urlencoded";
HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding("UTF-8"));
string text = sr.ReadToEnd();
MatchCollection mcGroup;
Regex GroupRex = new Regex(@"(?<=""groups"":).*(?=}).groups)");
mcGroup = GroupRex.Matches(text);
List<SingleGroup> allgroupinfo = new List<SingleGroup>();
if (mcGroup.Count != 0)
{
JArray groupjarray = (JArray)JsonConvert.DeserializeObject(mcGroup[0].Value);
for (int i = 0; i < groupjarray.Count; i++)
{
getEachGroupInfo(groupjarray[i]["id"].ToString(), groupjarray[i]["cnt"].ToString(), groupjarray[i]["name"].ToString(), ref allgroupinfo);
}
}
return allgroupinfo;
}
catch (Exception ex)
{
throw new Exception(ex.StackTrace);
}
}
public static void getEachGroupInfo(string groupid, string count, string group_name, ref List<SingleGroup> groupdata)//获取单个分组数据
{
CookieContainer cookie = null;
string token = null;
cookie = WeiXinLogin.LoginInfo.LoginCookie;//取得cookie
token = WeiXinLogin.LoginInfo.Token;//取得token
SingleGroup obj_single = new SingleGroup();
obj_single.group_name = group_name;
string TotalUser;
if (count != "0")
{
TotalUser = count;
}
else
{
return;
}
string Url = "https://mp.weixin.qq.com/cgi-bin/contactmanage?t=user/index&pagesize=" + TotalUser + "&pageidx=0&type=0&groupid=" + groupid.Trim() + "&token=" + token + "&lang=zh_CN";
HttpWebRequest webRequest2 = (HttpWebRequest)WebRequest.Create(Url);
webRequest2.CookieContainer = cookie;
webRequest2.ContentType = "text/html; charset=UTF-8";
webRequest2.Method = "GET";
webRequest2.UserAgent = "Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1";
webRequest2.ContentType = "application/x-www-form-urlencoded";
HttpWebResponse response2 = (HttpWebResponse)webRequest2.GetResponse();
StreamReader sr2 = new StreamReader(response2.GetResponseStream(), Encoding.GetEncoding("UTF-8"));
string text2 = sr2.ReadToEnd();
MatchCollection mcJsonData;
Regex rexJsonData = new Regex(@"(?<=friendsList : ({""contacts"":).*(?=}).contacts)");
mcJsonData = rexJsonData.Matches(text2);
if (mcJsonData.Count != 0)
{
JArray JsonArray = (JArray)JsonConvert.DeserializeObject(mcJsonData[0].Value);
obj_single.groupdata = JsonArray;
groupdata.Add(obj_single);
}
}
}
public class SingleGroup//存储一个分组的信息的类
{
public string group_name;
public JArray groupdata;
}
-
获取成功用户的fakeid之后我们就可以将其存在本地数据库了,然后便可以通过fakeid对用户发送消息了。原理大同小异都是通过post或者get对相关链接进行请求。不在详述
c#代码如下:
public static bool SendMessage(string Message, string fakeid, int flag)//发送消息
{
bool result = false;
CookieContainer cookie = null;
string token = null;
cookie = WeiXinLogin.LoginInfo.LoginCookie;//取得cookie
token = WeiXinLogin.LoginInfo.Token;//取得token
string strMsg = "";
string padate = "";
if (flag == 0)//发送文字
{
strMsg = Message;
padate = "type=1&content=" + strMsg + "&tofakeid=" + fakeid + "&imgcode=&token=" + token + "&lang=zh_CN&random=0.4486911059357226&t=ajax-response";
}
if (flag == 1)//发送图文
{
padate = "type=10&app_id=" + Message + "&tofakeid=" + fakeid + "&appmsgid=" + Message + "&imgcode=&token=" + token + "&lang=zh_CN&random=0.22518408996984363&f=json&ajax=1&t=ajax-response";
}
string url = "https://mp.weixin.qq.com/cgi-bin/singlesend";
byte[] byteArray = Encoding.UTF8.GetBytes(padate); // 转化
HttpWebRequest webRequest2 = (HttpWebRequest)WebRequest.Create(url);
webRequest2.CookieContainer = cookie; //登录时得到的缓存
//webRequest2.Referer = "https://mp.weixin.qq.com/cgi-bin/singlemsgpage?fromfakeid=" + fakeid + "&count=20&t=wxm-singlechat&token=" + token + "&token=" + token + "&lang=zh_CN";
webRequest2.Referer = "https://mp.weixin.qq.com/cgi-bin/singlesendpage?t=message/send&action=index&tofakeid=" + fakeid + "&token=" + token + "&lang=zh_CN";
webRequest2.Method = "POST";
webRequest2.UserAgent = "Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1";
webRequest2.ContentType = "application/x-www-form-urlencoded";
webRequest2.ContentLength = byteArray.Length;
Stream newStream = webRequest2.GetRequestStream();
// Send the data.
newStream.Write(byteArray, 0, byteArray.Length); //写入参数
newStream.Close();
HttpWebResponse response2 = (HttpWebResponse)webRequest2.GetResponse();
StreamReader sr2 = new StreamReader(response2.GetResponseStream(), Encoding.GetEncoding("UTF-8"));
string text2 = sr2.ReadToEnd();
if (text2.Contains("ok"))
{
result = true;
}
return result;
}
-
下面为实例展示自己的WPF写的模拟登陆然后群发消息的程序
1、登陆界面
2、登陆成功进入界面
操作界面更新关注用户会把关注用户信息列表写入本地数据库,更新图文消息可以获取公众平台上图文消息的链接,左下角的表情符点击后会呈现和公众平台类似的图文消息选择界面
3、发送文字消息
4、发送图文消息选择界面
-
源代码下载地址链接我的百度网盘: http://pan.baidu.com/s/1szGah 密码: ss8e
注意:需要修改app.config里的数据库连接字符串为自己本地的数据库链接字符串就可以使用了。