【问题标题】:submit ajax aspforms req to modify viewstate提交ajax aspforms req修改viewstate
【发布时间】:2014-10-20 00:25:40
【问题描述】:

我正在开发一个非常具体的网络抓取应用程序,它需要登录多个网站并从中检索一些数据。

我正在使用通过覆盖以下方法来识别 cookie 的 WebClient:

protected override WebRequest GetWebRequest(Uri address)
{
    WebRequest request = base.GetWebRequest(address);
    var castRequest = request as HttpWebRequest;
    if (castRequest != null)
    {
        castRequest.CookieContainer = this.CookieContainer;
    }
    return request;
}

我可以使用常规的 POST/GET 请求(通过网络客户端上的适当下载/上传方法)很好地登录网站

目标网站使用 ajax ASP.Net 顶级表单,并且在您单击页面上的按钮后会启用一个状态变量。也就是说,当您单击按钮时,表单被提交,状态被改变,然后当它加载响应时,它就有了我需要的信息。此时的状态修改也是持久的。如果我重新加载页面,甚至关闭选项卡并重新打开它,我需要的数据仍然存在,因为它与 ASP 会话相关联。一旦 ASP 会话到期,您必须登录并再次单击按钮,服务器才会发送我需要的数据。

单击按钮时,我通过 Chrome 开发人员工具查看了提交的表单,我重新创建了表单提交,与我在 chrome 网络监视窗口中看到的完全一样,但它仍然没有正确修改视图状态。

所以我的问题是,我如何模拟单击此按钮,以便服务器将修改视图状态并返回我需要的值。

我不能为此使用网络浏览器控件,但如果它使事情变得更容易,我可以使用 html 敏捷包(尽管我真的不想使用外部库)

按钮定义如下:

<form name="aspnetForm" method="post" action="enterurlhere..." id="aspnetForm">
    <input type="image" name="ctl00$....." id="ctl00...." title="...." src="...." style="height:50px;border-width:0px;">

【问题讨论】:

  • 不能根据aspnetForm中的数据创建一个webrequest吗?

标签: c# asp.net .net ajax http


【解决方案1】:

如果您的目标是 ASP.NET WebForms 站点:

1) 您必须先登录才能导航到所需页面

2) 在所需页面上有一个 UpdatePanel,比如说一个文本框,您需要在其中输入一些内容然后提交该信息,如果该信息正确,您将获得“您期望的”

我之前做过各种爬虫,因此以一个为基础,但精简了很多,没有错误记录,验证您已登录,验证您在请求页面时仍处于登录状态, HtmlAgilityPack、结构、代码清洁度、用户代理字符串随机化等,为您保持简单,但您当然可以增强它:) 无论如何,我在 Visual Studio 2013 中创建了一个 Web 项目(Web 窗体)。你可能知道它有一些登陆页面,包括用户注册等。然后你有“管理帐户”页面,显然需要对用户进行身份验证。在那个页面上,我添加了另一个 div,然后在其中放置了 UpdatePanel(这使得回发 ajaxified)。在 UpdatePanel 中,我放置了文本框、一个按钮和一个文字服务器控件。在后面的代码中,我为该按钮添加了一个单击事件处理程序:如果用户输入等于,让我们说“秘密”,然后将一些文本放入文字中以指示操作成功。因此,应用程序必须先登录,然后通过将密码短语提交到“管理帐户”页面来获取该密码文本。

实际提取器:

using Pokemon.BL.Utils;
using System;
using System.Text;
using System.Web;

namespace Pokemon.BL
{
    sealed class UrlFetcher : IDisposable
    {
        private static readonly UrlFetcher _instance;
        private CGWebClient _cgWebClient;

        private string loginPostString = "__EVENTTARGET={0}&__EVENTARGUMENT={1}&__VIEWSTATE={2}&__VIEWSTATEGENERATOR={3}&__EVENTVALIDATION={4}&ctl00$MainContent$Email={5}&ctl00$MainContent$Password={6}&ctl00$MainContent$ctl05={7}";
        private string secretPhrasePostString = "__EVENTTARGET={0}&__EVENTARGUMENT={1}&__VIEWSTATE={2}&__VIEWSTATEGENERATOR={3}&__EVENTVALIDATION={4}&__ASYNCPOST=true&ctl00$MainContent$btnGetSecretPhrase=Button&ctl00$ctl08=ctl00$MainContent$UpdatePanel1|ctl00$MainContent$btnGetSecretPhrase&ctl00$MainContent$txtSecret={5}";

        private UrlFetcher()
        {
            _cgWebClient = new CGWebClient();
        }

        static UrlFetcher()
        {
            _instance = new UrlFetcher();
        }

        #region Methods

