C#屏幕抓取ASP.NET Web表单页面 - POST请求未完全正常工作

时间:2015-07-21 15:35:59

标签: c# asp.net

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

1)网站使用HTTPS进行基本身份验证,因此我需要正确登录。

2)我在页面上执行GET请求以检索__VIEWSTATE值(如果我没有设置这个东西,那就什么都不做了!)

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

4)当按下提交按钮时,表单将POST到服务器,响应是相同的页面和表单,但现在在表单底部有一个额外的小HTML表,我需要一些数据得到。

到目前为止,我设法使用WebClient类对登录和表单进行排序。我使用fiddler(和firebug)检查正常使用浏览器填写表单时发送的POST字段值。我可以成功地从POST请求获得响应,并且数据表出现在表单下方,如预期的那样。然而问题是,虽然表格中填充了数据,但却填充了我不期望的数据。出现的数据是我在浏览器中正常完成表单,但是将一个特定参数(下拉列表)设置为与我将POST请求传递给服务器的值不同的值。我已经确认使用了小提琴手和萤火虫,我正在使用完全相同的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:感谢您的快速回复,我只能对这些评论说......

正常的ASP会话cookie但是cookie中没有值(当然除了会话ID),我认为该网站使用基本身份验证而不是表单身份验证我可以忽略cookie - 当我进入网站并获取数据时,这是可以的。我想这值得一试,但我必须改变代码才能使用WebRequest类方法......

对于选择列表javascript,没有javascript在页面加载后改变选择列表的值。选择列表中唯一的javascript是onchange事件,用于回复'这似乎只是改变了表格上的其他选择列表,无论如何在最终的POST中都是空的。注意我在生成POST请求时包括所有POST参数,即使它们已经空了,我还要包括所有的“Web表单”和#39;特殊字段,例如__VIEWSTATE,__ EVENTTARGET等......

我不是网络形式的专家(自己是MVC人),但网络还有什么其他形式的引擎'期待?我已经为内容类型'发送了1个标题。 ' application / x-www-form-urlencoded'但是我尝试过设置其他人,比如复制“用户代理”等。来自原始POST的标题,但这最终导致我从服务器收到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);
    }

1 个答案:

答案 0 :(得分:0)

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

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