1

我的要求是下载和抓取各种 HTML 页面,根据我们在该页面上查找的对象类型从页面上的代码中提取对象列表。例如,一个页面可能包含一个嵌入的医生手术列表,另一个可能包含一个主要信任列表等。我必须逐页查看并最终得到适当对象类型的列表。

我选择这样做的方式是有一个名为的通用类HTMLParser<T> where T : IEntity, new()

IEntity是所有可以被抓取的对象类型都将实现的接口,虽然我还没有弄清楚接口成员将是什么。

所以你可以有效地说

HTMLParser<Surgery> parser = new HTMLParser<Surgery>(URL, XSD SCHEMA DOC);
IList<Surgery> results = parser.Parse();

Parse()将验证从 URL 下载的 HTML 字符串是否包含符合提供的 XSD 文档的块,然后以某种方式使用此模板提取一个List<Surgery>手术对象,每个对象对应于 HTML 字符串中的一个 XML 块。

我遇到的问题是

  1. 我不确定如何以一种很好的方式为每种对象类型指定模板,除了HTMLParser<Surgery> parser = new HTMLParser<Surgery>(new URI("...."), Surgery.Template);有点笨拙。谁能建议使用 .NET 3.0/4.0 的更好方法?

  2. 我不确定如何以通用方式获取 HTML 字符串、获取 XSD 或 XML 模板文档,并返回通用类型的构造对象的通用列表。谁能建议如何做到这一点?

  3. 最后,我不相信泛型是解决这个问题的正确方法,因为它开始看起来非常复杂。你会同意还是谴责我在这里选择的解决方案,如果不同意,你会怎么做?

4

2 回答 2

2

我也不相信泛型是正确的解决方案。我使用良好的旧继承实现了与此非常相似的东西,我仍然认为这是完成这项工作的正确工具。

当您想要对不同类型执行相同的操作时,泛型很有用。例如,集合是泛型非常方便的一个很好的例子。

另一方面,当您希望对象继承通用功能,然后扩展和/或修改该功能时,继承很有用。用泛型做这件事很麻烦。

我的刮板基类看起来像这样:

public class ScraperBase
{
    // Common methods for making web requests, etc.

    // When you want to download and scrape a page, you call this:
    public List<string> DownloadAndScrape(string url)
    {
        // make request and download page.
        // Then call Scrape ...
        return Scrape(pageText);
    }

    // And an abstract Scrape method that returns a List<string>
    // Inheritors implement this method.
    public abstract List<string> Scrape(string pageText);
}

那里还有一些其他的东西用于记录、错误报告等,但这就是它的要点。

现在,假设我有一个 Wordpress 博客抓取工具:

public class WordpressBlogScraper : ScraperBase
{
    // just implement the Scrape method
    public override List<string> Scrape(string pageText)
    {
        // do Wordpress-specific parsing and return data.
    }
}

我可以做同样的事情来为任何页面、站点或数据类编写一个 Blogspot 刮板或自定义刮板。

我实际上尝试做类似的事情,但我没有使用继承,而是使用了刮板回调函数。就像是:

public delegate List<string> PageScraperDelegate(string pageText);

public class PageScraper
{
    public List<string> DownloadAndScrape(string url, PageScraperDelegate callback)
    {
        // download data to pageText;
        return callback(pageText);
    }
}

然后你可以写:

var myScraper = new PageScraper();
myScraper.DownloadAndScrape("http://example.com/index.html", ScrapeExample);

private List<string> ScrapeExample(string pageText)
{
    // do the scraping here and return a List<string>
}

这工作得相当好,并且不必为每种刮板类型创建一个新类。但是,我发现在我的情况下它太局限了。我最终需要为几乎每种类型的刮刀使用不同的类,所以我继续使用继承。

于 2011-12-30T15:53:40.060 回答
1

我宁愿专注于您的解析器/验证器类,因为正确设计它们对于未来使用的便利性至关重要。我认为更重要的是该机制将如何根据输入确定使用哪个解析器/验证器。

此外,当您被告知需要解析另一种类型的网站(例如Invoice实体)时会发生什么 - 您是否能够通过 2 个简单的步骤扩展您的机制以处理此类要求?

于 2011-12-30T16:20:35.960 回答