小技巧 - 抓取 ASP.NET WebForm 網頁 PostBack 結果

栏目: ASP.NET · 发布时间: 6年前

内容简介:用 WebClient 爬網頁抓內容已是老生常談,但最近發現抓 ASP.NET WebForm 網頁的特殊眉角,忍不住又想分享。 (是的,「我種了一棵葱,大家快來嚐嚐」的毛病又犯了)主要關鍵在於當 WebForm 邏輯寫在 Server-Side Event,例如 Button_OnClick(),單純用 POST Request 是不會觸發的。例如以下示範,假設有個簡單的 WebForm 網頁,有一個 DropDownList 放選項,一個 Label 顯示結果,Button 在 Server-Side

用 WebClient 爬網頁抓內容已是老生常談,但最近發現抓 ASP.NET WebForm 網頁的特殊眉角,忍不住又想分享。 (是的,「我種了一棵葱,大家快來嚐嚐」的毛病又犯了)

主要關鍵在於當 WebForm 邏輯寫在 Server-Side Event,例如 Button_OnClick(),單純用 POST Request 是不會觸發的。例如以下示範,假設有個簡單的 WebForm 網頁,有一個 DropDownList 放選項,一個 Label 顯示結果,Button 在 Server-Side OnClick 事件中依 DropDownList 選取值在 Label 顯示不同文字:

<%@Page Language="C#"%>
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
	if (!IsPostBack) 
	{
		ddlCatg.Items.Clear();
		ddlCatg.Items.Add(new ListItem("筆電", "Notebook"));
		ddlCatg.Items.Add(new ListItem("手機", "Mobile"));
	}
}
void btnQuery_OnClick(object sender, EventArgs e)
{
	lblDisplay.Text = 
		ddlCatg.SelectedItem.Value == "Notebook" ? "ThinkPad X21" : "Nokia 3310";
}
</script>
<html>
<body>
<form runat="server">
	<asp:DropDownList runat="server" id="ddlCatg" Height="23"></asp:DropDownList>
	<asp:Button runat="server" id="btnQuery" text="查詢" OnClick="btnQuery_OnClick"></asp:Button>
	<div class="result">
	<asp:Label runat="server" id="lblDisplay" Text="請按查詢" ></asp:Label>
	</div>
</form>
</body>
</html>

實際操作如下:

小技巧 - 抓取 ASP.NET WebForm 網頁 PostBack 結果

依照直覺,反正就是個 POST 傳送表單內容,用 WebClient.UploadValues 應該就可以搞定:

static void Main(string[] args)
{
    var wc = new WebClient();
    var form = new NameValueCollection();
    form.Add("ddlCatg", "Mobile");
    var resp = Encoding.UTF8.GetString(
        wc.UploadValues("http://localhost/aspnet/webform.aspx", form));
    var m = Regex.Match(resp,
        @"(?ims)ID=""lblDisplay"">(?<t>.*?)</span>");
    if (m.Success)
        Console.WriteLine("Result=" + m.Groups["t"].Value);
    Console.Read();
}

很不幸地,雖然成功發出 POST 請求取回網頁,但並沒有觸發 btnQuery_OnClick,只會得到 Result=請按查詢

原因是這樣的,ASP.NET WebForm 有自己的一套機制,網頁包含了 __VIEWSTATE、__EVENTVALIDATION 等隱藏欄位:(延伸閱讀:這也成了 WebForm 的原罪,例如UpdatePanel招誰惹誰?)

小技巧 - 抓取 ASP.NET WebForm 網頁 PostBack 結果

而在 POST 送出表單時,這些欄位需一併傳送才會進入 PostBack 流程觸發 Server-Side Event:

小技巧 - 抓取 ASP.NET WebForm 網頁 PostBack 結果

