4

我正在尝试使用 jsoup 解析以下 HTML,但无法获得正确的语法。

<div class="info"><strong>Line 1:</strong> some text 1<br>
  <b>some text 2</b><br>
  <strong>Line 3:</strong> some text 3<br>
</div>

我需要在三个不同的变量中捕获一些文本 1、一些文本 2 和一些文本 3。

我有第一行的 xpath(第 3 行应该类似),但无法计算出等效的 css 选择器。

//div[@class='info']/strong[1]/following::text()

在另一个上,我有几百个 html 文件,需要解析并从中提取数据以存储在数据库中。Jsoup 是最好的选择吗?

4

3 回答 3

5

看起来 Jsoup 确实无法处理从具有混合内容的元素中获取文本。这是一个使用您制定的使用XOMTagSoup的 XPath 的解决方案:

import java.io.IOException;

import nu.xom.Builder;
import nu.xom.Document;
import nu.xom.Nodes;
import nu.xom.ParsingException;
import nu.xom.ValidityException;
import nu.xom.XPathContext;

import org.ccil.cowan.tagsoup.Parser;
import org.xml.sax.SAXException;

public class HtmlTest {
    public static void main(final String[] args) throws SAXException, ValidityException, ParsingException, IOException {
        final String html = "<div class=\"info\"><strong>Line 1:</strong> some text 1<br><b>some text 2</b><br><strong>Line 3:</strong> some text 3<br></div>";
        final Parser parser = new Parser();
        final Builder builder = new Builder(parser);
        final Document document = builder.build(html, null);
        final nu.xom.Element root = document.getRootElement();
        final Nodes textElements = root.query("//xhtml:div[@class='info']/xhtml:strong[1]/following::text()", new XPathContext("xhtml", root.getNamespaceURI()));
        for (int textNumber = 0; textNumber < textElements.size(); ++textNumber) {
            System.out.println(textElements.get(textNumber).toXML());
        }
    }
}

这输出:

 some text 1
some text 2
Line 3:
 some text 3

虽然不知道你想要做什么的更多细节,但我不确定这是否正是你想要的。

于 2012-08-05T15:08:04.000 回答
2

可以获得对单个 TextNode 的对象引用。我想也许你看过 Jsoup 的TextNode对象。

Element顶层的文本是 TextNode 对象的一个​​实例。例如,“some text 1”和“some text 3”都是“<div class='info'>”下的TextNode对象,“Line 1:”是“<strong>”下的TextNode对象

元素对象有一个textNodes()方法,可用于获取这些 TextNode 对象。

检查以下代码:

String html = "<html>" +
                  "<body>" +
                      "<div class="info">" +
                          "<strong>Line 1:</strong> some text 1<br>" +
                          "<b>some text 2</b><br>" +
                          "<strong>Line 3:</strong> some text 3<br>" +
                      "</div>" +
                  "</body>" +
              "</html>";

Document document = JSoup.parse(html);
Element infoDiv = document.select("div.info").first();
List<TextNode> infoDivTextNodes = infoDiv.textNodes();

此代码查找第一个 <div> 元素,该元素具有 key="class" 和 value="info" 的属性。然后直接在“<div class='info'>”下获取对所有 TextNode 对象的引用。该列表如下所示:

List<TextNode>[" some text 1", " some text 3"]

TextNode 对象有一些与它们相关联的甜蜜数据和方法,您可以使用它们,并且扩展Node为您提供更多可以使用的功能。

以下是获取 div 中每个 TextNode 的对象引用的示例,其中 class="info"。

for(Iterator<Element> elementIt = document.select("div.info").iterator(); elementIt.hasNext();){
    Element element = elementIt.next();

    for (Iterator<TextNode> textIt = element.textNodes().iterator(); textIt.hasNext();) {
        TextNode textNode = textIt.next();
        //Do your magic with textNode now.
        //You can even reference it's parent via the inherited Node Object's 
        //method .parent();
    }
}

使用这种嵌套迭代器技术,您可以访问对象的所有文本节点,并且通过一些巧妙的逻辑,您可以在 Jsoup 的结构中做任何您想做的事情。

我已经为我过去创建的拼写检查方法实现了这个逻辑,它确实对具有大量元素的非常大的 html 文档有一些性能影响,也许是很多列表或其他东西。但是,如果您的文件长度合理,您应该获得足够的性能。

以下是获取 Document 的每个 TextNode 的对象引用的示例。

Document document = Jsoup.parse(html);

for (Iterator<Element> elementIt = document.body().getAllElements().iterator(); elementIt.hasNext();) {
    Element element = elementIt.next();
    //Maybe some magic for each element..

    for (Iterator<TextNode> textIt = element.textNodes().iterator(); textIt.hasNext();) {
        TextNode textNode = textIt.next();
        //Lots of magic here for each textNode..
    }
}
于 2013-07-02T00:59:14.577 回答
1

我认为你的问题是你感兴趣的文本,只有一个短语包含在任何定义标签中,“一些文本 2”被<b> </b>标签包围。所以这很容易通过以下方式获得:

String text2 = doc.select("div.info b").text();

返回

some text 2

其他感兴趣的文本只能定义为<div class="info">标签内的文本,仅此而已。因此,我所知道的唯一方法是获取这个较大元素所包含的所有文本:

String text1 = doc.select("div.info").text();

但不幸的是,这会获取该元素所持有的所有文本:

Line 1: some text 1 some text 2 Line 3: some text 3

这是我能做的最好的事情,我希望有人能找到更好的答案并继续关注这个问题。

于 2012-08-05T22:16:46.180 回答