• 233
    Like
  • 0
    Comment
More than 1 year has passed since last update.

jsoup は Java で HTML の解析・編集を行うためのライブラリ。

URL を指定すれば実際の Web ページを解析のインプットに指定でき、タグの検索には CSS セレクタが使えるので、 Web スクレイピングをしたい時にとても便利。

導入

Maven のセントラルリポジトリに jar がある。

build.gradle
dependencies {
    compile 'org.jsoup:jsoup:1.7.3'
}

解析する HTML の指定

URL を指定して実際の Web ページをインプットにする

package sample.jsoup;

import java.io.IOException;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

public class Main {

    public static void main(String[] args) throws IOException {
        Document document = Jsoup.connect("http://www.google.co.jp").get();
        System.out.println(document.html());
    }
}
実行結果
<!DOCTYPE html>
<html itemscope="" itemtype="http://schema.org/WebPage">
 <head>
  <meta content="世界中のあらゆる情報を検索するためのツールを提供しています。さまざまな検索機能を活用して、お探しの情報を見つけてください。" name="description" />
  <meta content="noodp" name="robots" />
  <meta itemprop="image" content="/images/google_favicon_128.png" />
  <title>Google</title>
(以下略)

Jsoup.connect("<URL>").get() で、指定した URL に GET メソッドでアクセスし、結果をパースした Document オブジェクトを取得できる。

あとは、この Document オブジェクトから必要なタグを検索する。