因此網路爬蟲必須模擬相同的行為才能得到正確結果,我的解法是先發一個 GET Request 拿到 HTML 從中取出 __VIEWSTATE 及 __EVENTVALIDATION 欄位值,當成 POST Form 的一部分 。另外有一點要注意,上圖中標註為紅色的 btnQuery: "查詢",對後端執行並無作用,但對 ASP.NET WebForm 是必須的,需包含在傳送內容中。

修改後的程式如下:

static void Main(string[] args)
{
    var wc = new WebClient();
    var url = "http://localhost/aspnet/webform.aspx";
    var html = wc.DownloadString(url);
    var mViewState = Regex.Match(html, @"(?ims)id=""__VIEWSTATE"" value=""(?<v>.+?)""");
    var mEventVald = Regex.Match(html, @"(?ims)id=""__EVENTVALIDation"" value=""(?<v>.+?)""");
    var form = new NameValueCollection();
    form.Add("__VIEWSTATE", mViewState.Groups["v"].Value);
    form.Add("__EVENTVALIDATION", mEventVald.Groups["v"].Value);
    form.Add("btnQuery", "查詢"); 
    form.Add("ddlCatg", "Mobile");
    var resp = Encoding.UTF8.GetString(
        wc.UploadValues(url, form));
    var m = Regex.Match(resp,
        @"(?ims)ID=""lblDisplay"">(?<t>.*?)</span>");
    if (m.Success)
        Console.WriteLine("Result=" + m.Groups["t"].Value);
    Console.Read();
}

實測結果為 Result=Nokia 3310 ,成功!

同場加映,在 .NET Core 裡 WebClient 已被 HttpClient 取代,在此一併示範用 HttpClient 的寫法。主要差異在於 HttpClient 改用 GetStringAync()/PostAsync()+new FormUrlEncodedContent(Dictionary<string, string>) 取代 WebCient DownloadString()、UploadValues(),其餘原理相同。

static void Main(string[] args)
{
    var hc = new HttpClient();
    var url = "http://localhost/aspnet/webform.aspx";
    var html = hc.GetStringAsync(url).Result;
    var mViewState = Regex.Match(html, @"(?ims)id=""__VIEWSTATE"" value=""(?<v>.+?)""");
    var mEventVald = Regex.Match(html, @"(?ims)id=""__EVENTVALIDation"" value=""(?<v>.+?)""");
    var form = new Dictionary<string, string>();
    form.Add("__VIEWSTATE", mViewState.Groups["v"].Value);
    form.Add("__EVENTVALIDATION", mEventVald.Groups["v"].Value);
    form.Add("btnQuery", "查詢"); 
    form.Add("ddlCatg", "Mobile");
    var resp = hc.PostAsync(url, new FormUrlEncodedContent(form))
        .Result.Content.ReadAsStringAsync().Result;
    var m = Regex.Match(resp,
        @"(?ims)ID=""lblDisplay"">(?<t>.*?)</span>");
    if (m.Success)
        Console.WriteLine("Result=" + m.Groups["t"].Value);
    Console.Read();
}

When crawling ASP.NET WebForm page, __VIEWSTATE and __EVENTVALIDATION hidden fields are required in the POST request to trigger server-side event.


以上所述就是小编给大家介绍的《小技巧 - 抓取 ASP.NET WebForm 網頁 PostBack 結果》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Ruby on Rails社区网站开发

Ruby on Rails社区网站开发

布拉德伯纳 / 柳靖 / 2008-10 / 55.00元

《Ruby on Rails社区网站开发》全面探讨创建完整社区网站的开发过程。首先介绍开发一个内容简单的管理系统,之后逐渐添加新特性,以创建更完整的、使用Ruby on Rails 的Web 2.0 社区网站。还给出了开发和测试中的一些建议和提示,同时指导如何使网站更生动以及维护得更好。《Ruby on Rails社区网站开发》也探讨了如何与Flickr 、Google Maps 等其他平台集成,......一起来看看 《Ruby on Rails社区网站开发》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具