基于Java模板提取网页信息



现在我使用Jsoup从一些第三方网页中提取某些信息(不是所有文本),我定期这样做。这工作很好,直到某些网页的HTML改变,这种改变导致改变现有的Java代码,这是一个繁琐的任务,因为这些网页的变化非常频繁。它还需要程序员来修复Java代码。下面是一个我感兴趣的HTML代码示例:

<div>
<p><strong>Score:</strong>2.5/5</p>
<p><strong>Director:</strong> Bryan Singer</p>
</div>
<div>some other info which I dont need</div>

现在这是我想做的,我想保存这个网页(一个HTML文件)本地并创建一个模板,如:

<div>
<p><strong>Score:</strong>{MOVIE_RATING}</p>
<p><strong>Director:</strong>{MOVIE_DIRECTOR}</p>
</div>
<div>some other info which I dont need</div>

与网页的实际url一起,这些HTML模板将是Java程序的输入,该程序将找出这些预定义关键字的位置(例如{MOVIE_RATING}{MOVIE_DIRECTOR})并从实际网页中提取值。

这样我就不必每次网页更改时都修改Java程序,我只需要保存网页的HTML并将数据替换为这些关键字,其余的将由程序处理。例如,将来实际的HTML代码可能看起来像这样:

<div>
<div><b>Rating:</b>**1/2</div>
<div><i>Director:</i>Singer, Bryan</div>
</div>

和相应的模板看起来像这样:

<div>
<div><b>Rating:</b>{MOVIE_RATING}</div>
<div><i>Director:</i>{MOVIE_DIRECTOR}</div>
</div>

创建这种类型的模板也可以由非程序员完成,任何可以编辑文件的人。

现在的问题是,我如何在Java中实现这一点,是否有任何现有的更好的方法来解决这个问题?

注意:在谷歌上我发现了一些研究论文,但大多数都需要一些先前的学习数据和准确性也是一个问题。

你给出的方法与吉尔伯特的方法非常相似正则表达式部分。我不想踏入丑陋的正则表达式世界,我是计划将模板方法用于许多其他领域之外电影信息,如价格,产品规格提取等

  1. 你描述的模板实际上并不是一个正常意义上的"模板":一组静态内容被转储到输出中,其中插入了一堆动态内容。相反,它是模板的"反面"——它是一种被"吞掉"的解析模式。丢弃,留下所需参数等待查找。

  2. 因为你的网页经常变化,你不想硬编码的内容被解析得太精确,但想"放大"它的"基本功能,做最小的假设。例如,你想要承诺从字面上匹配关键文本,如"Rating:",并以更灵活的方式处理交错标记,如"<b/>" -忽略它并允许它在不破坏的情况下改变。

  3. 当您组合(1)和(2)时,您可以给结果任何您喜欢的名称,但它是使用正则表达式进行解析。即模板方法是使用正则表达式的解析方法——它们是同一种方法。问题是:正则表达式应该采用什么形式?

    3。如果您使用java手工编码来进行解析,那么显而易见的答案是正则表达式格式应该只是java.util.regex格式。其他任何东西都是开发负担,是"非标准的",很难维护。

    3 b。如果您想使用一个支持html的解析器,那么jsoup是一个很好的解决方案。问题是您需要比jsoup提供的更多的文本/正则表达式处理和灵活性。它似乎过于锁定在特定的html标签和结构中,因此当页面更改时就会中断。

    3 c。您可以使用功能强大得多的语法控制的通用文本解析器,例如ANTLR—使用一种受backus- name启发的语法形式来控制解析,并插入生成器代码来处理解析后的数据。在这里,解析语法表达式可能非常强大,它具有复杂的规则,可以决定文本在页面上如何排序,以及文本字段和值如何相互关联。这种能力超出了你的需求,因为你不是在处理一种语言。不可避免的事实是,您仍然需要描述要跳过的丑陋部分——例如标记标签等。在你获得生产力回报之前,第一次与ANTLR搏斗涉及教育投资。

    3 d。是否有一个java工具,只是使用一个简单的模板类型的方法来给出一个简单的答案?好吧,谷歌搜索没有给太多的希望https://www.google.com/search?q=java+template+based+parser&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:en-GB:official&client=firefox-a。我相信,任何创建这种野兽的尝试都将退化为基本的正则表达式解析或更高级的语法控制解析,因为匹配/忽略/替换文本的基本需求推动了这些方向的解决方案。其他任何方法都太简单而无法实际工作。

