如何使用HtmlAgilityPack获取含注释内的所有HTML Table?
问题:如何获取被HTML注释包裹的Table元素
我用C#代码尝试提取页面中所有HTML Table,但其中一个Table被<!--<table>-->形式的注释包裹,当前逻辑只能获取到1个Table,请问有没有办法获取包括注释内的所有Table?
代码示例:
static void Main(string[] args) { string SEPARATOR = ","; //WebClient webClient = new WebClient(); //string page = webClient.DownloadString("https://www.pro-football-reference.com/boxscores/202209110det.htm"); HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument(); string HTML = "<html><head>\r\n</head>\r\n<body contenteditable=\"false\">\r\n\r\n<h2>HTML Table</h2>\r\n\r\n<!--<table>\r\n <tbody><tr>\r\n <th>Company</th>\r\n <th>Contact</th>\r\n <th>Country</th>\r\n </tr>\r\n <tr>\r\n <td>Alfreds Futterkiste</td>\r\n <td>Maria Anders</td>\r\n <td>Germany</td>\r\n </tr>\r\n</tbody></table>-->\r\n\r\n<table>\r\n <tbody><tr>\r\n <th>Company</th>\r\n <th>Contact</th>\r\n <th>Country</th>\r\n </tr>\r\n <tr>\r\n <td>Alfreds Futterkiste</td>\r\n <td>Maria Anders</td>\r\n <td>Germany</td>\r\n </tr>\r\n</tbody></table>\r\n</body></html>"; doc.LoadHtml(HTML); var tables = doc.DocumentNode.Descendants("table"); int tablesCount = tables.Count(); int x = 0; foreach ( var table in tables) { var rows = table.Descendants("tr") .Select(tr => tr.Descendants("td").Select(td => td.InnerText).ToList()) .ToList(); foreach (var row in rows) Console.WriteLine(String.Join(",", row)); string tableName = string.Format("{0}_{1}.csv",table.Name, x); using (StreamWriter writer = new StreamWriter(tableName)) { rows.ForEach(line => { var lineArray = line.Select(c => c.Contains(SEPARATOR)? c.Replace(SEPARATOR.ToString(), "\\\\" + SEPARATOR) : c).ToArray(); writer.WriteLine(string.Join(SEPARATOR, lineArray)); }); } x ++; } }
解决方案
HtmlAgilityPack默认会忽略注释节点内的HTML结构,所以需要手动处理注释内容,以下是两种可行的方法:
方法一:预处理HTML,移除注释标记还原Table
直接在加载HTML前,把所有注释标记替换掉,让被注释的Table变成正常HTML元素:
string HTML = "你的HTML内容"; // 移除HTML注释标记,还原被注释的Table HTML = HTML.Replace("<!--", "").Replace("-->", ""); HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument(); doc.LoadHtml(HTML); // 后续逻辑不变,此时会获取到所有Table var tables = doc.DocumentNode.Descendants("table"); // ... 你的处理代码
注意:这种方法会移除所有注释,适合不需要保留原注释、只需要提取Table的场景。
方法二:遍历注释节点,单独解析其中的Table
如果需要保留原注释,只提取注释内的Table,可以遍历所有注释节点,单独解析其中的HTML内容:
HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument(); doc.LoadHtml(HTML); // 收集所有正常显示的Table List<HtmlNode> allTables = doc.DocumentNode.Descendants("table").ToList(); // 遍历所有注释节点,提取其中的Table foreach (var commentNode in doc.DocumentNode.Descendants().Where(n => n.NodeType == HtmlNodeType.Comment)) { string commentContent = commentNode.InnerText; // 检查注释内是否包含Table if (commentContent.Contains("<table")) { HtmlAgilityPack.HtmlDocument commentDoc = new HtmlAgilityPack.HtmlDocument(); commentDoc.LoadHtml(commentContent); // 将注释内的Table添加到总列表 allTables.AddRange(commentDoc.DocumentNode.Descendants("table")); } } // 现在allTables包含所有正常和注释内的Table int tablesCount = allTables.Count(); // 此时结果为2 int x = 0; foreach (var table in allTables) { // 后续处理逻辑与原代码一致 var rows = table.Descendants("tr") .Select(tr => tr.Descendants("td").Select(td => td.InnerText).ToList()) .ToList(); foreach (var row in rows) Console.WriteLine(String.Join(",", row)); string tableName = string.Format("{0}_{1}.csv", table.Name, x); using (StreamWriter writer = new StreamWriter(tableName)) { rows.ForEach(line => { var lineArray = line.Select(c => c.Contains(SEPARATOR) ? c.Replace(SEPARATOR.ToString(), "\\\\" + SEPARATOR) : c).ToArray(); writer.WriteLine(string.Join(SEPARATOR, lineArray)); }); } x++; }
这种方法更灵活,既能保留原HTML的注释,又能精准提取注释内的Table元素。
内容的提问来源于stack exchange,提问作者Jefferson
相关产品推荐
相关产品推荐

