使用Generics完成HTML scraper。对还是错?

时间:2011-12-30 12:33:37

标签: c# html xml generics scraper

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

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

的Generic类

IEntity是可以删除的所有对象类型都将实现的接口,尽管我还没有弄清楚接口成员将是什么。

所以你可以有效地说出

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

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

我遇到的问题是

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

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

  3. 最后,我不相信仿制药是解决这个问题的正确方法,因为它开始变得非常复杂。你是否同意或谴责我在这里选择的解决方案,如果没有,你会做什么呢?

2 个答案:

答案 0 :(得分: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>
}

效果相当好,无需为每种刮刀类型创建新类。但是,我发现在我的情况下它太有限了。我最终需要为几乎所有类型的刮刀提供不同的类,所以我只是继续使用继承。

答案 1 :(得分:1)

我宁愿专注于你的解析器/验证器类,因为正确地设计它们对于将来使用的简易性是非常重要的。我认为这个机制将根据输入确定哪个解析器/验证器更为重要如何

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