スクレイピングとWebサーバー実行のサンプルコードを、17のプログラミング言語で紹介します。
やることはどの言語でも同じで、「Webサーバーを起動して、Wikipediaのページから取得したプログラミング言語一覧を表示する」だけの小さなプログラムです。タブで言語を切り替えると、ページ内のすべてのコード例が選んだ言語に切り替わります。同じ処理を書いたときの、言語ごとの書き味の違いをぜひ見比べてみてください。
まずは言語ごとに開発環境とプロジェクトを用意します。
# プロジェクトのディレクトリ作成
$ mkdir scraping_server
$ cd scraping_server
# パッケージの追加
$ pip install flask requests beautifulsoup4
インストールしている外部パッケージは以下のものです。
# プロジェクトのディレクトリ作成
$ mkdir scraping-server
$ cd scraping-server
# パッケージのインストール
$ npm init
$ npm install express axios cheerio
インストールしている外部パッケージは以下の3つです。
# プロジェクトのディレクトリ作成
$ mkdir scraping-server
$ cd scraping-server
# パッケージのインストール
$ npm init
$ npm install express axios cheerio
# TypeScriptと型定義ファイルのインストール
$ npm install -D typescript ts-node
$ npm install -D @types/node @types/express @types/axios @types/cheerio
インストールしている外部パッケージは以下の3つです。
プロジェクトのディレクトリ内に tsconfig.json というファイルを作成します。
tsconfig.json はTypeScriptでコンパイルする際の設定を書き込んでおくファイルです。
ファイル内に以下のように記述します。
{
"compilerOptions": {
"types": ["node"],
"esModuleInterop": true
}
}
# Javaの開発環境構築
$ brew install --cask temurin
$ brew install gradle
# プロジェクトのディレクトリ作成
$ mkdir scraping_server
$ cd scraping_server
# プロジェクトの初期化
$ gradle init --type java-application
$ gradle init --type java-application のコマンドが実行完了したら、以下のファイルが作成されていることを確認してください。この2つのファイルを編集して開発を進めていきます。
app/build.gradle ファイルの dependencies { … } の中に、以下の行を追加してください。
implementation 'com.sparkjava:spark-core:2.2'
implementation 'org.jsoup:jsoup:1.11.1'
implementation 'com.google.code.gson:gson:2.4'
インストールしている外部パッケージは以下のものです。
# 開発環境構築
$ brew install --cask dotnet-sdk
# プロジェクトのディレクトリ作成
$ dotnet new web -o ScrapingServer
$ cd ScrapingServer
# パッケージの追加
$ dotnet add package AngleSharp
$ dotnet new web -o ScrapingServer のコマンドが実行完了後、以下のファイルが存在することを確認してください。
インストールしている外部パッケージは以下のものです。
# 開発環境構築
$ brew install go
# プロジェクトのディレクトリ作成
$ mkdir scraping-server
$ cd scraping-server
$ go mod init scraping-server
# 外部パッケージのインストール
$ go get github.com/gin-gonic/gin
$ go get github.com/PuerkitoBio/goquery
インストールしている外部パッケージは以下のものです。
# Rustの開発環境構築
$ brew install rustup-init
$ rustup-init
$ exec $SHELL -l
# プロジェクトのディレクトリ作成
$ cargo new scraping_server
$ cd scraping_server
# 外部パッケージのインストール
$ cargo add actix-web reqwest scraper serde_json
インストールしている外部パッケージは以下のものです。
# 開発環境構築
$ brew install --cask temurin
$ brew install gradle
$ brew install kotlin
# プロジェクトのディレクトリ作成
$ mkdir scraping_server
$ cd scraping_server
# プロジェクトの初期化
$ gradle init --type kotlin-application
$ gradle init --type kotlin-application のコマンドが実行完了したら、以下のファイルが作成されていることを確認してください。この2つのファイルを編集して開発を進めていきます。
app/build.gradle.kts ファイルの dependencies { … } の中に、以下の行を追加してください。
implementation("com.sparkjava:spark-kotlin:1.0.0-alpha")
implementation("it.skrape:skrapeit:+")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2")
インストールしている外部パッケージは以下のものです。
以下のコマンドを実行してプロジェクトを作成します。
# プロジェクトのディレクトリ作成
$ mkdir ScrapingServer
$ cd ScrapingServer
# プロジェクトの開発環境構築
$ swift package init --type executable
$ swift package init --type executable のコマンドが実行完了後、以下のファイルが存在することを確認してください。
Package.swift の内容を以下のように書き替えます。
// swift-tools-version: 5.7
import PackageDescription
let package = Package(
name: "ScrapingServer",
platforms: [
// 対応するプラットフォームをここに追加
],
dependencies: [
// 必要な依存関係をここに追加
],
targets: [
.target(
name: "ScrapingServer",
dependencies: []
),
.testTarget(
name: "ScrapingServerTests",
dependencies: ["ScrapingServer"]
)
]
)
インストールしている外部パッケージは以下のものです。
# パッケージマネージャーのインストール
$ gem install bundler
# プロジェクトの作成
$ mkdir scraping_server
$ cd scraping_server
$ bundler init
# パッケージの追加
$ bundle add sinatra mechanize
インストールしている外部パッケージは以下のものです。
# プロジェクトのディレクトリ作成
$ mkdir scraping-server
$ cd scraping-server
# composer (PHPのパッケージマネージャー)のインストール
$ brew install composer
# パッケージのインストール
$ composer init
$ composer require slim/slim:"3.*"
$ composer require guzzlehttp/guzzle
$ composer require paquettg/php-html-parser
インストールしている外部パッケージは以下の3つです。
# C++の開発環境構築
$ brew reinstall gcc
$ brew install cmake
$ sudo pip install conan
# プロジェクトのディレクトリ作成
$ mkdir scraping-server
$ cd scraping-server
パッケージマネージャーの conan を使うため、プロジェクトのディレクトリ配下に以下のファイルを追加します。
cmake_minimum_required(VERSION 2.8.12)
project(ScrapingServer)
add_definitions("-std=c++11")
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
add_executable(main main.cpp)
target_link_libraries(main ${CONAN_LIBS})
crowcpp-crow/1.0+3
cpr/1.9.0
gumbo-parser/0.10.1
nlohmann_json/3.11.2
cmake
CMakeLists.txt と conanfile.txt のファイルを設置したら、以下コマンドでパッケージをインストールします。
$ conan install . --build=missing
インストールしている外部パッケージは以下のものです。
以下のコマンドを実行してプロジェクトを作成します。
# 環境構築
$ brew install dart
# プロジェクトのディレクトリ作成
$ dart create scraping_server
$ cd scraping_server
# 外部ライブラリの追加
$ dart pub add shelf http html
インストールしている外部パッケージは以下のものです。
# 開発環境構築
$ brew install --cask temurin
$ brew install gradle
$ brew install scala
# プロジェクトのディレクトリ作成
$ mkdir scraping_server
$ cd scraping_server
# プロジェクトの初期化
$ gradle init --type scala-application
$ gradle init --type scala-application のコマンドが実行完了したら、以下のファイルが作成されていることを確認してください。この2つのファイルを編集して開発を進めていきます。
app/build.gradle ファイルの dependencies { … } の中に、以下の行を追加してください。
implementation 'org.scalatra:scalatra_2.13:2.7.1'
implementation 'org.eclipse.jetty:jetty-webapp:9.4.12.v20180830'
implementation 'javax.servlet:javax.servlet-api:4.0.1'
implementation 'net.ruippeixotog:scala-scraper_2.13:2.2.0'
implementation 'com.google.code.gson:gson:2.4'
インストールしている外部パッケージは以下のものです。
# 開発環境構築
$ brew install erlang
$ brew install elixir
# パッケージマネージャーのインストール
$ mix local.hex
# プロジェクトのディレクトリ作成
$ mix new scraping_server
$ cd scraping_server
$ mix new scraping_server のコマンド実行後に作成された mix.exs ファイルを開きます。
外部パッケージのインストールのために、ファイル内の defp deps do 〜 end の箇所を以下のように記述します。
defp deps do
[
{:plug_cowboy, "~> 2.5"}, # Webサーバー フレームワーク
{:httpoison, "~> 1.8"}, # HTTPリクエスト用
{:floki, "~> 0.30.0"}, # HTMLパース用
{:poison, "~> 5.0"} # JSONライブラリ
]
end
ファイルに上記を記述したら、以下コマンドを実行してパッケージをインストールします。
$ mix deps.get
インストールしている外部パッケージは以下のものです。
# Haskellの開発環境構築
$ brew install stack
$ stack setup
# プロジェクトの作成
$ stack new scraping-server
$ cd scraping-server
package.yaml ファイルの dependencies: の部分を以下のように記述します。
dependencies:
- base >= 4.7 && < 5
- warp
- wai
- http-types
- xml-conduit
- html-conduit
- http-conduit
- aeson
- text
インストールしている外部パッケージは以下のものです。
# 開発環境構築
$ brew install opam
$ opam init # コマンド実行時の質問にはすべて「y」を書いてEnter
$ opam install dune
# プロジェクトのディレクトリ作成
$ dune init proj scraping_server
$ cd scraping_server
# 外部パッケージのインストール
$ opam install opium cohttp-lwt-unix cohttp-async tls lambdasoup
$ eval $(opam env)
$ dune init proj scraping_server を実行後、以下のファイルが作られていることを確認してください。
scraping_server/bin/dune のファイル内の libraries の箇所に、利用する外部パッケージを記述します。以下の内容になるように追記しましょう。
(executable
(public_name scraping_server)
(name main)
(libraries
scraping_server
opium
cohttp-lwt-unix
cohttp-async
tls
lambdasoup))
インストールしている外部パッケージは以下のものです。
まずは「test」という文字を返すだけの、最小のWebサーバーを起動してみます。
まず、main.py を以下の内容で作成します。
from flask import Flask
app = Flask(__name__)
@app.route("/")
def handler():
return "test"
if __name__ == "__main__":
app.run(port=3000)
その後、以下コマンドを実行して http://localhost:3000 にブラウザでアクセスすると、”test” の文字が表示されます。
$ python main.py
これで、Webサーバーの起動ができました。
まず以下の内容で main.js を作成します。
const express = require("express");
const app = express();
app.get("/", async function (req, res, next) {
res.json("test");
});
app.listen(3000);
その後、以下コマンドを実行して http://localhost:3000 にブラウザでアクセスすると、”test” の文字が表示されます。
$ node main.js
これで、Webサーバーの起動ができました。
以下の内容で main.ts を作成します。
import express from "express";
const app: express.Express = express();
app.get(
"/",
async function (
req: express.Request,
res: express.Response,
next: express.NextFunction,
) {
res.json("test");
},
);
app.listen(3000);
その後、以下コマンドを実行して http://localhost:3000 にブラウザでアクセスすると、”test” の文字が表示されます。
$ npx ts-node main.ts
これで、Webサーバーの起動ができました。
App.java を以下の内容に変更します。
package scraping_server;
import static spark.Spark.*;
public class App {
public static void main(String[] args) {
port(3000);
get("/", (req, res) -> "test");
}
}
その後、以下コマンドを実行してから http://localhost:3000 にブラウザでアクセスすると、”test” の文字が表示されます。
$ gradle run
これで、Webサーバーの起動ができました。
Program.cs を以下の内容に書き替えてみましょう。
static string Handler()
{
return "test";
}
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", Handler);
app.Run("http://localhost:3000");
その後、以下コマンドを実行して http://localhost:3000 にブラウザでアクセスすると、”test” の文字が表示されます。
$ dotnet run
これで、Webサーバーの起動ができました。
まず以下の内容で main.go を作成します。
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.GET("/", func(c *gin.Context) {
c.JSON(http.StatusOK, "test")
})
router.Run(":3000")
}
その後、以下コマンドを実行して http://localhost:3000 にブラウザでアクセスすると、”test” の文字が表示されます。
$ go run main.go
これで、Webサーバーの起動ができました。
まず、 scraping_server/src/main.rs を以下の内容に書き換えます。
use actix_web::{get, App, HttpResponse, HttpServer, Responder};
#[get("/")]
async fn handler() -> impl Responder {
HttpResponse::Ok().body("test")
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(handler)
})
.bind(("127.0.0.1", 3000))?
.run()
.await
}
その後、以下コマンドを実行して http://localhost:3000 にブラウザでアクセスすると、”test” の文字が表示されます。
# プログラムのビルドと実行
$ cargo run
これで、Webサーバーの起動ができました。
App.kt を以下の内容に変更します。
package scraping_server
import spark.kotlin.*
fun main(args: Array<String>) {
val http: Http = ignite().port(3000)
http.get("/") {
"test"
}
}
その後、以下コマンドを実行してから http://localhost:3000 にブラウザでアクセスすると、”test” の文字が表示されます。
$ gradle run
これで、Webサーバーの起動ができました。
ScrapingServer.swift を以下の内容に変更します。
import Vapor
@main
public struct ScrapingServer {
public static func main() {
do {
let app = try Application(.detect())
app.get("") { request -> EventLoopFuture<String> in
let promise = request.eventLoop.makePromise(of: String.self)
promise.completeWithTask {
return "test"
}
return promise.futureResult
}
app.http.server.configuration.port = 3000
try app.run()
} catch {
print(error.localizedDescription)
}
}
}
その後、以下コマンドを実行して http://localhost:3000 にブラウザでアクセスすると、”test” の文字が表示されます。
$ swift run
これで、Webサーバーの起動ができました。
※ 「error: no such module ‘PackageDescription’」というエラーが発生した場合
→ Xcodeを開いて、Preferences > Locations > Command Line Tools: に Xcode が選択されている状態にしましょう。
まず、main.rb を以下の内容で作成します。
require "sinatra"
set :port, 3000
get "/" do
"test"
end
その後、以下コマンドを実行して http://localhost:3000 にブラウザでアクセスすると、”test” の文字が表示されます。
$ ruby main.rb
これで、Webサーバーの起動ができました。
まず以下の内容で index.php を作成します。
<?php
require __DIR__ . '/vendor/autoload.php';
$app = new \Slim\App();
$app->get('/', function ($request, $response) {
$response->getBody()->write("test");
return $response;
});
$app->run();
その後、以下コマンドを実行して http://localhost:3000 にブラウザでアクセスすると、”test” の文字が表示されます。
$ php -S localhost:3000
これで、Webサーバーの起動ができました。
まず以下の内容で main.cpp を作成します。
#include "crow.h"
int main() {
crow::SimpleApp app;
CROW_ROUTE(app, "/")([](){
return "test";
});
app.port(3000).multithreaded().run();
}
その後、以下コマンドを実行して http://localhost:3000 にブラウザでアクセスすると、”test” の文字が表示されます。
# プログラムのビルド
$ cmake .
$ make
# プログラムの実行
$ ./bin/main
これで、Webサーバーの起動ができました。
ちなみにビルドと実行は以下のように続けて1行で書くこともできます。
$ cmake . && make && ./bin/main
scraping_server/bin/scraping_server.dart の内容を以下のように変更します。
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;
Future<Response> _handler(Request request) async {
return Response.ok("test");
}
void main() async {
var handler = const Pipeline()
.addMiddleware(logRequests())
.addHandler(_handler);
var server = await shelf_io.serve(handler, 'localhost', 3000);
}
その後、以下コマンドを実行して http://localhost:3000 にブラウザでアクセスすると、”test” の文字が表示されます。
$ dart run
これで、Webサーバーの起動ができました。
App.scala を以下の内容に変更します。
package scraping_server
import org.scalatra.ScalatraServlet
import javax.servlet.Servlet
import org.eclipse.jetty.webapp.WebAppContext
import org.eclipse.jetty.servlet.ServletHolder
import org.eclipse.jetty.server.Server
class MyWebService extends ScalatraServlet {
get("/") {
"test"
}
}
object WebServiceBuilder {
def buildWebService(port: Integer, webServiceClass: Class[_ <: Servlet]): Server = {
val server = new Server(port)
val context = new WebAppContext()
context.setContextPath("/")
context.setResourceBase("/tmp")
context.addServlet(new ServletHolder(webServiceClass), "/")
server.setHandler(context)
server
}
}
object App {
def main(args: Array[String]): Unit = {
val server: Server = WebServiceBuilder.buildWebService(3000, classOf[MyWebService])
server.start()
}
}
その後、以下コマンドを実行してから http://localhost:3000 にブラウザでアクセスすると、”test” の文字が表示されます。
$ gradle run
これで、Webサーバーの起動ができました。
まず以下の内容で main.exs を作成します。
defmodule ScrapingServer do
def main() do
Plug.Cowboy.http(ScrapingServer.Plug, [], port: 3000)
end
end
defmodule ScrapingServer.Plug do
import Plug.Conn
def init(options), do: options
def call(conn, _opts) do
conn
|> put_resp_content_type("application/json")
|> send_resp(200, "test")
end
end
その後、以下コマンドを実行して http://localhost:3000 にブラウザでアクセスすると、”test” の文字が表示されます。
$ mix run -e 'ScrapingServer.main' --no-halt
これで、Webサーバーの起動ができました。
まず、app/Main.hs を以下の内容に変更します。
{-# LANGUAGE OverloadedStrings #-}
module Main where
import qualified Network.Wai.Handler.Warp as Warp
import qualified Network.Wai as Wai
import qualified Network.HTTP.Types as HTypes
app :: Wai.Application
app _ send = do
send $ Wai.responseBuilder HTypes.status200 [] "test"
main :: IO ()
main = do
Warp.run 3000 app
その後、以下コマンドを実行して http://localhost:3000 にブラウザでアクセスすると、”test” の文字が表示されます。
$ stack run
これで、Webサーバーの起動ができました。
scraping_server/bin/main.ml を以下の内容に書き換えます。
open Opium
let handler _request =
"test"
|> Response.of_plain_text
|> Lwt.return
;;
let _ =
App.empty
|> App.get "/" handler
|> App.run_command
;;
その後、以下のコマンドを実行して http://localhost:3000 にブラウザでアクセスすると、”test” の文字が表示されます。
$ dune exec scraping_server
これで、Webサーバーの起動ができました。
次に、Wikipediaの List of programming languages のページからプログラミング言語一覧を取得して、Webサーバーのレスポンスとして返すようにします。
main.py に以下の実装を追加します。
from flask import Flask
# パッケージを追加
import requests
from bs4 import BeautifulSoup
# 関数を追加
def scraping():
url = "https://en.wikipedia.org/wiki/List_of_programming_languages"
res = requests.get(url)
soup = BeautifulSoup(res.text, 'html.parser')
items = []
for el in soup.select('.div-col li'):
items.append(el.text)
return items
app = Flask(__name__)
@app.route("/")
def handler():
# 追加した関数を使用する
return scraping()
if __name__ == "__main__":
app.run(port=3000)
追加実装の後、 $ python main.py を再実行して http://localhost:3000 にアクセスすると、Wikipediaのページから取得したプログラミング言語一覧の文字が表示されるようになります。
main.js に以下の実装を追加します。
const express = require("express");
/* 外部パッケージの追加 */
const axios = require("axios");
const cheerio = require("cheerio");
/* この関数を追加 */
async function getItems() {
// プログラミング言語一覧記事を取得
const url = "https://en.wikipedia.org/wiki/List_of_programming_languages";
const res = await axios.get(url);
// HTMLをパース
const $ = cheerio.load(res.data);
let items = [];
$(".div-col li").each((i, elem) => {
items.push($(elem).text());
});
return items;
}
const app = express();
app.get("/", async function (req, res, next) {
/* 追加した関数を使用する */
let items = await getItems();
res.json(items);
});
app.listen(3000);
追加実装の後、 $ node main.js を再実行して http://localhost:3000 にアクセスすると、Wikipediaのページから取得したプログラミング言語一覧の文字が表示されるようになります。
main.ts に以下の実装を追加します。
import express from "express";
// パッケージを追加
import axios from "axios";
import cheerio from "cheerio";
// 関数を追加
async function getItems(): Promise<string[]> {
const url: string =
"https://en.wikipedia.org/wiki/List_of_programming_languages";
const res = await axios.get(url);
const $: cheerio.Root = cheerio.load(res.data);
let items: Array<string> = [];
$(".div-col li").each((i: Number, elem: cheerio.Element) => {
items.push($(elem).text());
});
return items;
}
const app: express.Express = express();
app.get(
"/",
async function (
req: express.Request,
res: express.Response,
next: express.NextFunction,
) {
// 追加した関数を使用する
const items: Array<string> = await getItems();
res.json(items);
},
);
app.listen(3000);
追加実装の後、 $ npx ts-node main.ts を再実行して http://localhost:3000 にアクセスすると、Wikipediaのページから取得したプログラミング言語一覧の文字が表示されるようになります。
App.java に以下の実装を追加します。
package scraping_server;
import static spark.Spark.*;
// パッケージを追加する
import java.util.*;
import java.io.IOException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import com.google.gson.Gson;
public class App {
// 関数を追加する
public static String scraping() {
try {
var url = "https://en.wikipedia.org/wiki/List_of_programming_languages";
Document doc = Jsoup.connect(url).get();
Elements elements = doc.select(".div-col li");
List<String> items = new ArrayList<String>();
for (Element el : elements) {
items.add(el.text());
}
Gson gson = new Gson();
String itemsJson = gson.toJson(items);
return itemsJson;
} catch(IOException e) {
System.err.println(e.getMessage());
return "";
}
}
public static void main(String[] args) {
port(3000);
// 追加した関数を使用する
get("/", (req, res) -> scraping());
}
}
追加実装の後、 $ gradle run を再実行して http://localhost:3000 にアクセスすると、Wikipediaのページから取得したプログラミング言語一覧の文字が表示されるようになります。
Program.cs に以下の実装を追加します。
// パッケージの追加
using AngleSharp;
using System.Text.Json;
// 関数の追加
static async Task<IEnumerable<string>> Scraping()
{
var url = "https://en.wikipedia.org/wiki/List_of_programming_languages";
var config = Configuration.Default.WithDefaultLoader();
var context = BrowsingContext.New(config);
var document = await context.OpenAsync(url);
var cellSelector = ".div-col li";
var lists = document.QuerySelectorAll(cellSelector);
var items = lists.Select(el => el.TextContent);
return items;
}
static string Handler()
{
// 追加した関数を使用する
var task = Scraping();
var items = task.Result;
var itemsJson = JsonSerializer.Serialize(items);
return itemsJson;
}
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", Handler);
app.Run("http://localhost:3000");
追加実装の後、 $ dotnet run を再実行して http://localhost:3000 にアクセスすると、Wikipediaのページから取得したプログラミング言語一覧の文字が表示されるようになります。
main.go に以下の実装を追加します。
package main
import (
"net/http"
"log"
"github.com/gin-gonic/gin"
// パッケージの追加
"github.com/PuerkitoBio/goquery"
)
// スクレイピング用の関数を追加
func Scrape() []string {
res, err := http.Get("https://en.wikipedia.org/wiki/List_of_programming_languages")
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
doc, err := goquery.NewDocumentFromReader(res.Body)
if err != nil {
log.Fatal(err)
}
items := []string{}
doc.Find(".div-col li").Each(func(i int, s *goquery.Selection) {
items = append(items, s.Text())
})
return items
}
func main() {
router := gin.Default()
router.GET("/", func(c *gin.Context) {
// 追加した関数を使用する
items := Scrape()
c.JSON(http.StatusOK, items)
})
router.Run(":3000")
}
追加実装の後、 $ go run main.go を再実行して http://localhost:3000 にアクセスすると、Wikipediaのページから取得したプログラミング言語一覧の文字が表示されるようになります。
main.rs に以下の実装を追加します。
use actix_web::{get, App, HttpResponse, HttpServer, Responder};
/* パッケージを追加 */
use scraper::{Html, Selector};
/* 関数を追加 */
async fn scraping() -> Vec<String> {
let mut items: Vec<String> = Vec::new();
// HTMLの取得
let url = "https://en.wikipedia.org/wiki/List_of_programming_languages";
let html = reqwest::get(url)
.await
.unwrap()
.text()
.await
.unwrap();
// HTMLのパース
let fragment = Html::parse_fragment(&html);
let selector = Selector::parse(".div-col li").unwrap();
for element in fragment.select(&selector) {
items.push(element.text().collect::<Vec<_>>().join(""));
}
items
}
#[get("/")]
async fn handler() -> impl Responder {
// 追加した関数を使用する
let items: Vec<String> = scraping().await;
let items_json = serde_json::to_string(&items).unwrap();
HttpResponse::Ok().body(items_json)
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(handler)
})
.bind(("127.0.0.1", 3000))?
.run()
.await
}
追加実装の後、 $ cargo run を再実行して http://localhost:3000 にアクセスすると、Wikipediaのページから取得したプログラミング言語一覧の文字が表示されるようになります。
Rustには他のプログラミング言語にはない概念があり、コードの内容を理解するには公式のドキュメントが参考になります。 とくに所有権とライフタイムの概念を学んでおくとコードが理解しやすくなります。
App.kt に以下の実装を追加します。
package scraping_server
import spark.kotlin.*
// パッケージを追加する
import it.skrape.core.htmlDocument
import it.skrape.fetcher.HttpFetcher
import it.skrape.fetcher.response
import it.skrape.fetcher.skrape
import it.skrape.selects.eachText
import kotlinx.serialization.*
import kotlinx.serialization.json.*
// 関数を追加する
fun scraping(): String {
var items: List<String> = emptyList()
skrape(HttpFetcher) {
request {
url = "https://en.wikipedia.org/wiki/List_of_programming_languages"
}
response {
htmlDocument {
items = ".div-col li" { findAll { eachText } }
}
}
}
return Json.encodeToString(items)
}
fun main(args: Array<String>) {
val http: Http = ignite().port(3000)
http.get("/") {
// 追加した関数を使用する
scraping()
}
}
追加実装の後、 $ gradle run を再実行して http://localhost:3000 にアクセスすると、Wikipediaのページから取得したプログラミング言語一覧の文字が表示されるようになります。
ScrapingServer.swift に以下の実装を追加します。
import Vapor
// パッケージの追加
import Alamofire
import Kanna
@main
public struct ScrapingServer {
// 関数の追加
public static func scraping() async -> [String] {
let url = "https://en.wikipedia.org/wiki/List_of_programming_languages"
async let res = AF.request(url).serializingString().response
guard let html = await res.value else { return [] }
guard let doc = try? HTML(html: html, encoding: String.Encoding.utf8) else { return [] }
var items: [String] = []
for value in doc.css(".div-col li") {
guard let text = value.text else { continue }
items.append(text)
}
return items
}
// 関数の追加
public static func getItemsJson() async -> String {
let items = await scraping()
let encoder = JSONEncoder()
guard let itemsJson = try? encoder.encode(items) else { return "" }
return String(data: itemsJson, encoding: .utf8)!
}
public static func main() {
do {
let app = try Application(.detect())
app.get("") { request -> EventLoopFuture<String> in
let promise = request.eventLoop.makePromise(of: String.self)
promise.completeWithTask {
// 追加した関数を利用
let itemsJson = await getItemsJson()
return itemsJson
}
return promise.futureResult
}
app.http.server.configuration.port = 3000
try app.run()
} catch {
print(error.localizedDescription)
}
}
}
追加実装の後、 $ swift run を再実行して http://localhost:3000 にアクセスすると、Wikipediaのページから取得したプログラミング言語一覧の文字が表示されるようになります。
main.rb に以下の実装を追加します。
require "sinatra"
# パッケージを追加
require 'mechanize'
# 関数を追加
def scraping
agent = Mechanize.new
page = agent.get('https://en.wikipedia.org/wiki/List_of_programming_languages')
page.search('.div-col li').map do |el|
el.inner_text
end
end
set :port, 3000
get "/" do
# 追加した関数を呼び出す
scraping.to_json
end
追加実装の後、 $ ruby main.rb を再実行して http://localhost:3000 にアクセスすると、Wikipediaのページから取得したプログラミング言語一覧の文字が表示されるようになります。
index.php に以下の実装を追加します。
<?php
require __DIR__ . '/vendor/autoload.php';
$app = new \Slim\App();
/* この関数を追加 */
function getItems() {
// プログラミング言語一覧記事を取得
$url = 'https://en.wikipedia.org/wiki/List_of_programming_languages';
$client = new GuzzleHttp\Client();
$res = $client->request('GET', $url);
$html = $res->getBody()->getContents();
// HTMLをパース
$dom = new PHPHtmlParser\Dom;
$dom->loadStr($html);
$itemList = $dom->find('.div-col li');
$items = [];
foreach ($itemList as $item) {
$items[] = $item->innerText();
}
return $items;
}
$app->get('/', function ($request, $response) {
/* 追加した関数を使用する */
$items = getItems();
$response->getBody()->write(json_encode($items));
return $response;
});
$app->run();
追加実装の後、 $ php -S localhost:3000 を再実行して http://localhost:3000 にアクセスすると、Wikipediaのページから取得したプログラミング言語一覧の文字が表示されるようになります。
ここまでのコードは外部パッケージを利用して拡張性のある実装にしていました。
しかし、PHPはWeb開発に特化した言語であることもあり、上記の外部パッケージの機能は実はPHP言語そのものにも備わっています。
可読性や拡張性はやや下がってしまいますが、シンプルなスクレイピングとWebサーバーを手軽に実装する場合は下記のコードでも同じ結果を得ることができます。
<?php
// Webページの取得
$url = 'https://en.wikipedia.org/wiki/List_of_programming_languages';
$html = file_get_contents($url);
// HTMLのパース
$dom = new DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML($html);
libxml_clear_errors();
$finder = new DomXPath($dom);
$nodes = $finder->query("//*/ul/li");
$items = [];
foreach($nodes as $node) {
$items[] = $node->nodeValue;
}
// 結果の表示
echo json_encode($items);
こちらも前回と同様に、実装した後は、 $ php -S localhost:3000 を再実行して http://localhost:3000 にアクセスすることで結果を確認できます。
main.cpp に以下の実装を追加します。
#include "crow.h"
/* パッケージを追加 */
#include <string>
#include <vector>
#include <cpr/cpr.h>
#include "gumbo.h"
#include <nlohmann/json.hpp>
using std::string;
using std::vector;
using json = nlohmann::json;
/* 関数を追加 */
string extract_text(GumboNode *node) {
if (node->type == GUMBO_NODE_TEXT) {
return string(node->v.text.text);
} else if (
node->type == GUMBO_NODE_ELEMENT &&
node->v.element.tag != GUMBO_TAG_SCRIPT &&
node->v.element.tag != GUMBO_TAG_STYLE
) {
string contents = "";
GumboVector *children = &node->v.element.children;
for (unsigned int i = 0; i < children->length; ++i) {
string text = extract_text((GumboNode *)children->data);
if (i != 0 && !text.empty()) {
contents.append("");
}
contents.append(text);
}
return contents;
} else {
return "";
}
}
/* 関数を追加 */
vector<GumboNode*> find_elements(GumboNode *node) {
vector<GumboNode*> elems;
GumboAttribute *attr;
if (node->type != GUMBO_NODE_ELEMENT) {
return elems;
}
if (
(attr = gumbo_get_attribute(&node->v.element.attributes, "class")) &&
strstr(attr->value, "div-col") != NULL
) {
GumboVector *div_children = &node->v.element.children;
for (int i = 0; i < div_children->length; ++i) {
auto div_child = static_cast<GumboNode *>(div_children->data);
if (
div_child->type == GUMBO_NODE_ELEMENT &&
div_child->v.element.tag == GUMBO_TAG_UL
) {
GumboVector *ul_children = &div_child->v.element.children;
for (int i = 0; i < ul_children->length; ++i) {
auto ul_child = static_cast<GumboNode *>(ul_children->data);
if (
ul_child->type == GUMBO_NODE_ELEMENT &&
ul_child->v.element.tag == GUMBO_TAG_LI
) {
elems.push_back(ul_child);
}
}
}
}
return elems;
}
GumboVector *children = &node->v.element.children;
for (int i = 0; i < children->length; ++i) {
auto found_elems = find_elements(static_cast<GumboNode *>(children->data));
elems.insert(elems.end(), found_elems.begin(), found_elems.end());
}
return elems;
}
/* 関数を追加 */
vector<string> get_items() {
// Webページの取得
string url = "https://en.wikipedia.org/wiki/List_of_programming_languages";
cpr::Response r = cpr::Get(cpr::Url{url});
string html = r.text;
// HTMLのパース
GumboOutput* output = gumbo_parse(html.c_str());
auto elems = find_elements(output->root);
vector<string> items;
for (GumboNode* elem : elems) {
items.push_back(extract_text(elem));
}
gumbo_destroy_output(&kGumboDefaultOptions, output);
return items;
}
int main() {
crow::SimpleApp app;
CROW_ROUTE(app, "/")([](){
/* 関数を使用 */
auto items = get_items();
json items_json = items;
return items_json.dump();
});
app.port(3000).multithreaded().run();
}
追加実装の後、 $ cmake . && make && ./bin/main を再実行して http://localhost:3000 にアクセスすると、Wikipediaのページから取得したプログラミング言語一覧の文字が表示されるようになります。
なお、HTMLのパース部分は gumbo-query という外部パッケージを使うことでさらに簡潔に記述できます。 今回はパッケージマネージャーの conan でインストールできなかったので gumbo-parser だけで記述しています。
scraping_server/bin/scraping_server.dart に以下の実装を追加します。
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;
// パッケージを追加
import 'package:http/http.dart' as http;
import 'package:html/parser.dart' show parse;
import 'dart:convert';
// 関数を追加
Future _scraping() async {
final url = Uri.parse('https://en.wikipedia.org/wiki/List_of_programming_languages');
final response = await http.get(url);
final document = parse(response.body);
final items = document
.querySelectorAll('.div-col li')
.map((el) => el.text)
.toList();
return items;
}
Future<Response> _handler(Request request) async {
// 追加した関数を使用する
var items = await _scraping();
var itemsJson = jsonEncode(items);
final Map<String, String> _headers = {'Content-Type': 'application/json'};
return Response.ok(itemsJson, headers: _headers);
}
void main() async {
var handler = const Pipeline()
.addMiddleware(logRequests())
.addHandler(_handler);
var server = await shelf_io.serve(handler, 'localhost', 3000);
}
追加実装の後、 $ dart run を再実行して http://localhost:3000 にアクセスすると、Wikipediaのページから取得したプログラミング言語一覧の文字が表示されるようになります。
App.scala に以下の実装を追加します。
package scraping_server
import org.scalatra.ScalatraServlet
import javax.servlet.Servlet
import org.eclipse.jetty.webapp.WebAppContext
import org.eclipse.jetty.servlet.ServletHolder
import org.eclipse.jetty.server.Server
// パッケージの追加
import net.ruippeixotog.scalascraper.browser.JsoupBrowser
import net.ruippeixotog.scalascraper.dsl.DSL._
import net.ruippeixotog.scalascraper.dsl.DSL.Extract._
import net.ruippeixotog.scalascraper.dsl.DSL.Parse._
import com.google.gson.Gson
object Scraper {
// スクレイピング用の関数を追加
def getItems(): List[String] = {
val browser = JsoupBrowser()
val url = "https://en.wikipedia.org/wiki/List_of_programming_languages"
val doc = browser.get(url)
val lists = doc >> elementList(".div-col li")
lists.map(_ >> allText)
}
}
class MyWebService extends ScalatraServlet {
get("/") {
// 追加した関数を呼び出す
val items: List[String] = Scraper.getItems()
val gson = new Gson()
gson.toJson(items.toArray)
}
}
object WebServiceBuilder {
def buildWebService(port: Integer, webServiceClass: Class[_ <: Servlet]): Server = {
val server = new Server(port)
val context = new WebAppContext()
context.setContextPath("/")
context.setResourceBase("/tmp")
context.addServlet(new ServletHolder(webServiceClass), "/")
server.setHandler(context)
server
}
}
object App {
def main(args: Array[String]): Unit = {
val server: Server = WebServiceBuilder.buildWebService(3000, classOf[MyWebService])
server.start()
}
}
追加実装の後、 $ gradle run を再実行して http://localhost:3000 にアクセスすると、Wikipediaのページから取得したプログラミング言語一覧の文字が表示されるようになります。
main.exs に以下の実装を追加します。
defmodule ScrapingServer do
def main() do
Plug.Cowboy.http(ScrapingServer.Plug, [], port: 3000)
end
end
defmodule ScrapingServer.Plug do
import Plug.Conn
def init(options), do: options
def call(conn, _opts) do
# スクレイピング用の関数を呼び出す
items = ScrapingServer.Scraping.run()
items_json = Poison.encode!(items)
conn
|> put_resp_content_type("application/json")
|> send_resp(200, items_json)
end
end
# スクレイピング用の処理を追加
defmodule ScrapingServer.Scraping do
def run() do
url = "https://en.wikipedia.org/wiki/List_of_programming_languages"
body = HTTPoison.get!(url).body
Floki.find(body, ".div-col li")
|> Enum.map(&(&1 |> Floki.text() |> String.strip()))
end
end
追加実装の後、 $ mix run -e 'ScrapingServer.main' --no-halt を再実行して http://localhost:3000 にアクセスすると、Wikipediaのページから取得したプログラミング言語一覧の文字が表示されるようになります。
app/Main.hs に以下の実装を追加します。
{-# LANGUAGE OverloadedStrings #-}
module Main where
import qualified Network.Wai.Handler.Warp as Warp
import qualified Network.Wai as Wai
import qualified Network.HTTP.Types as HTypes
-- パッケージの追加
import Text.XML.Cursor
import Text.HTML.DOM as H
import Network.HTTP.Conduit
import Data.Aeson (encode)
import qualified Data.Text as T
import Data.Text.Lazy.Encoding
-- 関数を追加
getItems :: Cursor -> [T.Text]
getItems cursor = do
cursor
$// attributeIs "class" "div-col"
&// element "li"
&// element "a"
&// content
app :: Wai.Application
app _ send = do
-- スクレイピング用の処理を追加
doc <- parseLBS <$> simpleHttp "https://en.wikipedia.org/wiki/List_of_programming_languages"
let cursor = fromDocument doc
let items = getItems cursor
let itemsJson = encode items
let decodedItems = decodeUtf8 itemsJson
send $ Wai.responseBuilder HTypes.status200 [] (encodeUtf8Builder decodedItems)
main :: IO ()
main = do
Warp.run 3000 app
追加実装の後、 $ stack run を再実行して http://localhost:3000 にアクセスすると、Wikipediaのページから取得したプログラミング言語一覧の文字が表示されるようになります。
HaskellのAPIや機能を調べるとき、登場する記号が多いので、Google検索では調べにくいです。
そのため、HoogleというHaskellのAPIを検索するための専用サイトを利用することがおすすめです。
scraping_server/bin/main.ml に以下の実装を追加します。
open Opium
(* パッケージを追加 *)
open Lwt
open Soup
(* HTMLを取得する関数を追加 *)
let get_html =
let url = Uri.of_string "https://en.wikipedia.org/wiki/List_of_programming_languages" in
Cohttp_lwt_unix.Client.get(url) >>= fun (_resp, body) ->
body |> Cohttp_lwt.Body.to_string
;;
(* HTMLをパースする関数を追加 *)
let extract_items html =
(parse html) $$ ".div-col li"
|> to_list
|> List.fold_left (fun list elem ->
(trimmed_texts elem |> String.concat "") :: list) []
;;
(* スクレイピングする関数を追加 *)
let scraping =
get_html >>= fun html ->
html |> extract_items |> Lwt.return
;;
(* JSONに変換する関数を追加 *)
let convert_to_json list =
list
|> List.map (fun str -> `String str)
|> (fun list -> `List list)
;;
let handler _request =
(* 追加した関数を使用する *)
scraping >>= fun items ->
items
|> convert_to_json
|> Response.of_json
|> Lwt.return
;;
let _ =
App.empty
|> App.get "/" handler
|> App.run_command
;;
追加実装の後、 $ dune exec scraping_server を再実行して http://localhost:3000 にアクセスすると、Wikipediaのページから取得したプログラミング言語一覧の文字が表示されるようになります。
|> 左側の値を、右側の関数の引数に入れて実行します。
:: 左側の値を、右側のリストの先頭に追加します。
;; コードのトップレベルで式を定義するときの区切り文字として式の終わりに書きます。
>>= 左側に非同期関数、右側に実行結果を引数に受け取る関数を書くことができます。Lwtのライブラリで用意されている演算子です。
$$ 左側にLambdaSoupのnode、右側にCSSセレクターを書くことで、nodeからCSSセレクターに該当する要素をすべて抽出できます。LambdaSoupのライブラリで用意されている演算子です。
同じ「Webサーバーを立ててスクレイピング結果を返す」だけの処理でも、言語ごとにセットアップの手間もコードの雰囲気も大きく違います。気になった言語があれば、実際に手元で動かして書き味を確かめてみてください。