我认为(3A)是最简单、最强大、最灵活的解决方案。

这里不是真正的基于模板的方法,但是如果您只是将选择器查询外部化到配置文件中,jsoup仍然是一个可行的解决方案。

你的非程序员甚至不需要看到HTML,只需更新配置文件中的选择器。像SelectorGadget这样的东西将使选择实际使用的选择器更容易。

我如何在Java中实现这一点,是否有任何现有的和更好的方法来解决这个问题?

模板方法是一个很好的方法。你在你的问题中给出了所有的原因。

你的模板将只包含你想要处理的HTML,而不包含其他内容。这是我以你的例子为基础的例子。

<div>
<p><strong>Score:</strong>{MOVIE_RATING}</p>
<p><strong>Director:</strong>{MOVIE_DIRECTOR}</p>
</div>
基本上,您将使用Jsoup来处理您的模板。然后,当您使用Jsoup处理网页时,您检查所有已处理的模板,以查看是否有匹配。

在模板匹配中,您在处理过的模板中找到关键字,然后在处理过的网页中找到相应的值。

是的,这将是大量的编码,比我的描述更困难。您的Java程序员将不得不把这个描述分解成越来越简单的任务,直到她或他能够对这些任务进行编码。

如果网页频繁更改,那么您可能希望将搜索MOVIE_RATING等字段限制在页面的最小部分,并忽略其他所有字段。有两种可能性:您可以为每个字段使用正则表达式,也可以使用某种CSS选择器。我认为这两种方式都可以,而且"模板"都可以包含一个简单的搜索表达式列表,你可以使用正则表达式或css。只需遍历列表并提取您可以提取的内容,如果由于页面更改而没有找到某些特定字段,则失败。

例如,正则表达式可以像这样:

"Score:"(.)*[0-9].[0-9]/[0-9]

(我还没有测试过)

或者您可以尝试不同的方法,使用我称之为"规则"而不是模板:对于您需要从页面中获取的每一条信息,您可以定义提取文本的jQuery表达式。通常,当页面变化很小时,同样编写良好的jQuery表达式仍然会给出相同的结果。

然后您可以使用Jerry (Java中的jQuery),使用几乎相同的表达式来获取您正在寻找的文本。所以这不仅仅是关于选择器,你还可以使用其他jQuery方法来遍历/过滤DOM树。

例如,一些Director文本的规则是(类似于sudo-java-jerry-code):

$.find("div#movie").find("div:nth-child(2)")....text();

规则中可能有更多(和更复杂)的表达式,分布在几行中,例如迭代一些节点等。

如果您是OO人员,则可以在其自己的实现中定义每个规则。如果您是groovy爱好者,您甚至可以在需要时重写规则,而无需重新编译项目,并且仍然使用java。等。

如你所见,这里的核心思想是定义规则如何找到你的文本;不要匹配模式,因为这可能对微小的变化很脆弱——想象一下,如果只是在两个div之间添加了一个空格:)。在我的这个例子中,我使用了类似于jquery的语法(实际上,它是类似于Java的语法,因为我们是在Java中)来定义规则。这只是因为jQuery很流行且简单,并且你的web开发人员也知道;最后,您可以定义自己的语法(取决于您正在使用的解析工具):例如,您可以将HTML解析为DOM树,然后使用您的助手方法编写规则,如何遍历它到感兴趣的位置。Jerry还允许您访问底层DOM树。

希望这对你有帮助。

我在我的一个个人项目中使用了以下方法来做类似的事情,该项目从这里生成一个RSS提要西班牙领先的房地产网站。

使用这个工具我找到了我现在住的地方;-)

  1. 页面获取HTML代码
  2. 将HTML转换为XHTML。我使用了这个库,我想今天可能有更好的选项可用
  3. 使用XPath将XHTML导航到
  4. 中您感兴趣的信息

当然,每次更改原始页面时,您都必须更改XPath表达式。我能想到的另一种方法——对原始HTML源代码进行语义分析——远远超出了我的能力范围;-)

最新更新