💻️
スクレイピングとサーバー実行のサンプルコード(C++)

C++でスクレイピングとWebサーバーを開発する手順を書いていきます。

開発の手順

環境構築

# C++の開発環境構築
$ brew reinstall gcc
$ brew install cmake
$ sudo pip install conan

# プロジェクトのディレクトリ作成
$ mkdir scraping-server
$ cd scraping-server

パッケージマネージャーの conan を使うため、プロジェクトのディレクトリ配下に以下のファイルを追加します。

CMakeLists.txt
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})
conanfile.txt
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

インストールしている外部パッケージは以下のものです。

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

スクレイピングで取得した情報を表示する

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 だけで記述しています。