[转载]c#实现QQ群成员列表导出及邮件群发之群列表及群成员获取 – 狼性法则 – 博客园.
前言
通过前两篇的代码编写已经能正常模拟QQ登陆,拿到cookie也就是我们进行以后相关操作的金钥匙。这篇文章将通过代码的方式去获取登陆QQ账号的群列表,某群里面的群成员列表。
本文重点:
1、抓包获取QQ群列表访问地址
2、抓包获取QQ群成员列表
3、参数值计算,gtk的计算方法(网上几乎找不到的计算方法)
4、处理返回值
本文完成这一系列也基本算是完成了,到此篇为止,可正常获取群成员,当然也就是拿到了QQ邮箱,如果想进行其他操作的话,同样也可以用次方式来实现。
抓包
1、获取QQ群列表
首先我们用登陆成功的QQ去访问我们的群空间,群空间的头部有我的群,在此我们可以看到登陆QQ上面所有的群列表,也就是说此页面必有返回该列表的请求地址,做web开发的大体都差不多,这种东西多用Js处理,相信我们也可以抓到此地址,如下图所示。
相比而言这个地方的抓包不需要挨个去看了,这次给的JS请求地址很直观通过名字我们一眼就可以看出是群列表,在抓包过程中我们会发现有一条get_group_list的地址,不用说这个就是了,查看这个js返回的数据刚好便是群列表,如下图
右侧group便是登陆QQ上所有的qq群,包含总数目等信息。
相关请求地址:
http://qun.qzone.qq.com/cgi-bin/get_group_list?uin={0}&ua=Mozilla%2F5.0%20(Windows%20NT%206.1%3B
%20WOW64)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F28.0.1500.95%20Safari
%2F537.36&random={1}&g_tk={2}
参数0:账号,参数1:随即数,参数2:此次访问的gtk值
2、获取访问群空间的成员列表
访问具体群空间,在群空间右侧有群成员选项,选择以后变回返回所有的群成员,如图
在点击群成员列表的时候我们通过抓包工具可抓起返回数据的地址,这个也很明显,很直观get_group_member,一看便知是返回群成员,查看返回的数据,下面给出fiddler和谷歌浏览器返回的数据格式
请求地址:
http://qun.qzone.qq.com/cgi-bin/get_group_member?
uin={0}&groupid={2}&random={3}&g_tk={4}
参数0:账号,参数1:群号,参数2:随即数,参数3:此次访问的gtk值
代码部分
1、gtk参数的计算(此参数的计算方法几乎没有)
通过上述抓包我们看到,数据包主体部分最后一个参数就是g_tk值,一般是一串数字。那这个值到底怎么算出来的呢?因 为我们在网页登录QQ的时候,腾讯都会通过cookies里的skey值来计算,用js来算。既然在运算的时候执行了js脚本,那么我们就可以在抓包中获 得。那g_tk是通过什么算法算出来的?其实很简单,当我们得到skey后,循环取单字符的二进制并取左值.累加之后就得到后面的g_tk值了,这听上去 很复杂,不过算法不用我们自己写,我们只需要执行在腾讯网页登录的时候所执行的那个js脚本就可以了。在这里我已经将此算法转化成C#代码:
/// <summary> /// 计算gtk /// </summary> /// <returns></returns> public static Int32 GetGTK(List<Cookie> cookies) { int gtk = 0; foreach (var item in cookies) { if (item.Name == "skey") { int hash = 5381; string str = item.Value; for (int i = 0, len = str.Length; i < len; ++i) { hash += (hash << 5) + str.ElementAt(i); } gtk = hash & 0x7fffffff; } } return gtk; }
同时也给出一个遍历CookieContainer的方法:
/// <summary> /// 遍历CookieContainer /// </summary> /// <param name="cc"></param> /// <returns></returns> public static List<Cookie> GetAllCookies(CookieContainer cc) { List<Cookie> lstCookies = new List<Cookie>(); Hashtable table = (Hashtable)cc.GetType().InvokeMember("m_domainTable", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.Instance, null, cc, new object[] { }); foreach (object pathList in table.Values) { SortedList lstCookieCol = (SortedList)pathList.GetType().InvokeMember("m_list", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.Instance, null, pathList, new object[] { }); foreach (CookieCollection colCookies in lstCookieCol.Values) foreach (Cookie c in colCookies) lstCookies.Add(c); } return lstCookies; }
唯一一个重要的参数解决以后便是请求地址,返回数据了,收获果实的时候了。
2、获取群列表
下面先写两个处理返回Json字符串的方法,本文没有用第三方的处理工具,二是用.net 3.5以后出现的System.Web.Extensions.dll中提供的序列化方法实现。
//json解析群列表 public class QQGropJson { public int code { get; set; } public int defaults { get; set; } public QQGrouplist data { get; set; } public string message { get; set; } public int subcode { get; set; } } public class QQGroupInfo { public int count { get; set; } public int flag { get; set; } public string groupid { get; set; } public string groupname { get; set; } } public class QQGrouplist { public List<QQGroupInfo> group { get; set; } public int guid { get; set; } public int total { get; set; } } //以下三个类解析群成员 public class QQMember { public string iscreator { get; set; } public string ismanager { get; set; } public string nick { get; set; } public string uin { get; set; } } public class QQGroup { public int code { get; set; } public int subcode { get; set; } public string message { get; set; } public int defaults { get; set; } public QQInfo data { get; set; } public int level { get; set; } public string nick { get; set; } public int option { get; set; } public int total { get; set; } } public class QQInfo { public int alpha { set; get; } public int bbscount { get; set; } public int classs { get; set; } public string createtime { get; set; } public int filecount { get; set; } public string fingermemo { get; set; } public string groupmemo { get; set; } public string groupname { get; set; } public List<QQMember> item { get; set; } }
过滤返回的_callback标签:
/// <summary> /// 过滤返回的标签_callback(); /// </summary> /// <param name="retstr"></param> /// <returns></returns> public static string FilterReturnstring(string retstr) { string result = retstr.Substring(10, retstr.Length - 12); return result; }
解析返回的json数据
//解析json字符串返回群信息 public static List<QQGroupInfo> GetGropList(string jsonstring) { var js = new JavaScriptSerializer(); QQGropJson grouplist = new QQGropJson(); grouplist = js.Deserialize<QQGropJson>(jsonstring); return grouplist.data.group; }
获取群列表
var list = HttpHelper.GetAllCookies(_cookie); string gtk = HttpHelper.GetGTK(list).ToString(); ////群列表 string grouplisturl = string.Format(@"http://qun.qzone.qq.com/cgi-bin/get_group_list?uin={0}&random={1}&g_tk={2}",usernum,rand.NextDouble(),gtk); string tmp = HttpHelper.GetHtml(grouplisturl, _cookie); List<QQGroupInfo> grouplist = HttpHelper.GetGropList(HttpHelper.FilterReturnstring(tmp));
解析群成员列表json的方法
/// <summary> /// 群成员列表 /// </summary> /// <param name="jsonstring"></param> /// <returns></returns> public static List<QQMember> GetMemberList(string jsonstring) { var js = new JavaScriptSerializer(); QQGroup group = new QQGroup(); group = js.Deserialize<QQGroup>(jsonstring); return group.data.item; }
以上方法grouplist便是群列表账号
获取某一群的成员列表
////群成员 var list = HttpHelper.GetAllCookies(_cookie); string gtk = HttpHelper.GetGTK(list).ToString(); string groupnumber = this.cmbQQgroup.SelectedValue.ToString(); string groupmemberlist = string.Format(@"http://qun.qzone.qq.com/cgi-bin/get_group_member?uin={0}&groupid={1}&random={2}&g_tk={3}", usernum, groupnumber,rand.NextDouble(),gtk); string tmp = HttpHelper.GetHtml(groupmemberlist, _cookie); grouplist = HttpHelper.GetMemberList(HttpHelper.FilterReturnstring(tmp));//成员列表
文章到此,群成员拿到了,同时也拿到了QQ邮箱。
结语
本文的重点在于拿着cooke这把钥匙去开门,相对比较简单,唯一一个比较难的就是gtk参数的计算方法,这个在QQ空间对日志等操作的时候是必不可少的参数。
时间仓促,代码及文章比较杂乱,有什么出错的地方欢迎指出。若资料有用,帮忙顶一下。