Резюме использования Jsoup

задняя часть

1 Обзор

Раньше для парсинга HTML-страниц я обычно использовал HTMLParser, подробнее см.Обучающая серия HTMLParser — сводка обучения, но этот проект перестал обновляться. Теперь лучшим средством для разбора HTML является Jsoup. Эта статья суммирует использование Jsoup.
Основная функция Jsoup состоит из трех частей:

  1. Создавайте Documentn из строк, веб-страниц, локальных файлов и т. д.
  2. После создания документа получите элемент в соответствии с Dom и css или синтаксисом селектора, подобным jquery, а затем получите атрибуты узла, текст, html из элементов.
  3. Работа с элементом, включая значение HTML, значение содержимого узла и значение настройки атрибутов узла.

Каждый из следующих разделов продемонстрирует вышеперечисленные три пункта один за другим.

Конфигурация POM.xml

<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.10.2</version>
</dependency>

2. Создать документ

JSOUP генерирует документы по-разному, в основном тремя способами:
1. Строка
2. Веб-страница
3. Локальные файлы

2.1 Создание документа из строки

Ключевой метод: код Jsoup.parse

    /**
     * 将字符串转化为Document
     * 
     * html: https://jsoup.org/cookbook/input/parse-document-from-string
     */
    public void parseDocumentFromString() {
        String html = "<html><head><title>First parse</title></head>"
                + "<body><p>Parsed HTML into a doc.</p></body></html>";
        Document doc = Jsoup.parse(html);
        logger.info("parseDocumentFromString content={}", doc);
    }

выходной результат

parseDocumentFromString content=<html>
 <head>
  <title>First parse</title>
 </head>
 <body>
  <p>Parsed HTML into a doc.</p>
 </body>
</html>

2.2 Создание документов из веб-страниц

Ключевой метод: код Jsoup.connect

