C++ で libcurl と libxml2 使ってみる

まぁつまりは、C++API 叩いて XML をパースしてみようって話です。

必要なもの

  • curl
  • libxml2
  • g++
  • 気力

いらないもの

  • 眠気

サンプルコード

#include<string>
#include<iostream>
#include<curl/curl.h>
#include<libxml/xpath.h>
#include<libxml/xmlreader.h>
#define XPATH "/rss/channel/item/title/text()"
using namespace std;

size_t callBackFunk(char* ptr, size_t size, size_t nmemb, string* stream)
{
    int realsize = size * nmemb;
    stream->append(ptr, realsize);
    return realsize;
}
int main()
{
    CURL *curl;
    CURLcode res;
    curl = curl_easy_init();
    string chunk;
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, "http://dailynews.yahoo.co.jp/fc/rss.xml");
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callBackFunk);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (string*)&chunk);
        res = curl_easy_perform(curl);
        curl_easy_cleanup(curl);
    }
    if (res != CURLE_OK) {
        cout << "curl error" << endl;
        return 1;
    }
    xmlDocPtr doc;
    xmlXPathContextPtr ctx;
    xmlTextReaderPtr reader;
    xmlXPathObjectPtr xpobj;
    if (reader = xmlReaderForDoc((xmlChar*)chunk.c_str(), NULL, NULL, 1) ) { 
        xmlTextReaderRead(reader);
        xmlTextReaderExpand(reader);
        if ((doc = xmlTextReaderCurrentDoc(reader))) {
            if ((ctx = xmlXPathNewContext(doc))) {
                if ((xpobj = xmlXPathEvalExpression((xmlChar *)XPATH, ctx))) {
                    if (!xmlXPathNodeSetIsEmpty(xpobj->nodesetval)) {
                        xmlNodeSetPtr nodes = xpobj->nodesetval;
                        int size = (nodes) ? nodes->nodeNr : 0;
                        for (int i = 0; i < size; ++i) {
                            if (!xmlXPathNodeSetIsEmpty(nodes)) {
                                xmlNodePtr node = xmlXPathNodeSetItem(nodes, i);
                                if (node->content) {
                                    cout << node->parent->name << " => " << node->content << endl;
                                } else {
                                    cout << "invalid node" << endl;
                                }
                            }
                        }
                    }
                    xmlXPathFreeObject(xpobj);
                }
                xmlXPathFreeContext(ctx);
            }
            xmlFreeDoc(doc);
        }
        xmlFreeTextReader(reader);
    }
    xmlCleanupParser();
    return 0;
}

何をしているの?

curl

curl は、デフォルトだと標準出力に結果が出てしまうので、

  • CURLOPT_WRITEFUNCTION にコールバック関数を
  • CURLOPT_WRITEDATA にコールバック関数にて処理されたあとのデータ格納ポインタを

指定し、 CURLOPT_WRITEFUNCTION で指定した関数では ptr に curl で得たデータが入っているので、それを stream にいれてやることでCURLOPT_WRITEDATA で指定した変数に格納されるようになる。

xml

xml のパースには、libxml2 を使い、今回は変数からXMLを読み込ませるので xmlReaderForDoc を使った。
実際に欲しいところを抜き出すには xpath を使って抜き出している。

ビルド
$ g++ curlxml.cpp -lcurl  -I/path/to/libxml2 -lxml2
誰得?

たぶん自分にしか需要ない。