ASFEnhance HTML解析技巧:HtmlParser处理复杂页面

ASFEnhance HTML解析技巧:HtmlParser处理复杂页面

【免费下载链接】ASFEnhance ASF增强插件 / External commands for ASF 【免费下载链接】ASFEnhance 项目地址: https://gitcode.com/GitHub_Trending/as/ASFEnhance

在ASFEnhance插件开发中,HTML解析是处理Steam社区页面、商店数据和账户相关信息的核心能力。本文将系统介绍如何使用项目内置的HtmlParser类高效处理各类复杂HTML页面,通过实例解析常见场景的解决方案。

HtmlParser模块架构

ASFEnhance采用模块化设计,将不同业务场景的HTML解析逻辑分离到独立文件中,主要包括:

这种架构确保了解析逻辑与业务功能的紧密耦合,同时保持代码可维护性。以账户历史记录解析为例,典型调用流程如下:

// 从Web请求获取HTML内容
var response = await WebRequest.GetAccountHistoryPage(...);
// 解析分页游标数据
var cursor = HtmlParser.ParseCursorData(response);
// 解析交易记录
var historyData = HtmlParser.ParseHistory(tableElement, exchangeRates, defaultCurrency);

核心解析技术与实战

1. 结构化数据提取

Steam页面通常包含大量嵌套HTML结构,HtmlParser使用AngleSharp库提供的CSS选择器进行精确定位。以解析账户邮箱为例:

internal static string? ParseAccountEmail(IDocument? document)
{
    if (document == null) return null;
    
    // 使用多层CSS选择器定位目标元素
    var eleEmail = document.QuerySelector(
        "#main_content div.account_setting_sub_block:nth-child(1) > div:nth-child(2) span.account_data_field");
    return eleEmail?.TextContent;
}

关键技巧

  • 使用浏览器开发者工具复制精确CSS选择器
  • 避免过度依赖页面结构,优先使用具有唯一ID的容器元素
  • 对可能为null的元素进行安全检查

2. 复杂表格数据处理

账户交易历史页面采用动态加载的表格结构,ParseHistory方法展示了如何处理这种复杂数据:

internal static HistoryParseResponse ParseHistory(IElement tableElement, 
    Dictionary<string, decimal> currencyRates, string defaultCurrency)
{
    var rows = tableElement.QuerySelectorAll("tr");
    var result = new HistoryParseResponse();
    
    foreach (var row in rows)
    {
        // 提取表格单元格数据
        var strItem = row.QuerySelector("td.wht_items")?.Text().Trim() ?? "";
        var strType = row.QuerySelector("td.wht_type")?.Text().Trim() ?? "";
        var strTotal = row.QuerySelector("td.wht_total")?.Text().Trim() ?? "";
        
        // 根据交易类型分类处理
        if (strType.StartsWith("购买"))
        {
            result.StorePurchase += ParseMoneyString(strTotal);
        }
        // ...其他交易类型处理
    }
    
    return result;
}

