【问题标题】:C# screen scraping an ASP.NET web forms page - POST request not completely workingC# 屏幕抓取 ASP.NET Web 表单页面 - POST 请求未完全正常工作
【发布时间】:2015-10-11 04:09:54
【问题描述】:

请耐心等待这个稍微冗长的描述,但我遇到了一个奇怪的问题,即 C# 屏幕抓取 ASP.NET Web 表单页面。我正在尝试执行的步骤如下:-

1) 该站点使用基于 HTTPS 的基本身份验证进行保护,因此我需要正确登录。

2) 我正在页面上执行 GET 请求以检索 __VIEWSTATE 值(如果我不设置这个东西,该死的东西什么都不做!)

3) 登录后,有几个表单字段需要填写,然后是一个提交按钮,该按钮将表单发送到服务器

4) 当按下提交按钮时,表单被 POST'd 到服务器,响应是相同的页面和表单,但现在表单底部有一个额外的小 HTML 表,其中包含一些我需要获取的数据.

到目前为止,我已经设法使用 WebClient 类对登录和表单帖子进行排序。我使用提琴手(和萤火虫)来检查正常使用浏览器完成表单时发送的 POST 字段值。我可以成功地从 POST 请求中获得响应,相关数据表按预期显示在表单下方。然而问题是,虽然表格中填充了数据,但它填充了我不期望的数据。出现的数据是,如果我在浏览器中正常填写表单,但将一个特定参数(下拉列表)设置为与将 POST 请求传递给服务器不同的值。我已经使用 fiddler 和 firebug 确认我传递的 POST 参数与使用 Web 浏览器人工完成的表单正常发送的参数完全相同。我现在完全不知道为什么服务器没有“考虑”这个参数?

一个区别是这个特定的控件是一个选择列表,它在更改时执行页面重新加载或“回发”。但是,除了在表单后面更改一些其他选择列表内容之外,这似乎没有任何作用。

我想我在问还有什么我遗漏的东西会导致这种情况吗?我完全把头发扯掉了。任何人都可以帮忙吗?我已经发布了下面的代码(为了隐私,地址和参数被屏蔽了)。

    // a place to store the html
    string responseBody = "";

    // create out web client to handle the request
    using (WebClient webClient = new WebClient())
    {
        // space to store responses from the remote site
        byte[] responseBytes;

        // site uses basic authentication over HTTPS so we'll need to login
        CredentialCache credentials = new CredentialCache();
        credentials.Add(new Uri(Url), "Basic", new NetworkCredential(Username, Password));

        // set the credentials in the web client
        webClient.Credentials = credentials;

        // a place for __VIEWSTATE
        string viewState = "";

        // try and get __VIEWSTATE from the web site
        try
        {
            responseBytes = webClient.DownloadData(Url);
            viewState = GetHtmlInputValue(Encoding.UTF8.GetString(responseBytes), "__VIEWSTATE");
        }
        catch (Exception e)
        {
            bool cancel = false;
            ComponentMetaData.FireError(10, "Read web page data", "Error whilst trying to get __VIEWSTATE from web page: " + e.Message, "", 0, out cancel);
        }

        // add our POST parameters (don't forget the __VIEWSTATE or it won't work as its an ASP.NET web page)
        NameValueCollection requestParameters = new NameValueCollection();

        // add ASP.NET fields
        requestParameters.Add("__EVENTTARGET", __EVENTTARGET);
        requestParameters.Add("__EVENTARGUMENT", __EVENTARGUMENT);
        requestParameters.Add("__LASTFOCUS", __LASTFOCUS);

        // add __VIEWSTATE
        requestParameters.Add("__VIEWSTATE", viewState);

        // all other form parameters
        requestParameters.Add("btnSubmit", btnSubmit);      
        /* I've hidden the rest of the parameters hidden for privacy just in case */

        // see if we can connect and get data
        try
        {
            // set content type
            webClient.Headers.Clear();
            webClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");                             

            // 'POST' the form data using web client and hope we get a response
            responseBytes = webClient.UploadValues(Url, "POST", requestParameters);

            // transform the response to a string
            responseBody = Encoding.UTF8.GetString(responseBytes);
        }
        catch (Exception e)
        {
            bool cancel = false;
            ComponentMetaData.FireError(10, "Read web page data", "Error whilst trying to connect to web page: " + e.Message, "", 0, out cancel);
        }
    }

请忽略“ComponentMetaData”引用,因为这是 SSIS 脚本源的一部分。

任何想法或帮助将不胜感激 - 干杯!

RE:感谢您的快速回复,我只能对这些 cmets 说...

有正常的 ASP 会话 cookie,但 cookie 中没有值(当然除了会话 ID),我认为该站点使用的是基本身份验证而不是表单身份验证,我可以忽略 cookie - 因为我是进入网站并返回数据,这没关系。我想这值得一试,但我只需要更改代码以使用 WebRequest 类方法...

至于选择列表javascript,没有javascript在页面加载后更改选择列表的值。选择列表中唯一的 javascript 是执行“回发”的 onchange 事件,它似乎只会更改表单上的其他一些选择列表,这些列表在最终 POST 中仍然是空的。请注意,我在生成 POST 请求时包含了所有 POST 参数,即使它们是空的,我还包括所有“网络表单”特殊字段,例如 __VIEWSTATE、__EVENTTARGET 等...

我不是 Web 表单方面的专家(我自己是 MVC 人),但是 Web 表单“引擎”还有其他什么期望吗?我已经为“application/x-www-form-urlencoded”的“Content-Type”发送了 1 个标头,但我尝试设置其他标头,例如从原始 POST 复制“User-Agent”标头,但这最终我从服务器收到 500 错误,不知道为什么会发生这种情况??

这是“GetHtmlInputValue”的代码,它有点简单/基本,可以做得更好,但是:-

    private string GetHtmlInputValue(string html, string inputID)
    {
        string valueDelimiter = "value=\"";

        int namePosition = html.IndexOf(inputID);
        int valuePosition = html.IndexOf(valueDelimiter, namePosition);

        int startPosition = valuePosition + valueDelimiter.Length;
        int endPosition = html.IndexOf("\"", startPosition);

        return html.Substring(startPosition, endPosition - startPosition);
    }

【问题讨论】:

  • 您的浏览器中是否传递了任何cookie?您可能需要在请求中包含这些内容。
  • 您是否在页面加载后检查了选择框的值是否使用 javascript 设置?这可以解释为什么它与您的预期不同。
  • 您是否尝试使用webClient.AppendUploadString(PostFieldName, PostFieldValue) 来传递输入控件的值?
  • 您是否捕获了选择列表回发后的__VIEWSTATE。也许正在改变存储在 ViewState 中的服务器端?
  • 根据下面的答案,这是解决方案,所以我也投票支持这条评论,谢谢老兄

标签: c# asp.net


【解决方案1】:

如果我理解正确,那么在下拉列表中选择一个项目将导致执行POST,并且服务器会更改表单另一部分中的可用选项。然后,服务器会将下拉列表的当前值包含在 __VIEWSTATE 字段值中。

执行抓取时,应确保__VIEWSTATE 包含所需的下拉列表值。要进一步调查,请尝试从服务器发送 decode the viewstate 并查看发回的值。

【讨论】:

  • 就是这样,所有发布“网络表单”教程的表单都只提到了使用“GET”请求获取初始 __VIEWSTATE。我不知道表单的值也在 __VIEWSTATE 中编码,老实说,我根本不知道 __VIEWSTATE 到底在做什么。无论如何,我已经更新了我的代码以在发布表单值后获取更改后的视图状态,然后我再次发布更新后的视图状态及其工作。非常感谢,省了很多麻烦。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-07
  • 1970-01-01
  • 2015-10-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多