2025年のScalaとjsoupによるウェブスクレイピング

PlaywrightとスクレイパーAPIを使用して、動的なPinterestデータを抽出する効率的なテクニックを発見し、高速でスケーラブルなWebスクレイピングを実現します。
5 分読
Web Scraping With Scala blog image

PythonとJavaScriptがスクレイピング業界全体を支配している。パフォーマンスや移植性が必要な場合は、Scalaが強力な代替手段を提供してくれる。Scalaは、コンパイル可能で移植性が高く、強く型付けされた基盤を提供してくれる。

今日はScalaとjsoupを使ったスクレイピングの方法を説明する。Pythonを使ったWebスクレイピングほど頻繁に取り上げられることはありませんが、Scalaは強力な基盤と適切なスクレイピングツールを提供してくれます。

なぜScalaなのか?

PythonやJavaScriptではなく、Scalaを選ぶ理由はいくつかある。

  • パフォーマンス:ScalaはJVM(Java Virtual Machine)にコンパイルされます。コンパイラーは私たちのコードを機械で実行可能なバイトコードに変換します。そのため、Pythonよりも本質的に高速です。
  • 静的型付け:型チェックはさらなる安全性を提供する。よくあるバグの多くは、プログラムが実行される前に発見される。
  • 移植性:ScalaはJVM(Java Virtual Machine)バイトコードにコンパイルされます。JVMバイトコードは、Javaがインストールされていればどこでも実行できる。
  • Javaとの完全な互換性:ScalaのコードでJavaの依存関係を使用できます。これにより、利用できるエコシステムが大幅に広がります。

はじめに

始める前に、Scalaがインストールされていることを確認する必要があります。Ubuntu、macOS、Windows用の手順を以下に示します。

インストールに関する完全なドキュメントはこちらからご覧いただけます。

ウブントゥ

curl -fL https://github.com/coursier/coursier/releases/latest/download/cs-x86_64-pc-linux.gz | gzip -d > cs && chmod +x cs && ./cs setup

マックオス

brew install coursier && coursier setup

ウィンドウズ

Windows用のScalaインストーラをダウンロードする。

スクレーパーの作成

新しいプロジェクトフォルダを作成し、そこにCDを入れる。

mkdir quote-scraper
cd quote-scraper

新しい Scala プロジェクトを初期化します。このコマンドは、新しいフォルダをScalaプロジェクトに変換し、依存関係を保持するためのbuild.sbtファイルを作成します。

sbt new scala/scala3.g8

次に、build.sbtを開く。依存関係としてjsoupを追加する必要がある。ビルドファイルの完成形は以下のようになるはずだ。

val scala3Version = "3.6.3"

lazy val root = project
  .in(file("."))
  .settings(
    name := "quote-scraper",
    version := "0.1.0-SNAPSHOT",

    scalaVersion := scala3Version,

    libraryDependencies += "org.scalameta" %% "munit" % "1.0.0" % Test,

    libraryDependencies += "org.jsoup" % "jsoup" % "1.18.3"
  )

次に、以下のコードをコピーしてMain.scalaファイルに貼り付けます。

import org.jsoup.Jsoup
import scala.jdk.CollectionConverters._

@main def QuotesScraper(): Unit =
  val url = "http://quotes.toscrape.com"

  try
    val document = Jsoup.connect(url).get()
    //find all objects on the page with the quote class
    val quotes = document.select(".quote")

    for quote <- quotes.asScala do
      //find the first object with the class "text" and return its text
      val text = quote.select(".text").text()
      //find the first object with the class "author" and return its text
      val author = quote.select(".author").text()
      println(s"Quote: $text")
      println(s"Author: $author")
      println("-" * 50)
  
  catch case e: Exception => println(s"Error: ${e.getMessage}")

スクレーパーの運転

スクレーパーを実行するには、プロジェクトのルートから以下のコマンドを実行する。

sbt run

以下のような出力が表示されるはずだ。

Quote: “The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”
Author: Albert Einstein
--------------------------------------------------
Quote: “It is our choices, Harry, that show what we truly are, far more than our abilities.”
Author: J.K. Rowling
--------------------------------------------------
Quote: “There are only two ways to live your life. One is as though nothing is a miracle. The other is as though everything is a miracle.”
Author: Albert Einstein
--------------------------------------------------
Quote: “The person, be it gentleman or lady, who has not pleasure in a good novel, must be intolerably stupid.”
Author: Jane Austen
--------------------------------------------------
Quote: “Imperfection is beauty, madness is genius and it's better to be absolutely ridiculous than absolutely boring.”
Author: Marilyn Monroe
--------------------------------------------------
Quote: “Try not to become a man of success. Rather become a man of value.”
Author: Albert Einstein
--------------------------------------------------
Quote: “It is better to be hated for what you are than to be loved for what you are not.”
Author: André Gide
--------------------------------------------------
Quote: “I have not failed. I've just found 10,000 ways that won't work.”
Author: Thomas A. Edison
--------------------------------------------------
Quote: “A woman is like a tea bag; you never know how strong it is until it's in hot water.”
Author: Eleanor Roosevelt
--------------------------------------------------
Quote: “A day without sunshine is like, you know, night.”
Author: Steve Martin
--------------------------------------------------
[success] Total time: 6 s, completed Feb 18, 2025, 8:58:04 PM

jsoupによる選択

セレクタにマッチするすべての要素のリストを返します。Quote Scraperプロジェクトでこれがどのように動作するか見てみましょう。

この行では、document.select(".quote")を使ってquoteという クラスを持つすべてのページ要素を返している。

val quotes = document.select(".quote")

これらのセレクタをより構造的に書くこともできます:element[attribute='some value']。これにより、ページ上のオブジェクトを検索する際に、より強力なフィルタを適用することができます。

以下の行は、同じページオブジェクトを返すことに変わりはないが、より表現力が豊かになっている。

val quotes = document.select("div[class='quote']")

select()の他のインスタンスを見てみましょう。各見積もりにはひとつのテキスト要素とひとつの著者しかないので、select()はひとつのテキストオブジェクトとひとつの著者を返すだけです。もしquote要素に複数のテキストや著者が含まれていれば、select()は各quoteのすべてのテキストと著者を返すでしょう。

//find objects with the class "text" and return their text
val text = quote.select(".text").text()
//find objects with the class "author" and return their text
val author = quote.select(".author").text()

jsoupによる抽出

jsoupでデータを抽出するには、以下の方法がある:

  • text():ページ要素のリストからテキストを抽出します。ウェブサイトから価格をスクレイピングする場合、それらはテキストとしてページに表示されます。
  • attr():単一のページ要素から特定の属性を抽出する。これは、HTMLタグ内にあるデータの一部です。このメソッドは、ウェブサイトからリンクを抽出するためによく使われます。

テキスト()

text()は呼び出した要素のテキストを返す。下の例で2人の著者を見つけるとすると、text()は両方のテキストを抽出し、1つの文字列にまとめます。

//find objects with the class "text" and return their text
val text = quote.select(".text").text()
//find objects with the class "author" and return their text
val author = quote.select(".author").text()

attr()

attr()メソッドはtext() とは動作が異なります。このメソッドは、単一のページ項目から単一の属性を抽出します。

//find link elements with the class "tag" and extract the "href" from the first one
val firstTagLink = quote.select("a[class='tag']").attr("href")

この行を追加すると、出力は次のようになる。

Quote: “The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”
Author: Albert Einstein
First Tag Link: /tag/change/page/1/
--------------------------------------------------
Quote: “It is our choices, Harry, that show what we truly are, far more than our abilities.”
Author: J.K. Rowling
First Tag Link: /tag/abilities/page/1/
--------------------------------------------------
Quote: “There are only two ways to live your life. One is as though nothing is a miracle. The other is as though everything is a miracle.”
Author: Albert Einstein
First Tag Link: /tag/inspirational/page/1/
--------------------------------------------------
Quote: “The person, be it gentleman or lady, who has not pleasure in a good novel, must be intolerably stupid.”
Author: Jane Austen
First Tag Link: /tag/aliteracy/page/1/
--------------------------------------------------
Quote: “Imperfection is beauty, madness is genius and it's better to be absolutely ridiculous than absolutely boring.”
Author: Marilyn Monroe
First Tag Link: /tag/be-yourself/page/1/
--------------------------------------------------
Quote: “Try not to become a man of success. Rather become a man of value.”
Author: Albert Einstein
First Tag Link: /tag/adulthood/page/1/
--------------------------------------------------
Quote: “It is better to be hated for what you are than to be loved for what you are not.”
Author: André Gide
First Tag Link: /tag/life/page/1/
--------------------------------------------------
Quote: “I have not failed. I've just found 10,000 ways that won't work.”
Author: Thomas A. Edison
First Tag Link: /tag/edison/page/1/
--------------------------------------------------
Quote: “A woman is like a tea bag; you never know how strong it is until it's in hot water.”
Author: Eleanor Roosevelt
First Tag Link: /tag/misattributed-eleanor-roosevelt/page/1/
--------------------------------------------------
Quote: “A day without sunshine is like, you know, night.”
Author: Steve Martin
First Tag Link: /tag/humor/page/1/
--------------------------------------------------
[success] Total time: 3 s, completed Feb 18, 2025, 10:29:30 PM

代替ウェブスクレイピングツール

  • スクレイピングブラウザPlaywrightとSeleniumから使用できるプロキシと完全に統合されたリモートブラウザ。
  • ウェブスクレーパーAPI:APIを呼び出すことで、スクレイピングプロセスを自動化します。スクレイパーAPIを呼び出すと、サイトをスクレイピングしてデータを送り返します。
  • コードスクレーパーはありません:スクレイピングしたいサイトとデータをお知らせください。あとはお任せください。
  • データセット私たちのデータセットは、おそらくどの抽出方法よりも簡単です。私たちは何百ものサイトをスクレイピングし、常にデータベースを更新しています。データセットは、分析の準備が整ったクリーンなデータセットを提供します。

結論

WebスクレイピングはScalaを使えばかなり直感的にできる。jsoupを使ってページ要素を選択し、データを抽出する方法を学びました。スクレイピングが苦手な方は、自動化ツールを使ってプロセスをガイドしたり、すぐに使えるデータセットを使ってスクレイピングプロセスを完全に省略することもできます。

今すぐ登録し、無料トライアルを開始しましょう!

クレジットカードは必要ありません