/**
     * 从网络上加载网页并转化为Document
     * 
     * html: https://jsoup.org/cookbook/input/load-document-from-url
     */
    public void loadDocumentFromURL() {
        Document doc;
        try {
            doc = Jsoup.connect("https://www.baidu.com/").get();
            // 从document中获取title值
            String title = doc.title();
            logger.info("LoadDocumentFromURL title={}", title);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

Вывод: поскольку страница слишком велика, содержимое страницы здесь не печатается, выводится только значение заголовка страницы.

LoadDocumentFromURL title=百度一下,你就知道

2.3 Создать документ из локального файла

Локальный тестовый файл: loadDocumentFromFile.html

<html>
<body>
.. body
</body>
</html>

Метод ключа кода: Jsoup.parse

    /**
     * 从本地加载文件并转化为Document
     * 
     * html: https://jsoup.org/cookbook/input/load-document-from-file
     */
    public void loadDocumentFromFile() {
        URL fileUrl = LoadParseDocument.class.getResource("/com/hry/tool/jsoup/doc/loadDocumentFromFile.html");
        File input = new File(fileUrl.getFile());
        try {
            /* The baseUri parameter is used by the parser to resolve relative URLs in the document 
             * before a <base href> element is found. If that's not a concern for you, 
             * you can pass an empty string instead.
             */
            Document doc = Jsoup.parse(input, "UTF-8", "https://www.baidu.com/");
            logger.info("LoadDocumentFromFile content={}", doc);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

вывод

LoadDocumentFromFile content=<html>
 <head></head>
 <body>
   .. body  
 </body>
</html>

3. Получить атрибуты элемента и узла, текст, html

Документ был сгенерирован в предыдущем разделе, и с документом можно работать ниже.Основной единицей работы является Элемент.Ниже описано, как выбрать элемент и получить содержимое элемента.

Основное содержимое, содержащееся локально, получает информацию об узле:
1. Есть два способа получить элемент: DOM, b с помощью css или синтаксиса селектора, такого как jquery.
2. Получить атрибуты узла, текст, html

3.1 Получить элемент

код DOM

    /**
     *  调用document的类似DOM的方法获取Element
     * 
     * html: https://jsoup.org/cookbook/extracting-data/dom-navigation
     * @throws IOException
     */
    public void extractDataByDOM() throws IOException{
        Document doc = Jsoup.connect("https://www.baidu.com/").get();
        Element lg = doc.getElementById("lg");
        logger.info("getElementById lg = {}", lg);
        Elements links = doc.getElementsByTag("a");
        for (Element link : links) {
          String linkHref = link.attr("href");
          String linkText = link.text();
          logger.info("linkHref={}, linkText={}",linkHref, linkText);
        }
    }

вывод

输出内容过多,略

b через css или jquery-подобный синтаксис селектора
Нажмите, чтобы увидеть весь синтаксис селектора
Тестовый файл: extractDataByCSSOrJqueryLikeSelectorSyntax.html

<html>
<header>
    <h3 class="r">h3<a href="/test/h3"></a></b></h3>
</header>
<body>
.. body
<div>
    <a href="/test" >test</a>
    <a href="/test2" >test2</a>
</div>
<div>
    <img alt="test" src="/image/a.png" />
    <img alt="test2" src="/image/b.png" />
</div>
<div>
    <div class="masthead">masthead</div>
</div>
</body>
</html>

код

    /**
     * 通过css或类似jquery的selector语法
     * 
     * html: https://jsoup.org/cookbook/extracting-data/selector-syntax
     * @throws IOException 
     */
    public void extractDataByCSSOrJqueryLikeSelectorSyntax() throws IOException{
        URL fileUrl = LoadParseDocument.class.getResource("/com/hry/tool/jsoup/extractingdata/extractDataByCSSOrJqueryLikeSelectorSyntax.html");
        File input = new File(fileUrl.getFile());
        Document doc = Jsoup.parse(input, "UTF-8", "http://example.com/");

        // 获取所有的a节点
        Elements links = doc.select("a[href]"); 
        logger.info("links = {}", links);

        // 获取img的src以.png结果结尾
        Elements pngs = doc.select("img[src$=.png]");
        logger.info("pngs = {}", pngs);

        // 获取class=masthead的div节点
        Element masthead = doc.select("div.masthead").first();
        logger.info("masthead = {}", masthead);

        // 获取class=r的h3节点下面的a节点
        Elements resultLinks = doc.select("h3.r > a"); // direct a after h3
        logger.info("resultLinks = {}", resultLinks);
    }

Вывод: часть содержимого журнала опущена для удобства просмотра.

links = <a href="/test/h3"></a>
<a href="/test">test</a>
<a href="/test2">test2</a>

pngs = <img alt="test" src="/image/a.png">
<img alt="test2" src="/image/b.png">

masthead = <div class="masthead">masthead</div>

3.2 Получить атрибуты узла, текст, html

код

    /**
     * 获取节点属性、文本、html
     * 
     * html: https://jsoup.org/cookbook/extracting-data/attributes-text-html
     */
    public void extractAttributesTextAndHTML(){
        String html = "<p>An <a href='/abc'><b>example</b></a> link.</p>";
        // 指定baseUri的值,在使用abs:attr会使用到
        Document doc = Jsoup.parse(html, "http://example.com/");
        Element link = doc.select("a").first();

        /**
         * text获取所有子节点的文本并组合在一一起
         * 如:HTML <p>Hello <b>there</b> now! </p>, --》 调用p.text() --》输出: "Hello there now!"
         */
        String text = doc.body().text(); // "An example link"
        logger.info("text={}", text);

        // 获取link的href属性值
        String linkHref = link.attr("href"); // /abc
        // 在href前面加上abs,会在现有的href(如/abc)的前面别上Jsoup.parse(html, "http://example.com/")里指定的baseUri值
        String absLinkHref = link.attr("abs:href"); // http://example.com/abc
        logger.info("linkHref={}, absLinkHref={}",linkHref, absLinkHref);

        // 获取link里所有字节点的内容组合在一起
        String linkText = link.text(); // "example""
        logger.info("linkText={}", linkText);

        // 获取本节点所有HTML文本信息
        String linkOuterH = link.outerHtml(); // "<a href="http://example.com"><b>example</b></a>"
        logger.info("linkOuterH={}", linkOuterH);

        // 获取本节点字节点的html文本信息
        String linkInnerH = link.html(); // "<b>example</b>"
        logger.info("linkInnerH={}", linkInnerH);
    }
}

Вывод: опустить часть лога для удобства чтения

text=An example link.

linkHref=/abc, absLinkHref=http://example.com/abc

linkText=example

linkOuterH=<a href="/abc"><b>example</b></a>

linkInnerH=<b>example</b>

4. Установите значение узла

Установите значение узла, в основном, следующими способами:
1. Установите значение узла HTML
2. Установите значение содержимого узла
3. Установите значение атрибута узла

общедоступный тестовый файл

<html>
<body>
.. body
<div></div>
<span>One</span>
</body>
</html>

Сгенерировать текст документа из локального файла

    private Document getDoucment() throws IOException{
        URL fileUrl = LoadParseDocument.class.getResource("/com/hry/tool/jsoup/modifyingdata/modifyingData.html");
        File input = new File(fileUrl.getFile());
        Document doc = Jsoup.parse(input, "UTF-8", "http://example.com/");
        return doc;
    }

4.1 Установка значения узла HTML

Установите html-содержимое узла:

  1. Element.html: заменить старые значения новым HTML
  2. Element.prepend: добавить новый html на передний план внутри указанного узла
  3. Element.append: добавить новый html к последнему внутри указанного узла
  4. Element.wrap: инкапсулировать указанный узел в самый внутренний html

    Код:

/**
 * 设置节点的html内容:
 *  1. html:使用新的HTML替换旧的值
 *  2. prepend:将新html添加到指定节点内部的最前面
 *  3. append:将新html添加到指定节点内部的最后面
 *  4. wrap:将指定节点封装到html最里面
 *  
 *  html: https://jsoup.org/cookbook/modifying-data/set-html
 * @throws IOException
 */
public void setHTMLofAnElement() throws IOException{
    Document doc = getDoucment();

    // 获取div节点
    Element div = doc.select("div").first(); // <div></div>
    // 使用新的HTML替换div旧的html值
    div.html("<p>lorem ipsum</p>"); // <div><p>lorem ipsum</p></div>
    // 将新html添加到div内部的最前面
    div.prepend("<p>First</p>"); // <div><p>First</p><p>lorem ipsum</p></div>
    // 将新html添加到div内部的最后面
    div.append("<p>Last</p>");  // now: <div><p>First</p><p>lorem ipsum</p><p>Last</p></div>
    logger.info("now div={}", div);

    Element span = doc.select("span").first(); // <span>One</span>
    // 将span节点封装到html最里面 --》 <li><a href="http://example.com"><span>One</span></a></li>
    span.wrap("<li><a href='http://example.com/'></a></li>");

    logger.info("doc={}", doc);
}

вывод:

2017-06-07 22:35:48.271  INFO 6644 --- [           main] c.h.t.jsoup.modifyingdata.ModifyingData  : now div=<div>
 <p>First</p>
 <p>lorem ipsum</p>
 <p>Last</p>
</div>
2017-06-07 22:35:48.271  INFO 6644 --- [           main] c.h.t.jsoup.modifyingdata.ModifyingData  : doc=<html>
 <head></head>
 <body>
   .. body 
  <div>
   <p>First</p>
   <p>lorem ipsum</p>
   <p>Last</p>
  </div> 
  <li><a href="http://example.com/"><span>One</span></a></li>  
 </body>
</html>

4.2 Установка значения содержимого узла

Изменить содержимое узла
1. Element.text: полностью заменить содержимое
2. Element.prepend: добавить содержимое в начало содержимого узла.
3. Element.append: добавить содержимое в конец содержимого узла

    /**
     * 修改节点内容
     *  1. 完全替换内容
     *  2. 在节点的内容最前面加内容
     *  3. 在节点的内容最后面加内容
     * 
     * html: https://jsoup.org/cookbook/modifying-data/set-text
     * @throws IOException
     */
    public void setTextContentofAnElement() throws IOException{
        Document doc = getDoucment();

        Element div = doc.select("div").first(); // <div></div>
        // 替换div里的内容 输出 -->  <div>five four</div>
        div.text("five four"); 
        logger.info("text = {}", div);

        // 在div的内容最前面加内容  输出 --> <div> First five four</div>
        div.prepend("First ");
        logger.info("prepend = {}", div);

        // 在div的内容最后面加内容  输出 --> <div>First five four Last</div>
        div.append(" Last");
        logger.info("append = {}", div);
    }

вывод:

2017-06-07 22:39:20.289  INFO 11476 --- [           main] c.h.t.jsoup.modifyingdata.ModifyingData  : text = <div>
 five four
</div>
2017-06-07 22:39:20.289  INFO 11476 --- [           main] c.h.t.jsoup.modifyingdata.ModifyingData  : prepend = <div>
 First five four
</div>
2017-06-07 22:39:20.289  INFO 11476 --- [           main] c.h.t.jsoup.modifyingdata.ModifyingData  : append = <div>
 First five four Last
</div>

4.3 Установка значения атрибута узла

Установить свойства и значения классов узлов

  1. element.attr: установить атрибуты
  2. element.addClass: установить класс

код

    /**
     * 设置节点的属性和class值
     *  1. 设置属性
     *  2. 设置class
     * 
     * html:https://jsoup.org/cookbook/modifying-data/set-attributes
     * @throws IOException
     */
    public void setAttributeValues() throws IOException{
        Document doc = getDoucment();
        Element div = doc.select("div").first(); 
        // 在div节点上增加属性和其值:输出 --> <div title="nofollow"></div>
        div.attr("title", "nofollow");
        logger.info("div={}", div);
        // 在div节点上增加class和其值:输出 --> <div title="nofollow" class="round-box"></div>
        div.addClass("round-box");
        logger.info("div={}", div);
    }

вывод

2017-06-07 22:39:44.466  INFO 11280 --- [           main] c.h.t.jsoup.modifyingdata.ModifyingData  : div=<div title="nofollow"></div>
2017-06-07 22:39:44.466  INFO 11280 --- [           main] c.h.t.jsoup.modifyingdata.ModifyingData  : div=<div title="nofollow" class="round-box"></div>

5. Код ##

Подробный код см.код гитхаба