首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Jsoup 抓取 ASP.NET 動態頁面實戰:__VIEWSTATE、__EVENTVALIDATION 動態更新與分頁處理

     編輯:程式設計綜合問答

針對 ASP.NET WebForms 頁面(ReportByDateOfPivot.aspx),本文完整記錄從靜態 HTML 解析失敗、分析 __VIEWSTATE/__EVENTVALIDATION 動態生成機制、模擬 POST 請求攜帶完整表單欄位、處理分頁參數 __EVENTARGUMENT 的全過程,並提供可直接運行的 Java 代碼範例。

問題背景

  • 目標頁面使用 ASP.NET UpdatePanel 進行局部更新。
  • 直接 Jsoup.connect(url).get() 只能拿到初始頁面,分頁按鈕觸發 __doPostBack 需要攜帶完整 ViewState 與 EventValidation。
  • 初版代碼使用硬編碼的 body 字串,導致分頁失敗、資料為空。

核心解決策略

  1. 首次 GET 取得初始 ViewState/EventValidation:解析隱藏欄位 #__VIEWSTATE, #__EVENTVALIDATION。
  2. 構造 POST 表單:包含 __EVENTTARGET(分頁控制項 ID)、__EVENTARGUMENT(頁碼)、__VIEWSTATE、__EVENTVALIDATION 及所有 ctl00$... 欄位。
  3. 每次請求前更新 ViewState/EventValidation:從上一次回應的 HTML 中重新解析,確保 Token 同步。
  4. 設定完整 Header:User-Agent, Content-Type: application/x-www-form-urlencoded, Referer。

完整可運行代碼(Java 8+)

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;

public class LnPriceCrawler {

    private static final String BASE_URL = 
        "https://www.lnprice.gov.cn/wjjc/jgjc/ReportByDateOfPivot.aspx" +
        "?PriceBureauMainType_Id=101&YM=201502&DP=28";

    public static void main(String[] args) throws Exception {
        for (int page = 1; page <= 3; page++) {
            List prices = fetchPage(page);
            System.out.println("Page " + page + ": " + prices);
        }
    }

    static List fetchPage(int pageNo) throws Exception {
        // 1. 首次 GET 取得初始 Token
        Document initDoc = Jsoup.connect(BASE_URL)
                .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
                .get();

        String viewState = initDoc.select("#__VIEWSTATE").val();
        String eventValidation = initDoc.select("#__EVENTVALIDATION").val();

        // 2. 構造 POST Body
        String body = buildPostBody(pageNo, viewState, eventValidation);

        // 3. 發送 POST
        String html = postHtml(body);

        // 4. 解析表格
        Document doc = Jsoup.parse(html);
        Elements rows = doc.select("#ctl00_cphMainFrame_Table1 tr");
        List result = new ArrayList<>();
        for (int i = 1; i < rows.size(); i++) { // 跳過表頭
            Element row = rows.get(i);
            String price = row.select("td").get(5).text(); // 第 6 欄
            result.add(price);
        }
        return result;
    }

    static String buildPostBody(int pageNo, String viewState, String eventValidation) {
        // 必須 URLEncode 每個欄位值
        return "__EVENTTARGET=ctl00$cphMainFrame$aspnetpager1" +
               "&__EVENTARGUMENT=" + pageNo +
               "&__VIEWSTATE=" + encode(viewState) +
               "&__EVENTVALIDATION=" + encode(eventValidation) +
               "&ctl00$cphMainFrame$ddlYear=2015" +
               "&ctl00$cphMainFrame$ddlMonth=02" +
               "&ctl00$cphMainFrame$ddlTimePoint=28" +
               "&ctl00$cphMainFrame$ScriptManager1=ctl00$cphMainFrame$UpdatePanel1|ctl00$cphMainFrame$aspnetpager1" +
               "&__ASYNCPOST=true";
    }

    static String postHtml(String body) throws IOException {
        URL url = new URL(BASE_URL);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("POST");
        conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
        conn.setRequestProperty("Referer", BASE_URL);
        conn.setDoOutput(true);
        try (OutputStream os = conn.getOutputStream()) {
            os.write(body.getBytes("UTF-8"));
        }
        try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"))) {
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = br.readLine()) != null) sb.append(line);
            return sb.toString();
        }
    }

    static String encode(String s) {
        try { return java.net.URLEncoder.encode(s, "UTF-8"); }
        catch (UnsupportedEncodingException e) { throw new RuntimeException(e); }
    }
}

關鍵調試技巧

  • 使用 Fiddler/Charles 抓取瀏覽器真實請求,對比 body 差異。
  • 將回應 HTML 存檔,用瀏覽器開啟確認表格是否渲染正確。
  • 若遇到 403/500,檢查 __VIEWSTATE 是否過期、Cookie 是否需維持 Session。

常見坑點提醒

坑點症狀解法
硬編碼 ViewState第一頁正常,第二頁失效每次請求前動態解析更新
缺少 __ASYNCPOST=true伺服器拒絕或返回完整頁面而非片段加上該參數標識局部更新
Content-Type 錯誤伺服器無法解析表單明確設定 application/x-www-form-urlencoded
分頁控制項 ID 錯誤__EVENTTARGET 不匹配從頁面原始碼搜尋 aspnetpager 確認 ClientID

總結

ASP.NET WebForms 爬蟲的核心在於「維持 ViewState/EventValidation 的同步更新」與「精確模擬 __doPostBack 表單結構」。採用 Jsoup 解析配合 HttpURLConnection 發送 POST,並在每次迴圈前刷新 Token,即可穩定抓取分頁資料。建議將上述邏輯封裝為可複用的 WebFormCrawler 基類,應對同類站點更高效。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved