如何通过C#使我的网络爬虫登录到这个网站

84

我有一个应用程序,可以读取网站上源代码的部分内容。这一切都很顺利;但问题在于,所涉及的页面要求用户登录才能访问此源代码。我的程序需要一种方法来最初登录到该网站-完成后,我将能够访问和读取源代码。

需要登录的网站是: mmoinn.com/index.do?PageModule=UsersLogin


所以,我可以想到很多方法来做到这一点... C#程序是直接通过HTTP从服务器请求'code'还是通过浏览器应用程序或其他方式?需要更多的信息。 - Mitch Baker
该程序使用WebClient.DownloadString("URL")。 - Dillon
4个回答

116
你可以继续使用WebClient进行POST请求(而不是GET,这是你目前在DownloadString中使用的HTTP动词),但我认为使用略低级别的WebRequest和WebResponse类会更容易一些。
这有两个部分-第一个是提交登录表单,第二个是恢复“Set-cookie”标头并将其作为“Cookie”与您的GET请求一起发送回服务器。服务器将使用此cookie从现在开始识别您(假设它正在使用基于cookie的身份验证,我相当有信心,因为该页面返回一个包含“PHPSESSID”的Set-cookie标头)。

提交登录表单

模拟表单提交很容易,只需要按照以下方式格式化您的提交数据:

field1=value1&field2=value2

使用WebRequest和我从Scott Hanselman那里改编的代码,以下是如何将表单数据POST到您的登录表单:

string formUrl = "http://www.mmoinn.com/index.do?PageModule=UsersAction&Action=UsersLogin"; // NOTE: This is the URL the form POSTs to, not the URL of the form (you can find this in the "action" attribute of the HTML's form tag
string formParams = string.Format("email_address={0}&password={1}", "your email", "your password");
string cookieHeader;
WebRequest req = WebRequest.Create(formUrl);
req.ContentType = "application/x-www-form-urlencoded";
req.Method = "POST";
byte[] bytes = Encoding.ASCII.GetBytes(formParams);
req.ContentLength = bytes.Length;
using (Stream os = req.GetRequestStream())
{
    os.Write(bytes, 0, bytes.Length);
}
WebResponse resp = req.GetResponse();
cookieHeader = resp.Headers["Set-cookie"];

以下是登录表单中Set-cookie头部应该看到的示例:
PHPSESSID=c4812cffcf2c45e0357a5a93c137642e; path=/; domain=.mmoinn.com,wowmine_referer=directenter; path=/; domain=.mmoinn.com,lang=en; path=/;domain=.mmoinn.com,adt_usertype=other,adt_host=-

获取登录后的页面

现在,您可以执行需要登录才能访问的页面的 GET 请求。

string pageSource;
string getUrl = "the url of the page behind the login";
WebRequest getRequest = WebRequest.Create(getUrl);
getRequest.Headers.Add("Cookie", cookieHeader);
WebResponse getResponse = getRequest.GetResponse();
using (StreamReader sr = new StreamReader(getResponse.GetResponseStream()))
{
    pageSource = sr.ReadToEnd();
}

编辑:

如果您需要查看第一个POST的结果,您可以使用以下方法恢复其返回的HTML:

using (StreamReader sr = new StreamReader(resp.GetResponseStream()))
{
    pageSource = sr.ReadToEnd();
}

将此代码直接放置在cookieHeader = resp.Headers["Set-cookie"];下方,然后检查pageSource中保存的字符串。


代码应该可以直接使用。服务器设置cookie(在Set-cookie中),客户端(也就是你)将cookie作为Cookie发送回来。首先要检查的是第一个POST是否真正地登录了你,你可能会发现服务器期望在你的表单POST中有另一个字段(听起来很奇怪,但有时你需要一个带有按钮名称的空字段)。我已经更新了帖子,以显示如何查看POST的结果。 - Matt Brindley
1
我该如何确定用户是否成功进行了身份验证? - Cyral
2
我知道我们不应该在这里表达感谢,但是你救了我的命! +1 - Owen James
找不到类型或命名空间名称“WebRequest”的错误。 - vee
简单问题:我正在尝试从我的路由器读取数据,我认为我已经成功进入了路由器,但无法读取下一页以查看其中的数据?我按原样复制了代码...我错过了什么吗? - Korenron
显示剩余3条评论

40

通过创建一个从WebClient派生的类,重写其GetWebRequest方法并在其上设置CookieContainer对象,您可以简化许多事情。如果始终设置相同的CookieContainer实例,则cookie管理将自动处理。

但是,在发送HttpWebRequest之前获取它的唯一方法是从WebClient继承并覆盖该方法。

public class CookieAwareWebClient : WebClient
{
    private CookieContainer cookie = new CookieContainer();

    protected override WebRequest GetWebRequest(Uri address)
    {
        WebRequest request = base.GetWebRequest(address);
        if (request is HttpWebRequest)
        {
            (request as HttpWebRequest).CookieContainer = cookie;
        }
        return request;
    }
}

var client = new CookieAwareWebClient();
client.BaseAddress = @"https://www.site.com/any/base/url/";
var loginData = new NameValueCollection();
loginData.Add("login", "YourLogin");
loginData.Add("password", "YourPassword");
client.UploadValues("login.php", "POST", loginData);

//Now you are logged in and can request pages    
string htmlSource = client.DownloadString("index.php");

调试时,(将其公开)的 cookie 始终为空。该网站肯定在我下载的页面上分发 cookies。 - C4d
谢谢,经过几个小时的寻找解决方案,这个方法可行! - Essej

9

Matthew Brindley,你的代码对于我需要的某些网站(需要登录)很有效,但我需要更改为HttpWebRequestHttpWebResponse,否则我会从远程服务器收到404错误请求。此外,我想分享一下使用您的代码的解决方法,就是我尝试登录基于moodle的网站时,在您的步骤“获取登录表单后面的页面”时无法正常工作,因为在成功提交登录后,标头'Set-Cookie'没有返回任何内容,尽管其他网站有返回。

所以我认为这是我们需要存储cookies以进行下一个请求的地方,所以我添加了这个。


在“提交登录表单”代码块中:

var cookies = new CookieContainer();
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(formUrl);
req.CookieContainer = cookies;


针对“登录表单后获取页面”的问题:

HttpWebRequest getRequest = (HttpWebRequest)WebRequest.Create(getUrl);
getRequest.CookieContainer = new CookieContainer();
getRequest.CookieContainer.Add(resp.Cookies);
getRequest.Headers.Add("Cookie", cookieHeader);

这样做可以让我"登录"并获取基于Moodle的网站上“登录页面”(即包含在登录状态下才能查看的内容)的源代码。使用CookieContainer和HTTPCookies可能有一些模糊之处,因为我们可能需要先检查是否在发送请求到服务器之前已经保存了一组 cookie。无论如何,这种方法确实有效,以下链接提供了有关WebRequestWebResponse的详细信息、示例项目和教程:
.NET中检索HTTP内容
如何在.NET中使用HttpWebRequest和HttpWebResponse

2
有时候,关闭AllowAutoRedirect并将登录的POST请求和页面的GET请求设置为相同的用户代理可能会有帮助。
request.UserAgent = userAgent;
request.AllowAutoRedirect = false;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接