ASP.NET WebForm PostBack 式查詢網頁之 PowerShell 爬蟲範例
0 |
寫爬蟲抓取網頁內容已算開發人員的基本功,說穿了不值兩毛錢,不外乎模擬瀏覽器發出 HttpRequst 取回 HTML,再設法解析內容取出想要的資訊。 各語言幾乎都有發送 HttpRequest 的程式庫函式,.NET 有 WebClient/HttpClient、Python 有 reuqests 模組、PowerShell 有 Invoke-WebRequest,工具類則有 PostMan、cUrl、Headless Chrome,選擇很多。 至於解析內容,從用 Headless Chrome 將其轉為完整 DOM (甚至 JavaScript 部分也照跑)進行存取、轉為 XML 文件做結構化查詢,到最簡單粗暴但有效的絕招 - 用 Regular Expression 挖資料,都能達成目標。最近處理排程作業要抓網頁內容,我開始試著用 PowerShell Invoke-WebRequest + .NET Regex 解題,因網頁結構還算簡單,倒也都能手到擒來。直到幾前天遇上一個 WebForm PostBack 式的查詢網頁,就是那種最傳統的 WebForm,把查詢邏輯寫在 Button1_Click(object sender, EventArgs e) 伺服器端 C# 事件的玩法,發現一些眉角。
用個範例展示,假設有一個 DropDownList 下拉選單列舉分類,按下 Button PostBack 回去,伺服器端依選取項目傳回該分類的庫存數字:
程式碼如下:
<%@Page Language="C#"%>
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
ddlCategory.Items.Clear();
ddlCategory.Items.AddRange(
Qtys.Keys.Select(o => new ListItem(o)).ToArray());
}
}
static Dictionary<string, int> Qtys = new Dictionary<string, int>()
{
["Notebook"] = 255,
["Monitor"] = 1024,
["Keybd/Mouse"] = 32767
};
protected void btnSend_Click(object sender, EventArgs e)
{
var catg = ddlCategory.Text;
lblMessage.Text =
"Category = " + catg + ", Qty = " + Qtys[catg];
}
</script>
<html>
<body>
<Form runat="server">
<asp:DropDownList id="ddlCategory" runat="server"></asp:DropDownList>
<asp:Button ID="btnSend" runat="server" Text="Show Qty" OnClick="btnSend_Click" />
<br />
<asp:Label ID="lblMessage" runat="server" Text=""></asp:Label>
</Form>
</body>
</html>
依據直覺,我用 Invoke-WebRequest 發出 POST 請求,將 Content-Type 設為 application/x-www-form-urlencoded,將下拉選單及按鈕結果組成 ddlCategory=Monitor&btnSend=Show+Qty 當傳送內容送出:
不料,ASP.NET WebForm 傳回如 GET 時看到的全新網頁,看到 HTML 出現 ViewState、Event Validation,這才想起這問題之前遇過 - 抓取 ASP.NET WebForm 網頁 PostBack 結果,解法不難,先發一次 GET 取回 __VIEWSTATE、__EVENTVALIDATION 兩個 <input type="hidden" > 加入送出參數即可,當年用 WebClient 與 HttpClient 示範,今天就來補上 PowerShell 版吧:
$html = (Invoke-WebRequest -Method GET -Uri http://localhost/aspnet/mywebform.aspx).Content
$m = [System.Text.RegularExpressions.Regex]::Match($html, "(?ms)__VIEWSTATE`" value=`"(?<v>\S+?)`".+__EVENTVALIDATION`" value=`"(?<e>\S+?)`"")
$form = @{
__VIEWSTATE = $m.Groups["v"].Value
__EVENTVALIDATION = $m.Groups["e"].Value
ddlCategory = "Monitor"
btnSend = "Show Qty"
}
$html = (Invoke-WebRequest -Method POST `
-ContentType application/x-www-form-urlencoded `
-Uri http://localhost/aspnet/mywebform.aspx `
-Body $form).Content
[System.Text.RegularExpressions.Regex]::Match($html, "lblMessage`">(?<v>.+?)</span>").Groups["v"].Value
測試成功!
這次還多學到一個技巧,表單內容可用 $form = @ 宣告直接當成 -Body 的參數,Invoke-WebRequest 將會自動轉成 paramA=valueA¶mB=valueB 還會加上 UrlEncode,之前都傻傻自己搞,學會這招省事不少。
Get postback result of ASP.NET WebForm with PowerShell script.
Comments
Be the first to post a comment