【问题标题】:Parsing tables, cells with Html agility in C#在 C# 中使用 Html 敏捷性解析表格、单元格
【发布时间】:2011-06-02 03:52:49
【问题描述】:

我需要解析 Html 代码。更具体地说,解析所有表中每一行的每个单元格。每行代表一个对象,每个单元格代表不同的属性。我想解析这些以便能够编写一个包含每个数据的 XML 文件(没有无用的 HTML 代码)。我已经成功地解析了 HTML 文件中的每一列,但现在我不知道将其写入 XML 文件的选项是什么。我很困惑。

HTML:

<tr><tr> 
<td class="statBox" style="border-width:0px 1px 1px 0px; background-color: #FFFFFF"> 
    1
</td> 
    <td class="statBox" style="border-width:0px 1px 1px 0px; background-color: #FFFFFF" align="left"> 
        <a href="/ice/player.htm?id=8471675">Sidney Crosby</a> 
    </td> 
    <td class="statBox" style="border-width:0px 1px 1px 0px; background-color: #FFFFFF" align="center"> 
        PIT
    </td> 
    <td class="statBox" style="border-width:0px 1px 1px 0px; background-color: #FFFFFF" align="center"> 
        C
    </td> 
    <td class="statBox" style="border-width:0px 1px 1px 0px; background-color: #FFFFFF" align="right"> 
        39
    </td> 
    <td class="statBox" style="border-width:0px 1px 1px 0px; background-color: #FFFFFF" align="right"> 
        32
    </td> 
    <td class="statBox" style="border-width:0px 1px 1px 0px; background-color: #FFFFFF" align="right"> 
        33
    </td> 
    <td class="statBox sorted" style="border-width:0px 1px 1px 0px; background-color: #E0E0E0" align="right"> 
        <font color="#000000"> 
            65
        </font> 
    </td> 
    <td class="statBox" style="border-width:0px 1px 1px 0px; background-color: #FFFFFF" align="right"> 
        20
    </td> 
    <td class="statBox" style="border-width:0px 1px 1px 0px; background-color: #FFFFFF" align="right"> 
        29
    </td> 
    <td class="statBox" style="border-width:0px 1px 1px 0px; background-color: #FFFFFF" align="right"> 
        10
    </td> 
    <td class="statBox" style="border-width:0px 1px 1px 0px; background-color: #FFFFFF" align="right"> 
        1
    </td> 
    <td class="statBox" style="border-width:0px 1px 1px 0px; background-color: #FFFFFF" align="right"> 
        3
    </td> 
    <td class="statBox" style="border-width:0px 0px 1px 0px; background-color: #FFFFFF" align="right"> 
    </td> 
    <td class="statBox" style="border-width:0px 1px 1px 0px; background-color: #FFFFFF" align="right"> 
        0
    </td> 
    <td class="statBox" style="border-width:0px 1px 1px 0px; background-color: #FFFFFF" align="right"> 
        154
    </td> 
    <td class="statBox" style="border-width:0px 1px 1px 0px; background-color: #FFFFFF" align="right"> 
        20.8
    </td> 
    <td class="statBox" style="border-width:0px 1px 1px 0px; background-color: #FFFFFF" align="right"> 
        21:54
    </td> 
    <td class="statBox" style="border-width:0px 1px 1px 0px; background-color: #FFFFFF" align="right"> 
        22.6
    </td> 
    <td class="statBox" style="border-width:0px 0px 1px 0px; background-color: #FFFFFF" align="right"> 
        55.7
    </td> 
</tr></tr>

C#:

using HtmlAgilityPack;

namespace Stats
{
    class StatsParser
    {
        private string htmlCode;
        private static string fileName = "[" + DateTime.Now.ToShortDateString() + " NHL Stats].xml";

        public StatsParser(string htmlCode)
        {
            this.htmlCode = htmlCode;
            this.ParseHtml();
        }

