mk-mode BLOG

このブログは自作の自宅サーバに構築した Debian GNU/Linux で運用しています。
PC・サーバ構築等の話題を中心に公開しております。(クローンサイト: GitHub Pages

ブログ開設日2009-01-05
サーバ連続稼働時間
Reading...
Page View 合計
Reading...
今日
Reading...
昨日
Reading...

C++ - HTML 取得!

[ プログラミング ] [ C言語 ]

こんばんは。

CentOS サーバ構築関連の記事の合間に、気分転換で別カテゴリのストック記事を。

C++ で Web サイトの HTML を取得してファイルに保存する方法についての記録です。

(それほど洗練されたソースコードでもありません。ご承知おきください)

0. 前提条件

  • Linux Mint 17 での作業を想定。
  • g++(c++) のバージョンは 4.8.2

1. C++ ソースコード作成

以下のようにソースコードを作成した。
cURL を利用して HTML を取得後、1行ずつ読み込んでテキストファイルに出力している。

GetHtml.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/***********************************************************
 * HTML 取得(テキストファイル保存)
 **********************************************************/
#include <fstream>           // for ofstream
#include <iostream>          // for cout, endl
#include <stdio.h>           // for fopen
#include <string.h>          // for strlen
#include <curl/curl.h>       // Require `libcurl4-openssl-dev`
#include <list>              // list

#define URL     "http://www.mk-mode.com/octopress/"
#define FILE_O  "test.html"  // Out-file

using namespace std;

/**
 * split関数
 * @param string str 分割したい文字列
 * @param string delim デリミタ
 * @return list<string> 分割された文字列
 */
list<string> split(string str, string delim) {
    list<string> result;
    int cutAt;
    while ((cutAt = str.find_first_of(delim)) != str.npos) {
        if (cutAt > 0) {
            result.push_back(str.substr(0, cutAt));
        }
        str = str.substr(cutAt + 1);
    }
    if (str.length() > 0) {
        result.push_back(str);
    }
    return result;
}

/*
 * CURL コールバック関数
 */
size_t callBackFunc(char* ptr, size_t size, size_t nmemb, string* stream) {
    int realsize = size * nmemb;
    stream->append(ptr, realsize);
    return realsize;
}

/*
 * [CLASS] Process
 */
class Proc
{
    // Declaration
    ofstream ofs;
    CURL     *curl;
    CURLcode res;
    string   chunk, strIter;

    public:
        Proc();          // Constructor
        ~Proc();         // Destructor
        int execMain();  // Main Process
};

/*
 * Constructor
 */
Proc::Proc()
{
    // CURL Initial
    curl = curl_easy_init();
    // CURLOPT_URL に URL を指定
    curl_easy_setopt(curl, CURLOPT_URL, URL);
    // CURLOPT_WRITEFUNCTION にコールバック関数を指定
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callBackFunc);
    // CURLOPT_WRITEDATA にコールバック関数にて処理されたあとのデータ格納ポインタを指定
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (string*)&chunk);
}

/*
 * Destructor
 */
Proc::~Proc()
{
    // Clean up
    curl_easy_cleanup(curl);
}

/*
 * Main Process
 */
int Proc::execMain()
{
    try {
        // Open Out-file
        ofs.open(FILE_O);
        if (ofs.fail()) {
            cerr << "[ERROR] Could not open " << FILE_O << endl;
            return 1;
        }

        // cURL リクエスト実行
        res = curl_easy_perform(curl);
        if (res == CURLE_OK) {  // リクエスト処理 OK 時
            // [ 丸ごと ] の場合はこれで OK
            //cout << chunk << endl;
            //ofs  << chunk << endl;

            // [ 1行ずつ ]
            // split の実行
            list<string> strList = split(chunk, "\n");
            // イテレータの取得
            list<string>::iterator iter = strList.begin();
            // コンソール出力
            while (iter != strList.end()) {  // 最後まで
                strIter = *iter;
                cout << strIter << endl;
                ofs  << strIter << endl;
                ++iter;
            }
        } else {  // リクエスト処理 NG 時
            cerr << "#### curl_easy_perform() failed: "
                 << curl_easy_strerror(res)
                 << endl;
            return 1;
        }
    } catch (char *e) {
        cout << "EXCEPTION : " << e << endl;
        return 1;
    }
    return 0;
}

/*
 * Execution
 */
int main(){
    try {
        Proc objMain;
        int iRetCode = objMain.execMain();
        if (iRetCode == 0) {
            cout << "SUCCESS!" << endl;
        } else {
            cout << "FAILED!"  << endl;
        }
    } catch (char *e) {
        cout << "EXCEPTION : " << e << endl;
        return 1;
    }
    return 0;
}

2. C++ ソースコンパイル

以下のコマンドで C++ ソースをコンパイルする。
-O2 最適化のオプション)

1
$ g++ -O2 -o GetHtml GetHtml.cpp -lcurl

何も出力されなければ成功。

3. 実行

実際に、実行してみる。

1
$ ./GetHtml

同じディレクトリ内に “text.html” というテキストファイルが作成されるので確認する。


Web スクレイピング等で使えるであろう情報でした。

以上。

Comments