※HTTP のメソッドは GET と POST しか無いようなので、 RESTful API のクライアントとしては利用できないっぽい。(Connection.Method (jsoup 1.7.4-SNAPSHOT API)

リクエストパラメータを設定する

package sample.jsoup;

import java.io.IOException;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class Main {

    public static void main(String[] args) throws IOException {
        Document document = Jsoup.connect("http://qiita.com/search")
                                 .data("q", "java")
                                 .get();

        Elements elements = document.select(".brand, .page-title, .stats");

        for (Element element : elements) {
            System.out.println(element.text());
        }
    }
}
実行結果
Qiita - プログラマの技術情報共有サービス
javaの検索結果
856投稿 • 7163フォロワー

data(String, String) メソッドでリクエストパラメータを設定できる。
URL エンコードは内部でしてくれるので、全角文字もそのまま指定できる。

HTML 形式の文字列をインプットにする

package sample.jsoup;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

public class Main {

    public static void main(String[] args) {
        String html = "<div><span>hoge</span><p>fuga</p></div>";

        Document document = Jsoup.parse(html);
        System.out.println(document.html());
    }
}
実行結果
<html>
 <head></head>
 <body>
  <div>
   <span>hoge</span>
   <p>fuga</p>
  </div>
 </body>
</html>

ローカルのファイルをインプットにする

input.html
<html>
  <head>
    <title>test page</title>
  </head>
  <body>
    <h1>テストページです。</h1>
  </body>
</html>
package sample.jsoup;

import java.io.File;
import java.io.IOException;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

public class Main {

    public static void main(String[] args) throws IOException {
        Document document = Jsoup.parse(new File("input.html"), "UTF-8");

        System.out.println(document.html());
    }
}
実行結果
<html>
 <head> 
  <title>test page</title> 
 </head> 
 <body> 
  <h1>テストページです。</h1>  
 </body>
</html>

テキスト・ HTML を取得する

input.html
<div id="hoge">
  <h1>Hoge</h1>
  <ul id="fuga">
    <li>Fuga</li>
    <li id="piyo">Piyo</li>
  </ul>
</div>
package sample.jsoup;

import java.io.File;
import java.io.IOException;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;

public class Main {

    public static void main(String[] args) throws IOException {
        Document document = Jsoup.parse(new File("input.html"), "UTF-8");

        Element hoge = document.getElementById("hoge");
        System.out.println("[hoge.html()]\r\n" + hoge.html() + "\r\n");

        Element fuga = document.getElementById("fuga");
        System.out.println("[fuga.text()]\r\n" + fuga.text() + "\r\n");

        Element piyo = document.getElementById("piyo");
        System.out.println("[piyo.outerHtml()]\r\n" + piyo.outerHtml() + "\r\n");
    }
}
実行結果
[hoge.html()]
<h1>Hoge</h1> 
<ul id="fuga"> 
 <li>Fuga</li> 
 <li id="piyo">Piyo</li> 
</ul>

[fuga.text()]
Fuga Piyo

[piyo.outerHtml()]
<li id="piyo">Piyo</li>
  • html() メソッドで、そのタグの中身を HTML 形式の文字列で取得できる。
  • text() メソッドで、そのタグの中身のうち、テキスト部分だけを抽出した文字列を取得できる。
  • outerHtml() メソッドで、そのタグ自身を HTML 形式の文字列で取得できる。

タグの検索

ID 指定で検索する

input.html
<h1 id="hoge">Hoge</h1>
<h1 id="fuga">Fuga</h1>
<h1 id="piyo">Piyo</h1>
package sample.jsoup;

import java.io.File;
import java.io.IOException;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;

public class Main {

    public static void main(String[] args) throws IOException {
        Document document = Jsoup.parse(new File("input.html"), "UTF-8");

        Element element = document.getElementById("hoge");
        System.out.println(element.outerHtml());
    }
}
実行結果
<h1 id="hoge">Hoge</h1>

その他検索用メソッド

ID 以外にも、 class や属性指定、兄弟タグ、親タグ、タグ内のテキスト、など様々な検索用メソッドが用意されている。

とりあえず、 Element クラスの API はざっと目を通しておいた方がよさげ。

Element (jsoup 1.7.4-SNAPSHOT API)

CSS セレクタを使って検索する

input.html
<div id="hoge">
  <ul>
    <li class="error">hoge</li>
    <li class="success">fuga</li>
    <li class="error">piyo</li>
  </ul>

  <span class="error">ERROR</span>
</div>

<span id="fuga">Fuga</span>
package sample.jsoup;

import java.io.File;
import java.io.IOException;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class Main {

    public static void main(String[] args) throws IOException {
        Document document = Jsoup.parse(new File("input.html"), "UTF-8");

        Elements elements = document.select("#hoge ul .error");
        for (Element element : elements) {
            System.out.println(element.outerHtml());
        }
    }
}
実行結果
<li class="error">hoge</li>
<li class="error">piyo</li>

select(String cssQuery) メソッドで、 CSS セレクタを使用した検索が可能。

使用できる CSS セレクタについては、以下の Selector クラスの API ドキュメントで説明されている。

Selector (jsoup 1.7.4-SNAPSHOT API)

ほとんどの場合普通の CSS セレクタと同じように使えるが、 E[foo="bar"] タイプのセレクタを使用するときだけ、以下のような違いがある。

input.html
<input id="hoge" name="HOGE" />
<input id="fuga" name="FUGA" />
package sample.jsoup;

import java.io.File;
import java.io.IOException;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;

public class Main {

    public static void main(String[] args) throws IOException {
        Document document = Jsoup.parse(new File("input.html"), "UTF-8");

        Elements elements = document.select("[name=HOGE]");
        System.out.println(elements.outerHtml());
    }
}
実行結果
<input id="hoge" name="HOGE" />

属性の値は ダブルクォーテーション " で括ってはいけない
"[name=\\"HOGE\\"]" ではなく、 "[name=HOGE]" が正)

おそらく、 Java のコード上ではダブルクォーテーションにエスケープが必要になるので、より簡潔に書けるようにこのような違いがあると思われる。

プロキシ環境下で使う

認証が不要な場合

package jsoup;

import java.io.IOException;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

public class Main {

    public static void main(String[] args) throws IOException {
        System.setProperty("http.proxyHost", "プロキシホスト名");
        System.setProperty("http.proxyPort", "プロキシポート");

        Document doc = Jsoup.connect("http://www.google.co.jp").get();
    }
}

認証が不要な場合は簡単で、システムプロパティにプロキシのホストとポートを設定してあげればいい。

認証が必要な場合

package jsoup;

import java.io.IOException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.net.Authenticator;
import java.net.PasswordAuthentication;

public class Main {

    public static void main(String[] args) throws IOException {
        // プロキシの設定(ホストとポート)
        System.setProperty("http.proxyHost", "プロキシホスト名");
        System.setProperty("http.proxyPort", "プロキシポート");

        // 認証情報の設定
        Authenticator.setDefault(new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication("ユーザー名", "パスワード".toCharArray());
            }
        });

        // Jsoup 取得し
        Document doc = Jsoup.connect("http://www.google.co.jp").get();
    }
}

参考