如何使用C#自动将在线HTML表格下载到本地

6

简单来说:

我有一些包含信息的不同网站和表格数据,希望可以"本地化"查询。

我已经寻找了一些可能性并且有一些自己的想法。

  1. 在Excel中,我发现一个函数,我可以导航到一个网页并从一个表格中复制数据。问题是这只会发生一次。表格中的数据将每周更新一次,所以每次打开我的程序时,需要自动更新Excel。

  2. 我可以使用爬虫,但那样我就必须为每个表写一个不同的解决方案并找到一种保存方法。

我现在拥有一个MySQL数据库,其中包含我程序所需的许多信息,因此如果任何解决方案需要数据库,完全可以接受。

关于我的程序:将用C#编写,首先作为本地程序,然后转换为MVC项目。对两个项目的建议非常欢迎,如果您需要更多信息,请在评论中说明,我会尽力描述更多内容。 :)

编辑!1

非常抱歉,我一开始没有告诉你我所谈论的是哪些表格,但当我开始这个问题时,我仍然需要找到所有的表格。现在,我已经取出其中一些表格,向您展示我需要处理的不同类型的表格数据。关于这个项目,它应该告诉您,我计划制作的程序仅供私人使用,而不是出售。我不知道在公共网站上抓取的规则,所以我把它保持为私人。

表2 表3

正如您所看到的,有很多足球数据以非常不同的方式显示,因此我需要了解哪种方式最适合我收集数据,因为我相信使用这些知识设计数据库会更容易。


我发布了一个初步答案,但我想问一下,这些网站是你控制的还是你不拥有的公共网站?此外,您能否详细说明您拥有包含大量所需信息的数据库,这些数据来自哪里?最后,Excel在这里是否必要,或者只是因为它可以手动完成所需工作,而您希望(更喜欢)将其自动化?这将有助于使我的答案更具体,并提供一些代码示例和免费工具链接。 - Dmitriy Khaykin
@DavidKhaykin 这是一个公共网站,我无法控制。数据库是MySQL 5。是的,Excel只在这里,因为我知道内置函数。 :)希望这足够了解。现在我所拥有的信息仅仅是一些没有联系的表格。在设计之前,我正在研究构建数据库的方法。 :) - Anders Gerner
它不允许我添加第一张表。这是它的链接:http://soccernet.espn.go.com/stats/_/league/eng.1/barclays-premier-league?cc=5739 - Anders Gerner
6个回答

7
Anders,Excel内置了一种获取数据的方法,你只需执行一次。下次只需刷新查询即可。请查看此链接。 html parsing of cricinfo scorecards 后续 在这个特定的网站上,如果您查看源代码,您会发现该表格没有ID。所有三个表格都有相同的类“tablehead”。如果您想,在工作簿打开事件中循环遍历所有表格并提取数据,您的工作将变得更加容易,因为所有3个表格都具有相同的类。
或者,您也可以这样做。
在Excel中,点击文件|打开,在对话框中直接输入下面提到的URL。你会发现Excel会整齐地堆叠数据 :)
实际上,你可以编写一个小的宏/代码,打开一个临时工作簿,然后打开URL,然后将临时工作簿中的表格提取到你的工作簿中。我的估计是,在良好的互联网连接下,整个过程不应超过15秒。

这非常有帮助,但不幸的是,我注意到内置函数无法获取所有类型的表格。 :( 但你在另一个问题上发布的帖子非常好! - Anders Gerner
请尝试查看此页面:http://soccernet.espn.go.com/stats/_/league/eng.1/barclays-premier-league?cc=5739有3个表格,但似乎Excel没有检测到它们。:( - Anders Gerner
我尝试使用“文件|打开”功能,它似乎非常好用。这样我就可以轻松地从特定单元格中获取值 :)我之前尝试过一些宏代码,但在Excel中并不是很熟练。尝试了VBA,但我不是Visual Basic的忠实粉丝。希望不会太麻烦,你能帮忙编写一个宏代码,每次打开时都能刷新数据吗? :) - Anders Gerner
谢谢伙计,这里才晚上11点。 :) 但是你应该去睡觉了,我们可不能因为你对我友好而让你的妻子离婚! :)干杯。 - Anders Gerner

1
如果我只是閱讀網頁信息,我發現 HtmlAgilityPack 非常有用。它使使用 LINQ 查找帶有識別信息的某些標記並輕鬆導航到子標記變得容易。因此,您可以找到一個 <table> 標籤,然後輕鬆找到 <tr> 和 <td> 並捕獲 Text 屬性以查找單元格的內容。

我同意。我已经在使用HAP来获取网站数据了。 :) 我想我正在寻找更多关于这两种解决方案的示例。 - Anders Gerner
@AndersGerner - HAP兼容XPATH和XSLT。这意味着您各种来源和唯一目标(SQL数据库)之间的耦合/绑定可以位于每个源的一个XSLT文件中。对于每个源,一个转换可以导致一个XML流作为输出,您可以轻松地将其注入任何数据库。这种解决方案的美妙之处在于,当源发生更改时,您无需重新编译任何内容,只需更新XSLT以匹配更改即可。缺点是您需要了解一些XSLT和XPATH。 - Simon Mourier
@SimonMourier 我知道一些使用Selenium和HAP的XPATH,但对于XSLT来说还是新手。您知道哪些好的网站可以学习它吗?这似乎是解决我的问题的好方法! :) - Anders Gerner

