LoginSignup
6
5

More than 5 years have passed since last update.

簡易RSSリーダーを作ってみた 〜 C編 〜

Last updated at Posted at 2015-05-30

確か、2〜3年くらい前にCで書いた簡易RSSリーダー。IBMのdeveloperWorksの記事を参考に、Linuxでlibcurlを使ってダウンロードしたRSSフィードをlibxml2を使って解析しながら記事のタイトルとリンク先を表示するというのをやってXMLを学習したような気がするけど、XMLの解析処理をどうやって書いたかさっぱり思い出せないorz

サンプルコード

libcurlによるRSSフィードのダウンロードはIBMのサンプルをほぼそのまま使って、ダウンロード結果を表示する所を、アップルのRSSフィードのサマリが出るようにしています。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include <sys/types.h>
#include <errno.h>
#include <libxml/xmlerror.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>

#define MAX_BUF 65536

char wr_buf[MAX_BUF+1];
int  wr_index;

#define PATH "http://www.apple.com/jp/main/rss/hotnews/hotnews.rss"

/*
 * Write data callback function (called within the context of 
 * curl_easy_perform.
 */

size_t write_data( void *buffer, size_t size, size_t nmemb, void *userp ) {
    int segsize = size * nmemb;

    if (wr_index + segsize > MAX_BUF) {
        *(int *)userp = 1;
        return 0;
    }

    memcpy( (void *)&wr_buf[wr_index], buffer, (size_t)segsize );
    wr_index += segsize;
    wr_buf[wr_index] = 0;
    return segsize;
}

/*
 * Parse RSS and print summary.
 */

int
print_rss(char* text) {
    int     i;
    int     size;
    xmlDoc  *doc;
    xmlXPathContextPtr  xpathCtx;
    xmlXPathObjectPtr   xpathObj;
    xmlNodeSetPtr       nodes;
    xmlNode             *node;
    xmlChar             *textcount;

    /* parse an XML in-memory document and build a tree. */
    doc = xmlReadMemory(text, strlen(text), "noname.xml", NULL, 0);
    if (doc == NULL) {
        perror("xmlReadFile");
        return -1;
    }

    /* Create a new xmlXPathContext */
    xpathCtx = xmlXPathNewContext(doc);
    if (xpathCtx == NULL) {
        perror("xmlXPathNewContext");
        xmlFreeDoc(doc);
        xmlCleanupParser();
        return -1;
    }

    /* Get Node list of RSS channel */
    xpathObj = xmlXPathEvalExpression(BAD_CAST "/rss/channel", xpathCtx);
    nodes = xpathObj->nodesetval;
    if (nodes == NULL) {
        xmlXPathFreeContext(xpathCtx);
        xmlFreeDoc(doc);
        xmlCleanupParser();
        return -1;
    }

    /* Get and print Title of RSS Feed. */
    size = nodes->nodeNr;
    for (i = 0; i < size; i++) {
        node = xmlFirstElementChild(nodes->nodeTab[i]);
        if (node == NULL) {
            continue;
        }
        for (; node; node = xmlNextElementSibling(node)) {
            if (strcmp((char*)node->name, "title")) {
                    continue;
            }
            textcount = xmlNodeGetContent(node);
            if (textcount) {
                printf("\nTitle: %s\n\n", textcount);
                xmlFree(textcount);
            }
        }
    }
    xmlXPathFreeObject(xpathObj);

    /* Get Node list of RSS items */
    xpathObj = xmlXPathEvalExpression(BAD_CAST "/rss/channel/item", xpathCtx);
    nodes = xpathObj->nodesetval;
    if (nodes == NULL) {
        xmlXPathFreeContext(xpathCtx);
        xmlFreeDoc(doc);
        xmlCleanupParser();
        return -1;
    }

    /* Get and print title and link of each items. */
    size = nodes->nodeNr;
    for (i = 0; i < size; i++) {
        node = xmlFirstElementChild(nodes->nodeTab[i]);
        if (node == NULL) {
            continue;
        }
        for (; node; node = xmlNextElementSibling(node)) {
            if (strcmp((char*)node->name, "title") != 0 &&
                strcmp((char*)node->name, "link") != 0) {
                    continue;
            }
            textcount = xmlNodeGetContent(node);
            if (textcount) {
                printf(" %s:\t%s\n", node->name, textcount);
                xmlFree(textcount);
            }
        }
        printf("\n");
    }

    xmlXPathFreeObject(xpathObj);
    xmlXPathFreeContext(xpathCtx);
    xmlFreeDoc(doc);
    xmlCleanupParser();
    return 0;
}

/*
 * Simple curl application to read the rss feed from a Web site.
 */

int main( void )
{
    CURL *curl;
    CURLcode ret;
    int  wr_error;

    wr_error = 0;
    wr_index = 0;

    curl = curl_easy_init();
    if (!curl) {
        printf("couldn't init curl\n");
        return 0;
    }

    curl_easy_setopt(curl, CURLOPT_URL, PATH);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&wr_error);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);

    ret = curl_easy_perform(curl);
    if ( ret == 0 ) {
        //
        print_rss(wr_buf);
    } else {
        printf( "ret = %d (write_error = %d)\n", ret, wr_error );
        curl_easy_cleanup(curl);
        return 1;
    }

    curl_easy_cleanup(curl);
    return 0;
}

コンパイル

コンパイルには libcurl-devel と libxml2-devel が必要なので事前にインストールしておきます(CentOSの場合)。コンパイル時は -I オプションによるlibxml2のヘッダファイルの指定と -l オプションによるライブラリの指定が必要です。

$ cc -I/usr/include/libxml2 getrss.c -o getrss -lcurl -lxml2

実行結果

「アップル - ホットニュース」のサマリが表示されました。

$ ./getrss

Title: アップル - ホットニュース

 title: Apple、感圧タッチトラックパッドを搭載した15インチのMacBook Pro、238,800円の新しいiMac Retina 5Kディスプレイモデルを発売
 link:  http://www.apple.com/jp/pr/library/2015/05/19Apple-Introduces-15-inch-MacBook-Pro-with-Force-Touch-Trackpad-New-1-999-iMac-with-Retina-5K-Display.html

 title: 日本郵政グループ、IBM、Apple、日本の高齢者がサービスを通じて家族・地域コミュニティーとつながるために、iPadと専用アプリケーションを提供
 link:  http://www.apple.com/jp/pr/library/2015/04/30Japan-Post-Group-IBM-and-Apple-Deliver-iPads-and-Custom-Apps-to-Connect-Elderly-in-Japan-to-Services-Family-and-Community.html

 title: Apple、第2四半期の業績を発表
 link:  http://www.apple.com/jp/pr/library/2015/04/27Apple-Reports-Record-Second-Quarter-Results.html

    ・
    ・
    ・
6
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5