指定したユーザーのはてなブックマーク一覧を取得する

いいかげん、そろそろプログラミングネタを書かねば。 ってことで、C# で「指定したユーザーのはてなブックマーク一覧を取得する」サンプルを書いてみた。

はてなブックマークに専用の API が見当たらないので、RSS フィードを取得してパースしている。 オフセットも指定できるから、これはこれで XML を返す Web API と見れなくもない。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace HatenaBookmarkSample
{
    class Program
    {
        static void Main(string[] args)
        {
            var bookmarks = GetUserBookmarks("griefworker", 100)
                .GetAwaiter().GetResult();

            foreach(var b in bookmarks)
            {
                Console.WriteLine(b.Title);
                Console.WriteLine(b.Url);
                Console.WriteLine(b.Comment);
                Console.WriteLine();
            }

            Console.WriteLine("Enter で終了します。");
            Console.ReadLine();
        }

        static async Task<IEnumerable<Bookmark>> GetUserBookmarks(string userId, int offset = 0)
        {
            var httpClient = new HttpClient();

            // RSS フィードなのでレスポンスは XML
            httpClient.DefaultRequestHeaders
                .Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));

            // User-Agent を設定しないと nginx にはじかれる
            httpClient.DefaultRequestHeaders
                .UserAgent.Add(new ProductInfoHeaderValue("HatenaClient", "0.0.1"));

            var requestUri = $"http://b.hatena.ne.jp/{userId}/rss";
            if (0 < offset)
            {
                requestUri += $"?of={offset}";
            }

            var rss = await httpClient.GetStringAsync(requestUri);

            var document = XDocument.Parse(rss);

            var bookmarks = document.Descendants()
                .Where(e => e.Name.LocalName == "item")
                .Select(ToBookmark)
                .ToList();

            return bookmarks;
        }

        static Bookmark ToBookmark(XElement element)
        {
            var bookmark = new Bookmark();

            foreach(var e in element.Descendants())
            {
                switch (e.Name.LocalName)
                {
                    case "title":
                        bookmark.Title = e.Value;
                        break;
                    case "link":
                        bookmark.Url = e.Value;
                        break;
                    case "description":
                        bookmark.Comment = e.Value;
                        break;
                }
            }

            return bookmark;
        }
    }

    class Bookmark
    {
        public string Title { get; set; }

        public string Url { get; set; }

        public string Comment { get; set; }
    }
}

実行結果は次の通り。

f:id:griefworker:20170405153751p:plain

はてなブックマークREST API で用意してくれたらいいんだけどな。