1

你可以使用Visual Web Ripper,它们有一个API可以从.NET中使用,并且你可以使用他们的设计师构建模板来提取你想要的数据,非常容易使用,我的公司甚至用它来从网站上提取评论,即使有分页和搜索。


我尝试了这个Visual Web Ripper,但感觉有点太复杂了。视频中展示的方式让它看起来非常容易,但我找不到任何收集表格数据的方法。我试过了,但没有成功。可能是程序的问题,也可能是我的问题.. :P - Anders Gerner

0

这里是使用HtmlAgilityPack的一些示例代码:

using System;
using System.Collections.Generic;
using System.Web;
using System.Xml.XPath;

using HtmlAgilityPack;

namespace TableRipper
{
    class Program
    {
        static List<string> SerializeColumnSet(XPathNodeIterator columnSet)
        {
            List<string> serialized = new List<string>();

            while (columnSet.MoveNext())
            {
                string value = HttpUtility.HtmlDecode(columnSet.Current.Value.ToString().Trim());

                if (value.Contains(",") || value.Contains("\""))
                {
                    value = string.Concat('"', value.Replace("\"", "\"\""), '"');
                }

                serialized.Add(value);
            }

            return serialized;
        }

        static List<List<string>> RipTable(string url, string xpath, bool includeHeaders = true)
        {
            HtmlWeb web = new HtmlWeb();
            HtmlDocument document = web.Load(url);
            XPathNavigator navigator = document.CreateNavigator();
            XPathNodeIterator tableElementSet = navigator.Select(xpath);
            List<List<string>> table = new List<List<string>>();

            if (tableElementSet.MoveNext())
            {
                XPathNavigator tableElement = tableElementSet.Current;
                XPathNavigator tableBodyElement = tableElement.SelectSingleNode("tbody") ?? tableElement;
                XPathNodeIterator tableRowSet = tableBodyElement.Select("tr");
                bool hasRows = tableRowSet.MoveNext();

                if (hasRows)
                {
                    if (includeHeaders)
                    {
                        XPathNavigator tableHeadElement = tableElement.SelectSingleNode("thead");
                        XPathNodeIterator tableHeadColumnSet = null;

                        if (tableHeadElement != null)
                        {
                            tableHeadColumnSet = tableHeadElement.Select("tr/th");
                        }
                        else if ((tableHeadColumnSet = tableRowSet.Current.Select("th")).Count > 0)
                        {
                            hasRows = tableRowSet.MoveNext();
                        }

                        if (tableHeadColumnSet != null)
                        {
                            table.Add(SerializeColumnSet(tableHeadColumnSet));
                        }
                    }

                    if (hasRows)
                    {
                        do
                        {
                            table.Add(SerializeColumnSet(tableRowSet.Current.Select("td")));
                        }
                        while (tableRowSet.MoveNext());
                    }
                }
            }

            return table;
        }

        static void Main(string[] args)
        {
            foreach (List<string> row in RipTable(args[0], args[1]))
            {
                Console.WriteLine(string.Join(",", row));
            }
        }
    }
}

测试过:

http://www.msn.com "//table[@summary='Market Update']"

http://www.worldclimate.com/cgi-bin/data.pl?ref=N48W121+2200+450672C "//table[1]"

它还远非完美,例如它无法处理colspan或rowspan,但这是一个开始。


0
我的方法是使用工具为包含表格数据的每个URL生成RSS源,然后在您的UI中显示数据(无论是WPF、WinForms还是asp.net)。这样,当您找到/获取新网站以提取数据时,您可以轻松地设置其他“频道”,并且您的工作将是将新站点规范化为您的标准rss源格式(可在这些工具之一中配置),甚至可以根据配置设置配置您的UI以基于附加的源拉取附加的源,因此添加新站点时无需重新编译。
您可以决定将源数据存储在数据库中或仅实时显示,并自动定期实现数据缓存/刷新。我认为这种方法的基本前提是将每个站点的各种表格格式标准化为一个通用格式(rss或其他),然后只关注在应用程序中消耗一个标准格式。这种方法可以在类库中设置,该类库以通用格式呈现数据,然后该类库可以被您的C#应用程序和Web应用程序所使用。

编辑:这里有一个链接,提供了关于几个工具的好信息,可以用来从任何网站创建RSS源:http://profy.com/2007/09/30/7-tools-to-make-an-rss-feed-of-any-website/


不错的方法,但你知道有什么工具可以制作这些RSS源吗? :) - Anders Gerner
我知道几个,我会挑选其中一些并举例说明。如果您能提供至少两个这些网站的链接,我可以提供一个示例,让您开始使用。 - Dmitriy Khaykin

0

你可以使用Selenium(用于自动化Web测试)。它是一个非常有用的工具。它的API将允许您通过XPath、CSS或DOM搜索特定表格等操作。

您可以通过许多不同的语言通过“远程控制”来驱动Selenium。请参见:http://seleniumhq.org/projects/remote-control/

例如,对于C#,请参见:http://www.theautomatedtester.co.uk/tutorials/selenium/selenium_csharp_nunit.htm

请参见StackoverFlow获取一些示例: 如何使用Selenium RC检索表列中的文本?


我以前用过Selenium,但不知道为什么没有想到尝试这个.. 我会试一下,并回来告诉你结果 :) 谢谢! - Anders Gerner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接