        public void ParseHtml()
    {
        HtmlDocument doc = new HtmlDocument();
        doc.LoadHtml(htmlCode);

        try
        {
            // Get all tables in the document
            HtmlNodeCollection tables = doc.DocumentNode.SelectNodes("//table");

            // Iterate all rows in the first table
            HtmlNodeCollection rows = tables[0].SelectNodes(".//tr");
            for (int i = 0; i < rows.Count; ++i)
            {

                // Iterate all columns in this row
                HtmlNodeCollection cols = rows[i].SelectNodes(".//td[@class='statBox']");
                for (int j = 0; j < cols.Count; ++j)
                {

                    // Get the value of the column and print it
                    string value = cols[j].InnerText;
                    if (value!="")
                        System.Windows.MessageBox.Show(value);
                }
            }
        }
        catch (NullReferenceException)
        {
            System.Windows.Forms.MessageBox.Show("Exception!!");
        }
    }

XML:

<?xml version="1.0" encoding="utf-8" ?>

<Stats Date="2011-01-01">
  <Player Rank="1">
    <Name>Sidney Crosby</Name>
    <Team>PIT</Team>
    <Position>C</Position>
    <GamesPlayed>39</GamesPlayed>
    <Goals>32</Goals>
    <Assists>33</Assists>
  </Player>
</Stats>

【问题讨论】:

  • 您能否提供您想要的 XML 输出示例?
  • FWIW,如果您更有效地使用 XPath,您可以大大简化您的“解析”代码。像:doc.DocumentNode.SelectNodes("//table[1]/tr/td[@class='statBox']") 会给你第一个表中的所有statBox 单元格。
  • @Cameron,我已经添加了 XML 的摘录,因为我想要它......
  • @Jeff,我还没有真正解析代码中的字段,因为如果遍历列以编译 17 个不同的不同数据列会很复杂,所以我想知道我是否可以而是将输出流式传输到 XML 文件中,如果在 C# 中有一种简单的方法可以做到这一点

标签: c# html-agility-pack xml-parsing html-parsing


【解决方案1】:

我在评论中的意思是,您在代码(嵌套循环)中所做的是拥有正确的 XPath 可以为您做的事情。使用 LINQ-to-XML 可以使编写起来更加简单。但是现在我们看到了您希望如何格式化您的 XML 文件,我们可以提供我们自己的答案。我会这样写ParseHtml() 方法:

public void ParseHtml()
{
    var htmlDoc = new HtmlDocument();
    htmlDoc.LoadHtml(htmlCode);
    var cells = htmlDoc.DocumentNode
                                    // use the right XPath rather than looping manually
                       .SelectNodes(@"//tr/tr/td[@class='statBox']")
                       .Select(node => node.InnerText.Trim())
                       .ToList();
    var elementNames = new[] { "Name", "Team", "Pos", "GP", "G", "A", "PlusMinus", "PIM", "PP", "SH", "GW", "OT", "Shots", "ShotPctg", "TOIPerGame", "ShiftsPerGame", "FOWinPctg", "UnknownField" };
    var xmlDoc =
        new XElement("Stats", new XAttribute("Date", DateTime.Now.ToShortDateString()),
            new XElement("Player", new XAttribute("Rank", cells.First()),
                // generate the elements based on the parsed cells
                cells.Skip(1)
                     .Zip(elementNames, (Value, Name) => new XElement(Name, Value))
                     .Where(element => !String.IsNullOrEmpty(element.Value))
            )
        );

    // save to your file
    xmlDoc.Save(filepath);
}

产生输出:

<?xml version="1.0" encoding="utf-8"?>
<Stats Date="1/3/2011">
  <Player Rank="1">
    <Name>Sidney Crosby</Name>
    <Team>PIT</Team>
    <Pos>C</Pos>
    <GP>39</GP>
    <G>32</G>
    <A>33</A>
    <PlusMinus>20</PlusMinus>
    <PIM>29</PIM>
    <PP>10</PP>
    <SH>1</SH>
    <GW>3</GW>
    <Shots>0</Shots>
    <ShotPctg>154</ShotPctg>
    <TOIPerGame>20.8</TOIPerGame>
    <ShiftsPerGame>21:54</ShiftsPerGame>
    <FOWinPctg>22.6</FOWinPctg>
    <UnknownField>55.7</UnknownField>
  </Player>
</Stats>

【讨论】:

  • 我正在尝试构建您的 ParseHtml() 方法,但我收到“.Select”属性错误:错误 1 ​​'HtmlAgilityPack.HtmlNodeCollection' 不包含 'SelectSingleNode' 的定义并且没有可以找到接受“HtmlAgilityPack.HtmlNodeCollection”类型的第一个参数的扩展方法“SelectSingleNode”(您是否缺少 using 指令或程序集引用?)
  • 我已经解决了这个问题...现在添加了 55.7 的值。
  • P.S.,我有 1.4.0.0 版的 HtmlAgilityPack 和 .NET 3.5
【解决方案2】:

看了一圈MSDN,终于找到了解决我问题的实现方案:

    using System;
    using HtmlAgilityPack;
    using System.Xml;

    namespace HockeyStats
    {
        class StatsParser
        {
            private string htmlCode;
            private static string fileName = "[" + DateTime.Now.ToShortDateString() + " NHL Stats].xml";

            public StatsParser(string htmlCode)
            {
                this.htmlCode = htmlCode;

                this.ParseHtml();
            }

            public void ParseHtml()
            {

                HtmlDocument doc = new HtmlDocument();
                doc.LoadHtml(htmlCode);
                XmlWriter writer = null;

                try
                {
                    // Create an XmlWriterSettings object with the correct options. 
                    XmlWriterSettings settings = new XmlWriterSettings();
                    settings.Indent = true;
                    settings.IndentChars = ("  ");
                    settings.OmitXmlDeclaration = false;

                    // Create the XmlWriter object and write some content.
                    writer = XmlWriter.Create(@"..\..\"+fileName, settings);
                    writer.WriteStartElement("Stats");
                    writer.WriteAttributeString("Date", DateTime.Now.ToShortDateString());

                // Iterate all rows within another row
                HtmlNodeCollection rows = doc.DocumentNode.SelectNodes(".//tr/tr");
                for (int i = 0; i < rows.Count; ++i)
                {
                    // Iterate all columns in this row
                    HtmlNodeCollection cols = rows[i].SelectNodes(".//td[@class='statBox']");
                    for (int j = 0; j < 20; ++j)
                    {
                                switch (j)
                                {
                                    case 0:
                                        {
                                            writer.WriteStartElement("Player");
                                            writer.WriteAttributeString("Rank", cols[j].InnerText.Trim()); break;
                                        }
                                    case 1: writer.WriteElementString("Name", cols[j].InnerText.Trim()); break;
                                    case 2: writer.WriteElementString("Team", cols[j].InnerText.Trim()); break;
                                    case 3: writer.WriteElementString("Pos", cols[j].InnerText.Trim()); break;
                                    case 4: writer.WriteElementString("GP", cols[j].InnerText.Trim()); break;
                                    case 5: writer.WriteElementString("G", cols[j].InnerText.Trim()); break;
                                    case 6: writer.WriteElementString("A", cols[j].InnerText.Trim()); break;
                                    case 7: writer.WriteElementString("PlusMinus", cols[j].InnerText.Trim()); break;
                                    case 8: writer.WriteElementString("PIM", cols[j].InnerText); break;
                                    case 9: writer.WriteElementString("PP", cols[j].InnerText); break;
                                    case 10: writer.WriteElementString("SH", cols[j].InnerText); break;
                                    case 11: writer.WriteElementString("GW", cols[j].InnerText); break;
                                    case 12: writer.WriteElementString("OT", cols[j].InnerText); break;
                                    case 13: writer.WriteElementString("Shots", cols[j].InnerText); break;
                                    case 14: writer.WriteElementString("ShotPctg", cols[j].InnerText); break;
                                    case 15: writer.WriteElementString("TOIPerGame", cols[j].InnerText); break;
                                    case 16: writer.WriteElementString("ShiftsPerGame", cols[j].InnerText); break;
                                    case 17: writer.WriteElementString("FOWinPctg", cols[j].InnerText); break;

                                }
                            }
                        }
                        writer.WriteEndElement();
                    }
                    writer.WriteEndElement();
                    writer.Flush();
                }
                finally
                {
                    if (writer != null)
                        writer.Close();
                }
            }
        }
    }

提供以下 XML 文件作为输出:

<?xml version="1.0" encoding="utf-8" ?> 
<Stats Date="2011-01-01">
 <Player Rank="1">
  <Name>Sidney Crosby</Name> 
  <Team>PIT</Team> 
  <Pos>C</Pos> 
  <GP>39</GP> 
  <G>32</G> 
  <A>33</A> 
  <PlusMinus>20</PlusMinus> 
  <PIM>29</PIM> 
  <PP>10</PP> 
  <SH>1</SH> 
  <GW>3</GW> 
  <Shots>0</Shots> 
  <ShotPctg>154</ShotPctg> 
  <TOIPerGame>20.8</TOIPerGame> 
  <ShiftsPerGame>21:54</ShiftsPerGame> 
  <FOWinPctg>22.6</FOWinPctg> 
 </Player>
</Stats>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-08-23
    • 2018-05-01
    • 2010-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多