13

现在我使用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 程序的输入,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 中实现这一点,是否有任何现有的更好的方法来解决这个问题?

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

4

6 回答 6

4

除了正则表达式部分外,您提供的方法与吉尔伯特的方法非常相似。我不想进入丑陋的正则表达式世界,我计划在电影信息之外的许多其他领域使用模板方法,例如价格、产品规格提取等。

  1. 您描述的模板实际上并不是正常意义上的“模板”:一组静态内容被转储到输出中,其中插入了一堆动态内容。相反,它是模板的“逆向”——它是一种被吞并丢弃的解析模式,留下需要的参数。

  2. 因为您的网页会定期更改,所以您不希望将要解析的内容硬编码得太精确,而是希望“放大”其基本特征,尽量减少假设。即,您希望承诺按字面意思匹配“Rating:”等关键文本,并"<b/>"以更灵活的方式处理交错标记——忽略它并允许它在不中断的情况下进行更改。

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

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

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

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

    3D。是否有一个 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) 作为满足您需求的最简单、最强大和最灵活的解决方案。

于 2013-03-11T01:31:22.920 回答
2

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

您的非程序员甚至不必查看 HTML,只需更新配置文件中的选择器即可。像SelectorGadget这样的东西可以更容易地挑选出实际使用的选择器。

于 2013-03-07T02:54:32.170 回答
1

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

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

您的模板将仅包含您要处理的 HTML,没有其他内容。这是基于您的示例的示例。

<div>
<p><strong>Score:</strong>{MOVIE_RATING}</p>
<p><strong>Director:</strong>{MOVIE_DIRECTOR}</p>
</div>

基本上,您将使用 Jsoup 来处理您的模板。然后,当您使用 Jsoup 处理网页时,您检查所有已处理的模板以查看是否有匹配项。

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

是的,这将是大量的编码,并且比我的描述更难。您的 Java 程序员将不得不将此描述分解为越来越简单的任务,直到她或他能够编写这些任务。

于 2013-03-04T19:42:06.553 回答
1

如果网页经常更改,那么您可能希望将 MOVIE_RATING 等字段的搜索限制在页面的最小可能部分,而忽略其他所有内容。有两种可能性:您可以为每个字段使用正则表达式,或者您可以使用某种 CSS 选择器。我认为任何一个都可以工作,并且“模板”可以包含一个简单的搜索表达式列表,正则表达式或 css,您可以应用。只需滚动列表并提取您可以提取的内容,如果由于页面更改而找不到某些特定字段,则失败。

例如,正则表达式可能如下所示:

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

(我没有测试过这个。)

于 2013-03-07T00:09:27.937 回答
1

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

然后您可以使用Jerry(Java 中的 jQuery),使用几乎相同的表达式来获取您要查找的文本。因此,它不仅与选择器有关,而且您还有其他用于遍历/过滤 DOM 树的 jQuery 方法。

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

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

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

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

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

希望这可以帮助。

于 2013-03-07T21:04:19.197 回答
1

我使用以下方法在我的个人项目中执行类似的操作,该项目从西班牙领先的房地产网站生成 RSS 提要。

使用这个工具,我找到了我目前居住的租来的地方;-)

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

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

于 2013-03-12T12:53:20.820 回答