您的位置:首页 > 运维架构 > 网站架构

多个账户模拟登录---c#异步模拟登录网站并采集网页

2013-02-01 12:42 555 查看
最近需要做一些数据分析,正好在做几个c#项目,于是直接用form程序实现模拟登录。

初步分析:

由于对模拟登录不是很理解,在网上查询后,热心朋友指出(http://bbs.csdn.net/topics/390352393?page=1#post-393522161)

“所谓登录,就是第一次访问,服务器返回一个set-cookie指令(不保存到cookie存储中),客户端浏览器在之后的访问中加上它,而服务器把所有这个set-cookie返回的cookie(很多web server将它命名为sessionid一类),识别为同一系列的请求,也就是一个会话,而服务器通过跟踪会话,当用户登录成功,将它存入会话中,判断出后续的操作是“已经登录的”。”

对于我这个工作而言,本次的模拟登录并采集信息关键在于每次登录都要保存下当时的cookie。而在c#中保存cookie,既可以保存
CookieContainer
,也可以保存
string
CookieHeader = request.CookieContainer.GetCookieHeader(request.RequestUri)
。后面是字符串,我感觉比较简单,就保存后面的
CookieHeader
。


同时考虑到,这个项目我需要登录的网站,我手中有15个帐号,以后或许更加多,所以我考虑多线程同时登录采集。如果一个一个轮询登录采集,则太慢了。考虑多线程的时候,也有两个选择,一个是自己写多线程分别实现登录,采集,二是使用c#的HttpWebRequest、HttpWebResponse异步操作。


我选择使用
HttpWebRequest
的异步操作,由于我要登录的账户很多,这些就使用task多线程。



启动函数如下:

void InitCookie()
{
string[] users = File.ReadAllLines("user.txt");
List<string> userList = new List<string>(users);
users = userList.Distinct().ToArray();
userNum = users.Length;
for (int i = 0;i < users.Length; i++)
{
string u = users[i];
if (u.Length > 0)
{
ThreadStatus.Add(u.Trim().ToUpper(), 0);//记录线程状态
Task.Factory.StartNew(() => Send(u));
//break;
}
}
}




1、获取登录页面html

//取得登录页面
void Send(string user)
{
string loginUrl = "http://xxx.com/Login.aspx?"+user;
HttpWebRequest request = null;
CookieContainer cc = new CookieContainer();
try
{
request = (HttpWebRequest)WebRequest.Create(loginUrl);//实例化web访问类
request.Method = "POST";//数据提交方式为POST
//模拟头
request.ContentType = "application/x-www-form-urlencoded";
request.AllowAutoRedirect = false;
request.CookieContainer = cc;
request.KeepAlive = true;
//提交请求
request.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), request);

}
catch (Exception e)
{
string adsad = e.ToString();
//第一次POST出错;
}
}


当执行send(url)后,如果提交成功,则根据网速,过一会就会触发GetRequestStreamCallback回调函数,在这个函数里,取得了登录页面信息。

上诉url中我在尾巴加了一句“+user”,是为了给回调函数传值,因为每次登录的账户不同,我有相应的字典来记录不同的密码。

在这个页面之中,提交账户密码等post数据,进行模拟登录操作。如下

//提交用户名密码登录
void GetRequestStreamCallback(IAsyncResult ar)
{
HttpWebRequest request = (HttpWebRequest)ar.AsyncState;
Stream postStream = request.EndGetRequestStream(ar);
string user = request.RequestUri.ToString().Split('?')[1];
string pwd = up[user];
string postdata = "....%3D%3D&__E=%2FwE&User=" + user + "&Pass=" + pwd + "&Login=Login";
byte[] byteArray = Encoding.UTF8.GetBytes(postdata);
postStream.Write(byteArray, 0, postdata.Length);
postStream.Close();
request.BeginGetResponse(new AsyncCallback(GetResponseCallbackCookie), request);
ShowLabel("正在登录"+user+"...", label9, Color.Black);
// PostData(request);

}
这里需要说明的,就是string postdata字符串的原因,因为每个网站的登录方式不一样,有的是get,有的是post,还设计数据格式不一样,需要一些抓包工具去分析这个数据格式以及相应的url。在这里我使用了火狐浏览器的firebug插件,当用火狐浏览器打开登录页面时,激活firebug,随便输入一个正确的用户名密码,随着浏览器的登录操作,firebug会自动分析相应的post数据格式。在firebug的网络选项下有个post,点击就可以看到post。我这个string post就是从那里复制下来的。

这个函数同样使用了异步提交BeginGetResponse的方式,当登录成功或失败都会触发GetResponseCallbackCookie回调函数。在这个回调函数中,如果登录成功,则需要立即保存cookie。代码如下:

//登录成功获取cookie
void GetResponseCallbackCookie(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
string asd= asynchronousResult.ToString();
//string asdasd = request.Credentials.ToString();
// End the operation
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
Stream streamResponse = response.GetResponseStream();
StreamReader streamRead = new StreamReader(streamResponse);
string responseString = streamRead.ReadToEnd();
streamResponse.Close();
streamRead.Close();

// Release the HttpWebResponse
response.Close();
//保存cookie信息
string CookieHeader = request.CookieContainer.GetCookieHeader(request.RequestUri);
if (CookieHeader.Length > 5)//登录成功
{
string user = request.RequestUri.ToString().Split('?')[1];
User.Add(user.Trim().ToUpper(),CookieHeader);
ShowLabel("登录" + user + "成功.", label9, Color.Blue);
//多线程下载当前用户的数据相应数据到数据库
Task.Factory.StartNew(() => GetWebByDate(user, CookieHeader));
//Task
}


当某个账户登录成功后,马上把cookieheader 保存在全局变量User 字典里,同时不必等别的帐号,直接采集当前账户的信息,如上面GetWebByDate函数就是采集函数,这里是重启一个线程。使用了线程工厂的方法,可以轻而易举的给多线程函数传多个参数。

在采集函数中,其实安装正常的获取html的方法即可,在这里只需要加上cookieheader 就可以以登录的身份去取得信息了。

如:

//异步获取网页信息
void GetWeb(string user, string Cookiesstr)
{
HttpWebRequest request = null;
try
{
request = (HttpWebRequest)WebRequest.Create("...."+user);//实例化web访问类
request.Method = "GET";//数据提交方式为POST
//模拟头
request.ContentType = "application/x-www-form-urlencoded";
request.AllowAutoRedirect = true;
request.KeepAlive = true;
string user = User.Where(p => p.Value == Cookiesstr).First().Key;
request.Headers.Add("Cookie:" + Cookiesstr);
request.BeginGetResponse(new AsyncCallback(GetWebCallback), request);

}
catch (Exception e)
{
string adsad = e.ToString();
//第一次POST出错;
}
}




这里需要注意的就是提交方式必须是“GET”,具体看网站内容来定,但一般登录之后获取页面信息多为get,我原来这里写成了post则无法采集。此外,加上cookie是这句

request.Headers.Add("Cookie:" + Cookiesstr)。这样就能十分顺利的采集信息了。我后面还有很多的采集不同信息的函数以及采集到信息后有很复杂的函数

处理信息,最后插入数据库中。我大概采集所有账户一次的数据,大概有1万多条记录,如果采集范围大一点,则有5--10万条数据,那么,如何快速的插入mysql数据库,

对我而言又是一个问题了。

下文:c#中往mysql里批量插入上万条数据
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: