【问题标题】:Scrape ASP.NET website with paging使用分页抓取 ASP.NET 网站
【发布时间】:2015-04-19 08:14:39
【问题描述】:

我正在尝试抓取具有分页功能的基本 asp.net 目录网站。

该网站有 50 多个页面,在任何一个页面上最多包含 10 个分页链接。

我正在使用 fiddler 来帮助复制使用浏览器发布的所有参数、变量、表单字段、cookie 等。我在两个帖子之间看到的唯一区别是__EVENTVALIDATION 值。

使用 HttpWebRequest 我总是具有相同的值,而通过浏览器在每次点击时都会发生变化。

使用 HttpWebRequest 我正确获得了前 10 个页面,但是以下所有页面都将我重定向到主页。 Bellow 是回发 javascript,前 10 个之后的链接始终相同。

javascript:__doPostBack('CT_Main_2$gvDirectorySearch$ctl53$ctl00$ctl11','')

知道为什么__EVENTVALIDATION 不会随 HttpWebRequest 改变吗?

【问题讨论】:

  • 不幸的是,它不是同一个问题。如果您注意到在我的情况下我没有 :__doPostBack('gvEmployees','Page$2')">2 Page$2 参数。我相信页面之间的差异是通过 EventvValidation 字段确定的

标签: c# asp.net httpwebrequest


【解决方案1】:

从你的描述来看,听起来像是anti-forgery tokenanti-forgery token是用来防止cross-site request forgery (XSRF)攻击的..

对于利用防伪令牌的网站,它通常会在客户端的浏览器中设置一个 cookie,并且它会期望与正在发布的表单中的参数完全相同的令牌。

要克服它,您需要发送服务器在后续请求中设置的令牌,您还需要扫描 HTML 表单以查找相同的令牌并将其包含在内。


编辑

所以我深入挖掘并创建了一个 ASP.NET WebForms 站点并尝试复制您的问题,但无法...在每个请求中我都设法提取了 __EVENTVALIDATION 字段。

如果你觉得有用的话,这里是我的代码...

void Main()
{
    string eventValidationToken = string.Empty;
    
    var firstResponse = this.Get(@"http://localhost:7428/Account/Login");
    
    firstResponse.FormValues["ctl00$MainContent$Email"] = "email@example.com";
    firstResponse.FormValues["ctl00$MainContent$Password"] = "password";

    string secondRequestPostdata = firstResponse.ToString();
    var secondResponse = this.Post(@"http://localhost:7428/Account/Login", secondRequestPostdata);
    
    Console.WriteLine (firstResponse.FormValues["__EVENTVALIDATION"]);
    Console.WriteLine (secondResponse.FormValues["__EVENTVALIDATION"]);
}


public FormData Get(string uri)
{
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://localhost:7428/Account/Login");
    using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
    using (Stream stream = response.GetResponseStream())
    using (StreamReader reader = new StreamReader(stream))
    {
        return  new FormData(reader.ReadToEnd());
    }
}

public FormData Post(string uri, string postContent)
{
    byte[] formBytes = Encoding.UTF8.GetBytes(postContent);
    
    var request = (HttpWebRequest)WebRequest.Create("http://localhost:7428/Account/Login");
    request.Method = "POST";
    request.ContentType = "application/x-www-form-urlencoded";
    request.ContentLength = formBytes.Length;
    
    using (Stream stream = request.GetRequestStream())
    {
        stream.Write(formBytes, 0, formBytes.Length);
    }
    
    using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
    using (Stream stream = response.GetResponseStream())
    using (StreamReader reader = new StreamReader(stream))
    {
        return new FormData(reader.ReadToEnd());
    }
}

public class FormData
{
    public FormData(string html)
    {
        this.Html = html;
    
        this.FormValues = new Dictionary<string, string>();
        this.FormValues["__EVENTTARGET"]                = this.Extract(@"__EVENTTARGET");
        this.FormValues["__EVENTARGUMENT"]              = this.Extract(@"__EVENTARGUMENT");
        this.FormValues["__VIEWSTATE"]                  = this.Extract(@"__VIEWSTATE");
        this.FormValues["__VIEWSTATEGENERATOR"]         = this.Extract(@"__VIEWSTATEGENERATOR");
        this.FormValues["__EVENTVALIDATION"]            = this.Extract(@"__EVENTVALIDATION");
        this.FormValues["ctl00$MainContent$Email"]      = string.Empty;
        this.FormValues["ctl00$MainContent$Password"]   = string.Empty;
        this.FormValues["ctl00$MainContent$ctl05"]      = "Log in";
    }
    
    public string Html { get; set; }
    
    private string Extract(string id)
    {
        return Regex.Match(this.Html, @"id=""" + id + @""" value=""([^""]*)")
                    .Groups[1]
                    .Value;
    }
    
    public Dictionary<string, string> FormValues { get;set; }
    
    public override string ToString()
    {
        var formData = this.FormValues.Select(form => HttpUtility.UrlEncode(form.Key) + "=" + HttpUtility.UrlEncode(form.Value));
                        
        return string.Join("&", formData);
    }
}

【讨论】:

  • hmm 我在提琴手中没有看到任何类似于防伪令牌的附加参数,而且我已经在帖子中获取并传递了所有 cookie。我只是不明白为什么我的 Eventvalidation 值没有改变。
  • 所以当你启动你的第一个HttpWebRequest... 观察提琴手时,服务器是否没有在响应中设置一个名为__EVENTVALIDATION 的令牌?
  • 在第一步中,我执行 GET 并获取原始 cookie 和令牌,它们也设置了 correclty__EVENTVALIDATION。使用该信息我正在发布并获得正确的结果(如上所述),尽管事实上我的 EVENTVALIDATION 在所有帖子中都具有相同的值。当我点击第 11 页(不在原始网页中)时,问题就开始了。之后我总是被重定向到主页。
  • 是的,这就是问题所在,一旦您发送HttpWebRequest,您就不会阅读正文以获取 new 令牌,您需要提取该令牌并在其后的请求中使用它。
  • 嗯,但我一直在读它:) 它总是一样的,这就是我好奇的原因。另外,我注意到我只传递了一个 cookie ASP.NET_SessionId=bbbbbbb,但是如果我通过浏览器执行 Cookie: __atuvc=; 则很少有其他的。 _utma=; __utmz=.utmcsr=(直接)|utmccn=(直接)|utmcmd=(无);
猜你喜欢
  • 1970-01-01
  • 2016-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-08
相关资源
最近更新 更多