现在我使用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>
除了网页的实际网址,这些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中实现这一点,是否有任何现有的更好的方法来解决这个问题?
注意: 在谷歌上搜索我发现了一些研究论文,但大多数都需要一些先前的学习数据,准确性也是一个值得关注的问题。
答案 0 :(得分:4)
你给出的方法与Gilbert的方法非常类似,除了 正则表达式部分。我不想进入丑陋的正则表达世界 计划在许多其他领域使用模板方法 电影信息例如价格,产品规格提取等。
您描述的模板实际上并不是正常意义上的“模板”:一组静态内容,它被转储到输出中,并在其中插入一堆动态内容。相反,它是模板的“反向” - 它是一种融化的解析模式。丢弃,留下所需的参数。
由于您的网页会定期更改,因此您不希望对要精确解析的内容进行硬编码,而是希望“放大”其基本功能,从而尽量减少假设。即你想要提交字面匹配的关键文本,如“评级:”,并以更加灵活的方式处理交错标记,如"<b/>"
- 忽略它并允许它更改而不会中断。
组合(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)作为满足您需求的最简单,最强大和最灵活的解决方案。
答案 1 :(得分:2)
这里不是真正基于模板的方法,但如果您只是将Selector查询外部化到配置文件中,jsoup仍然是一个可行的解决方案。
您的非程序员甚至不必查看HTML,只需更新配置文件中的选择器即可。像SelectorGadget这样的东西可以更容易地选择实际使用的选择器。
答案 2 :(得分:1)
我如何在Java中实现这一点,是否有任何现有的更好的方法来解决这个问题?
模板方法是一种很好的方法。你在问题中提出了所有原因。
您的模板将仅包含您要处理的HTML,而不包含任何其他内容。这是我的例子,基于你的例子。
<div>
<p><strong>Score:</strong>{MOVIE_RATING}</p>
<p><strong>Director:</strong>{MOVIE_DIRECTOR}</p>
</div>
基本上,您可以使用Jsoup来处理模板。然后,当您使用Jsoup处理网页时,检查所有已处理的模板以查看是否匹配。
在模板匹配中,您可以在已处理的模板中找到关键字,然后在已处理的网页中找到相应的值。
是的,这将是很多编码,比我的描述更难。您的Java程序员必须将此描述分解为更简单,更简单的任务,直到他或她可以编写任务代码。
答案 3 :(得分:1)
如果网页频繁更改,那么您可能希望将搜索MOVIE_RATING等字段限制在页面的最小部分,并忽略其他所有内容。有两种可能性:您可以为每个字段使用正则表达式,也可以使用某种CSS选择器。我认为要么可以工作,要么“模板”可以包含一个简单的搜索表达式列表,正则表达式或css,您将应用它们。只需浏览列表并提取您可以使用的内容,如果由于页面更改而找不到某个特定字段,则会失败。
例如,正则表达式可能如下所示:
"Score:"(.)*[0-9]\.[0-9]\/[0-9]
(我没有测试过这个。)
答案 4 :(得分:1)
或者你可以尝试不同的方法,使用我称之为“规则”而不是模板:对于你需要从页面获得的每条信息,你可以定义提取文本的jQuery表达式。通常当页面更改很小时,编写良好的jQuery表达式仍会产生相同的结果。
然后你可以使用Jerry(Java中的jQuery),使用几乎相同的表达式来获取你正在寻找的文本。所以它不仅仅是选择器,还有其他jQuery方法来遍历/过滤DOM树。
例如,某些 Director 文本的规则将是(以sudo-java-jerry-code排序):
$.find("div#movie").find("div:nth-child(2)")....text();
规则中可能有更多(更复杂)的表达式,分布在多行中,例如迭代某些节点等。
如果您是OO人员,则可以在其自己的实施中定义每个规则。如果你是一个常规的人,你甚至可以在需要时重写规则,而无需重新编译你的项目,仍然在java中。等
如您所见,此处的核心理念是定义规则如何查找文字;并且不要匹配模式,因为这可能是脆弱的微小变化 - 想象如果只是在两个div之间添加了一个空格:)。在我的这个例子中,我使用了类似jQuery的语法(实际上,它是类似Jerry的语法,因为我们在Java中)来定义规则。这只是因为jQuery很受欢迎且简单,而且您的Web开发人员也知道它;最后,您可以定义自己的语法(取决于您正在使用的解析工具):例如,您可以将HTML解析为DOM树,然后使用辅助方法编写规则,如何将其遍历到感兴趣的位置。 Jerry还允许您访问底层DOM树。
希望这有帮助。
答案 5 :(得分:1)
我使用以下方法在我的个人项目中执行类似的操作,从而生成RSS源the leading real estate website in spain。
使用此工具,我找到了我目前居住的租用地点; - )
当然,每当他们更改原始页面时,您都必须更改XPath表达式。我能想到的另一种方法 - 对原始HTML源的语义分析 - 远远超出了我的谦逊技能; - )