【问题标题】:Jsoup css selector code (xpath code included)Jsoup css 选择器代码(包括 xpath 代码)
【发布时间】:2012-08-02 18:01:26
【问题描述】:

我正在尝试使用 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 是最好的选择吗?

【问题讨论】:

    标签: xpath css-selectors jsoup html-parsing


    【解决方案1】:

    看起来 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
    

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

    【讨论】:

    • 我更改了答案,尝试使用 TagSoup 将您的 XPath 与 XOM 结合使用。
    • 感谢代码,我会给 tagoup 一个机会。 Tagsoup 比 Jsoup 好吗?我对解析很陌生,并且在 7 年后才再次开始用 java 编码,所以请考虑我是新手 :)。我只是试图解析存储在我的机器上的一组 html 文件以提取有用的数据并存储在数据库中。唯一的限制是我不想在 html 中进行 js 或图像调用,因为这些链接不存在并且可能会减慢进程。
    • 我刚刚尝试运行代码,输出为空。当我打印 textElements.size() 时,它是 0。有什么想法吗?
    • 嗯,你用的是什么版本的 XOM 和 TagSoup?
    • 最新的 - xom-1.2.8 和 tagoup-1.2.1
    【解决方案2】:

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

    Element 顶层的文本是 TextNode 对象的一个​​实例。例如,“some text 1”和“some text 3”都是“

    ”下的TextNode对象,“Line 1:”是“”下的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();
    

    此代码查找第一个具有 key="class" 和 value="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..
        }
    }
    

    【讨论】:

      【解决方案3】:

      您的问题我认为是您感兴趣的文本,任何定义标签中只包含一个短语,“some text 2”由&lt;b&gt; &lt;/b&gt; 括起来标签。所以这很容易通过以下方式获得:

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

      返回

      some text 2
      

      其他感兴趣的文本只能定义为 &lt;div class="info"&gt; 标记中的文本,仅此而已。所以我知道的唯一方法是获取这个较大元素所包含的所有文本:

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

      但不幸的是,这会得到 all 该元素所持有的文本:

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

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

      【讨论】:

      • 感谢 doc.select("div.info b").text();正在工作,其他 2 个是主要难题:)。
      猜你喜欢
      • 2019-10-11
      • 2019-04-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-19
      相关资源
      最近更新 更多