        public void LoginToSite(string email, string password)
        {
            var loginUrl = "http://localhost:53998/Account/Login";

            byte[] response = _cgWebClient.DownloadData(loginUrl);
            var content = Encoding.UTF8.GetString(response);

            string eventTarget = ExtractToken("__EVENTTARGET", content);
            string eventArg = ExtractToken("__EVENTARGUMENT", content);
            string viewState = ExtractToken("__VIEWSTATE", content);
            string viewStateGen = ExtractToken("__VIEWSTATEGENERATOR", content);
            string eventValidation = ExtractToken("__EVENTVALIDATION", content);

            string postData = string.Format(
                loginPostString,
                eventTarget,
                eventArg,
                viewState, 
                viewStateGen, 
                eventValidation,
                email, 
                password,
                "Log in"
                );

            _cgWebClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
            response = _cgWebClient.UploadData(loginUrl, "POST", Encoding.UTF8.GetBytes(postData));
            _cgWebClient.Headers.Remove("Content-Type");
        }

        public void GetSecretPhrase()
        {
            var loginUrl = "http://localhost:53998/Account/Manage";

            byte[] response = _cgWebClient.DownloadData(loginUrl);
            var content = Encoding.UTF8.GetString(response);

            string eventTarget = ExtractToken("__EVENTTARGET", content);
            string eventArg = ExtractToken("__EVENTARGUMENT", content);
            string viewState = ExtractToken("__VIEWSTATE", content);
            string viewStateGen = ExtractToken("__VIEWSTATEGENERATOR", content);
            string eventValidation = ExtractToken("__EVENTVALIDATION", content);

            string postData = string.Format(
                secretPhrasePostString,
                eventTarget,
                eventArg,
                viewState,
                viewStateGen,
                eventValidation,
                "secret"
                );

            _cgWebClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
            _cgWebClient.Headers.Add("X-Requested-With", "XMLHttpRequest");
            response = _cgWebClient.UploadData(loginUrl, "POST", Encoding.UTF8.GetBytes(postData));
            _cgWebClient.Headers.Remove("Content-Type");
            _cgWebClient.Headers.Remove("X-Requested-With");

            Console.WriteLine(Encoding.UTF8.GetString(response));
        }

        #region IDisposable Members
        public void Dispose()
        {
            if (_cgWebClient != null)
            {
                _cgWebClient.Dispose();
            }
        }
        #endregion

        private string ExtractToken(string whatToExtract, string content)
        {
            string viewStateNameDelimiter = whatToExtract;
            string valueDelimiter = "value=\"";

            int viewStateNamePosition = content.IndexOf(viewStateNameDelimiter);
            int viewStateValuePosition = content.IndexOf(valueDelimiter, viewStateNamePosition);

            int viewStateStartPosition = viewStateValuePosition + valueDelimiter.Length;
            int viewStateEndPosition = content.IndexOf("\"", viewStateStartPosition);

            return HttpUtility.UrlEncode(
                     content.Substring(
                        viewStateStartPosition,
                        viewStateEndPosition - viewStateStartPosition
                     )
                  );
        }
        #endregion

        #region Properties
        public static UrlFetcher Instance { get { return _instance; } }
        #endregion
    }
}

WebClient 包装器:

using System;
using System.Collections.Generic;
using System.Net;

namespace Pokemon.BL.Utils
{
    // http://codehelp.smartdev.eu/2009/05/08/improve-webclient-by-adding-useragent-and-cookies-to-your-requests/
    public class CGWebClient : WebClient
    {
        private System.Net.CookieContainer cookieContainer;
        private string userAgent;
        private int timeout;

        public System.Net.CookieContainer CookieContainer
        {
            get { return cookieContainer; }
            set { cookieContainer = value; }
        }

        public string UserAgent
        {
            get { return userAgent; }
            set { userAgent = value; }
        }

        public int Timeout
        {
            get { return timeout; }
            set { timeout = value; }
        }

        public CGWebClient()
        {
            timeout = -1;
            userAgent = "Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0";
            cookieContainer = new CookieContainer();
        }

        protected override WebRequest GetWebRequest(Uri address)
        {
            WebRequest request = base.GetWebRequest(address);

            if (request.GetType() == typeof(HttpWebRequest))
            {
                ((HttpWebRequest)request).CookieContainer = cookieContainer;
                ((HttpWebRequest)request).UserAgent = userAgent;
                ((HttpWebRequest)request).Timeout = timeout;
            }

            return request;
        }
    }
}

最后运行它:

UrlFetcher.Instance.LoginToSite("username", "password");
UrlFetcher.Instance.GetSecretPhrase();

UrlFetcher.Instance.Dispose();

这会将密码短语输出到控制台应用程序中。当然,您需要对其进行调整以使其正常工作,例如取决于您的目标站点正在运行的 ASP.NET 版本等等 :) 希望这会有所帮助:)

【讨论】:

  • 我实际上并没有使用太多/任何这段代码,但它引导我朝着正确的方向前进。我有自己的 WebClient 包装器,我没有设置用户代理 - 我猜是因此,网站的行为有所不同,这让我很困惑。同样,我从您的帖子中意识到您不必提交整个 asp 表单,只需提交 __* 值和更改的输入。感谢您的帮助!
【解决方案2】:

我认为这不会在服务器端起作用,因为客户端需要会话信息。为此,您可以实现一个 Iframe 控件,您可以在其中加载表单并调用服务器端或客户端调用以单击 Iframe 中的按钮并加载会话信息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-06-24
    • 2013-09-19
    • 2021-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-05
    • 2017-12-20
    相关资源
    最近更新 更多