表格解析最佳实践

  1. 先定位表格主体(<tbody>)而非整个表格
  2. 使用语义化类名筛选有效行(如wht_row
  3. 对数值型数据进行类型转换和异常处理

3. 货币与数值处理

Steam支持多币种显示,HtmlParser实现了智能货币识别系统,解决符号歧义(如¥同时表示货币A和货币B):

// 货币符号解析逻辑
string ParseSymbol(string symbol1, string symbol2)
{
    if (symbol1.Contains('¥') || symbol2.Contains('¥'))
    {
        // 使用钱包默认货币区分货币A/货币B
        return defaultCurrency; 
    }
    // ...其他货币处理
}

// 数值解析
decimal ParseMoneyString(string strMoney)
{
    var match = RegexUtils.MatchHistoryItem().Match(strMoney);
    if (!match.Success) return 0;
    
    var negative = match.Groups[1].Value == "-";
    var strPrice = match.Groups[3].Value;
    // 根据货币类型选择正确的数字格式
    var numberFormat = DotCurrency.Contains(currency) 
        ? new NumberFormatInfo { CurrencyDecimalSeparator = "." }
        : new NumberFormatInfo { CurrencyDecimalSeparator = "," };
    
    return decimal.TryParse(strPrice, NumberStyles.Currency, numberFormat, out var price) 
        ? (negative ? -price : price) 
        : 0;
}

关键挑战

  • 不同地区数字格式(1,000.00 vs 1.000,00)
  • 货币符号位置差异($100 vs 100€)
  • 退款和负数交易的特殊标记

4. 动态内容提取

对于使用JavaScript动态加载的内容(如无限滚动列表),HtmlParser结合正则表达式提取内嵌JSON数据:

internal static AccountHistoryResponse.CursorData? ParseCursorData(HtmlDocumentResponse? response)
{
    if (response?.Content?.Body == null) return null;
    
    var content = response.Content.Body.InnerHtml;
    // 使用正则匹配内嵌JSON数据
    var match = RegexUtils.MatchHistoryCursor().Match(content);
    if (!match.Success) return null;
    
    try
    {
        // 直接反序列化为对象
        return match.Groups[1].Value.ToJsonObject<AccountHistoryResponse.CursorData>();
    }
    catch
    {
        return null;
    }
}

常见问题与解决方案

1. 页面结构变化应对

Steam页面布局可能不定期更新,导致解析失败。防御性编程策略包括:

// 安全的元素查询链
var container = document.QuerySelector("#content_container");
if (container == null)
{
    ASFLogger.LogGenericWarning("内容容器未找到");
    return null;
}

var dataRows = container.QuerySelectorAll("div.data_row");
if (dataRows.Length == 0)
{
    // 尝试备选选择器
    dataRows = container.QuerySelectorAll("div.alt_data_row");
}

2. 性能优化

解析大型HTML文档时,可通过以下方式提升性能:

  1. 局部解析:只提取文档中需要的部分
  2. 延迟加载:分页数据按需解析
  3. 缓存复用:重复使用AngleSharp解析上下文
// 只解析表格部分而非整个文档
var tableHtml = response.Content.QuerySelector("table#transaction_history")?.OuterHtml;
if (tableHtml != null)
{
    using var context = BrowsingContext.New(Configuration.Default);
    var tableDocument = await context.OpenAsync(req => req.Content(tableHtml));
    // 仅解析表格内容
    return ParseHistory(tableDocument.Body, ...);
}

3. 调试与日志

解析过程中加入详细日志,便于问题诊断:

if (string.IsNullOrEmpty(currency))
{
    ASFLogger.LogGenericWarning($"检测货币符号失败, 使用默认货币单位 {defaultCurrency}");
    return defaultCurrency;
}

高级应用:构建解析器抽象层

对于复杂场景,可以设计解析器接口实现策略模式:

// 解析器接口定义
interface IHtmlParser<T>
{
    T Parse(IDocument document);
}

// 账户解析器实现
class AccountParser : IHtmlParser<AccountData>
{
    public AccountData Parse(IDocument document)
    {
        // 实现具体解析逻辑
    }
}

// 使用依赖注入
var parser = new AccountParser();
var data = parser.Parse(document);

这种模式便于单元测试和解析逻辑替换。

总结与最佳实践

ASFEnhance的HtmlParser模块展示了如何在实际项目中高效处理复杂HTML解析任务。核心要点包括:

  1. 模块化设计:按业务场景分离解析逻辑
  2. 多技术结合:CSS选择器+正则表达式+JSON反序列化
  3. 防御性编程:处理null值、格式异常和结构变化
  4. 性能优化:局部解析和延迟加载
  5. 详细日志:便于问题诊断和后期维护

通过本文介绍的技术和实例,开发者可以快速掌握复杂HTML页面的解析技巧,提升ASFEnhance插件的数据处理能力。更多解析实例可参考项目源代码中的各HtmlParser.cs文件。

【免费下载链接】ASFEnhance ASF增强插件 / External commands for ASF 【免费下载链接】ASFEnhance 项目地址: https://gitcode.com/GitHub_Trending/as/ASFEnhance

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值