3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

HTMLのパースにjsoupを使って見た

Posted at

HTMLをパースするライブラリ

今回あるサイトのHTTPのレスポンスのHTMLをパースする必要がありました。HTMLは基本はXMLなので色々なパーサがありますが、jsoupを使ったら意外とよかったのでそれを紹介したいと思います。

そもそも・・・なんでHTMLをパースする必要があるのか?

今、開発しているアプリがAndroid端末内でCSVファイルを作ってそれをWebサイトにアップロードする仕組みなんですが、そのWebサイトがWEB APIを提供していなくて、(おそらく、10年以上前に作った、そのまま)普通のブラウザからHTMLでのアップロードのインタフェースしか提供していません。

  1. ログイン。
  2. CSVを添付して送信。この時正常か、エラーになる
  3. 送信結果を過去の履歴も含めて取得
  4. ログアウト

という、一連の流れになっています。こんなの、APIあれば一発なのに・・・と思うのが普通でしょうが。各リクエスト/レスポンスのやり取りで、ブラウザから来る前提なので、結果のレスポンスは全てHTMLです。そのHTMLの中の特定のタグの値を見て正常か、失敗かを判定する必要があります。

jsoupを使って見る

jsoupのホームページ

依存ライブラリの追加

まず、ライブラリの依存性を追加します

build.gradle
dependencies {
    ・・・
    implementation 'org.jsoup:jsoup:1.15.3'
}

パース機能の呼び出し方

パースはString型を引数にorg.jsoup.Jsoup#parseを呼ぶと、org.jsoup.nodes.Document型が返ってきます。簡単ですね。

val doc = Jsoup.parse(response)

このorg.jsoup.nodes.Document型がHTMLの全ての情報がパースされて格納されています。ここなら目的の箇所を探し当てて、値を取得します。この方法ですが、いくつかあります。

  • Document#getElementById(String id)
    HTMLのid="・・・"で探す
  • Document#getElementsByTag(String tag)
    Tagで探す。"table"とか、"input"とか
  • Document#getElementsByClass(String className)
    HTMLのclass="・・・"で探す
  • Document#getElementsByAttribute(String key)
    属性で探す。"href"とか。

この結果にorg.jsoup.select.Elements型が返ってきます。上のいずれの方法で探しても、HTMLの中に複数個所該当する場合があります。

org.jsoup.select.Elementsの中にorg.jsoup.nodes.Elementを複数繰り返しで持っています。

elements.forEach { element ->
    println(element.text())
}

HTMLなので当然階層構造になっているので、org.jsoup.nodes.Elementには子要素が存在します。

val elements =  table.children()

子要素も複数のElementの繰り返しの可能性があるので、org.jsoup.select.Elementsで返ってきます。これらをforEach等で繰り返しながら、目的のElementを探し当てます。

目的のElementを探し当てたら、そのElementのデータを取得します。これも取得するデータによってメソッドが分かれています。

  • Element#attr(String key) 属性
  • Element#attributes() 全ての属性
  • Element#id() id="・・・"
  • Element#className() class="・・・" 複数の場合はスペース区切り
  • Element#classNames() class="・・・" Setで返ってくる
  • Element#text() テキストの部分
  • Element#html() HTMLの形で返ってくる
  • Element#outerHtml() 外側のHTMLが返ってくる
  • Element#data() データ、例えばScriptタグの内側
  • Element#tag() タグが返ってくる。Tag型
  • Element#tagName() タグ名が返ってくる。String型

例えば、探し当てた目的のElementが

<input type="hidden" id="xxxx_key" name="xxxxx_key" value="20221216195944675">

だったら、value="・・・" の部分を取得したければ、

var val = element.text()

と言った感じになります。

joupを使って、直接送信

jsoupは上の例のようにHTMLパーサとして使えますが、HTTPで送信する機能も持っています。ただし、非同期ではなく同期なので、Androidから送信する場合は非同期の部分はcoroutineなり、RxJavaなりで自分で実装する必要があります。

val document = Jsoup.connect("http://www.google.co.jp").get()
println(document.html())

リクエストパラメータを付けることもできます。

val doc = Jsoup.connect("http://example.com")
  .data("query", "Java")
  .userAgent("Mozilla")
  .cookie("auth", "token")
  .timeout(3000)
  .post()
3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?