読者です 読者をやめる 読者になる 読者になる

『僕だけがいない街(1)〜(9)』を読んだ

僕だけがいない街』をKindleでまとめ買いして読んだ。 過去にタイムリープしてしまう能力『再上映(リバイバル)』を持った主人公が、 母親の死をきっかけに18年前に戻って過去の事件の真犯人を追う、 というミステリー・サスペンス。

母親を救うために歴史を改変することを決意してからの主人公が、 とにかく行動的で格好良かった。 個人的に、真犯人が判明するまでが一番の山場。 そこに至るまでの展開、特に印象の操作が絶妙で、 真犯人が判明したときはまさに主人公と同じような気持ちになった。

あと、本作の裏ヒロインは主人公の母親なんじゃないか。そう思うくらい見せ場多い。第一印象で勝手にろくでなし系と思っていたら、まったく逆で、むしろ親の鑑だった。ごめんなさい。

本編は8巻で終わっているので長さも丁度良い。 ページをめくる手が止まらず、つい一気に読んでしまった。 アニメ化だけでなく実写映画化までされたのも納得だ。

とら食堂 福岡分店

福島の老舗ラーメン店「とら食堂」が福岡に出店していた。その名も「とら食堂 福岡分店」。

白河ラーメンと呼ばれるジャンルらしい。昼食を食べていなくて空腹だったので、焼豚ワンタン麺を注文した。しかも麺増しで。麺増しだと麺とスープだけでなく、焼豚も1.5倍になるみたい。おかげですごいボリュームだった。

琥珀色のスープは澄んで綺麗。それでいて鶏や醤油の旨味がしっかりと出ていた。すごく優しいスープで飲み干してしまった。平打ち麺は舌ざわりがツルツル。噛むともっちり。このスープにはこの麺しかないという組み合わせだと思う。焼豚は3種類の部位が食べられて、どの肉も甲乙つけがたい。焼豚1.5倍増しは、今回みたいにMAX空腹でないと食べきれなさそうだ。

期待以上の味だった。現時点で、自分の中の「醤油ラーメン食べるときの店第1位」に輝いた。豚骨と同じかそれ以上に醤油ラーメン好きなので、また食べに行こう。

とら食堂 福岡分店

食べログとら食堂 福岡分店

久楽

天神北にある『北海道らーめん奥原流 久楽』に行ってみた。この場所、以前は理髪店が入ってたと思うけど、いつのまに変わったんだ。オープン1周年らしい。

味噌ラーメンと餃子、ライスのセットを注文した。味噌ラーメンは白味噌を選択。白味噌の味噌ラーメンは食べたことないな。

白味噌のスープが濃厚でいてまろやかで、白米によく合う。シメにご飯を投入するのがオススメらしい。今回はしなかったけど。麺にも良く絡んで美味い。

餃子はオーソドックスな味。餃子にハズレはそうそう無い。

白味噌の味噌ラーメンというのもなかなか良かった。赤味噌や合わせ味噌もあるので、他の味も試してみたくなる。味噌のつけ麺も気になるところ。あと、ランチではご飯が無料で付くみたいだ。それを考えると餃子のセットはコスパ良くないな。ラーメンだけ注文したほうがお得な気がする。

北海道らーめん奥原流 久楽 天神店

食べログ北海道らーめん奥原流 久楽 天神店

『3月のライオン(1)~(12)』を読んだ

BUMP OF CHICKEN が『ファイター』でタイアップして、『3月のライオン』のことを知った。 そして、『アンサー』で再びタイアップしたので、これをいい機会に 1巻から12巻までを Kindle でまとめ買いして読んだ。

読む前は失礼ながら、競技が違うけど『ヒカルの碁』みたいなマンガかなと思っていた。実際に読んでみると、まったく違った。将棋マンガではあるが、むしろ将棋を題材にした人間ドラマを描いたマンガに思えた。

主人公の桐山零は物語開始時点ですでに、中学でプロ入りした天才棋士という周りの評価。ただメンタルに難があり、そのせいで伸び悩んでいる状態。物語内で明かされていく彼の半生からすると、それも仕方ないことではある。 なので、将棋の技量的な成長よりも、人間的な成長に重きを置いて物語は進んでいく。

零の人間的な成長に深く関わってくるのが川本家。3姉妹と祖父だけというワケありの家族なのだが、皆まっすぐで、そしてすごく温かい。 子供がいる身としては、特にひなたやモモみたいに健やかに育って欲しいと、かなり思った。

最近の零は、ひなたに(心を?)救われてからというもの、その成長は目を見張るものがある。 このマンガに悪役ってほとんどいないんだけど、数少ない悪役・捨男(仮名)と舌戦を繰り広げる姿は頼もしい限りで、ここまでに成長するとは。

個人的なお気に入りは二階堂。 めっちゃ熱くていいヤツだった。 ライバルというより強敵とかいてトモと呼ぶタイプ。むしろ親友。 モモに並ぶ癒しキャラでもあると思ってる。 モモとのコンビは見ててホッコリする。

将棋の知識が無くても楽しめるマンガになっていた。 ヤングアニマルで連載中なので、連載の方も読んでみようかな。 ただ、ヤングアニマルはちと購入のハードルが高めの雑誌だ。 モーニングやアフタヌーンだったらなぁ。 13巻が待ち遠しい。

S3 からファイルをフォルダごとダウンロードする方法のメモ

S3 Management Console では、フォルダごとダウンロードできなかった。 2017/04/10 現在でも、まだ AWS CLI を使うしかない。

aws s3 cp --region ap-northeast-1 s3://バケット名/パス ダウンロード先 --recursive

指定した URL に付けられたすべてのブックマークを取得する

はてなブックマークネタ第3弾。 最後は、指定した URL に付けられたすべてのブックマークを取得してみた。

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace HatenaBookmarkSample
{
    class Program
    {
        static void Main(string[] args)
        {
            var bookmarks = GetAllBookmarks("http://hatenablog.com")
                .GetAwaiter().GetResult();

            foreach(var bookmark in bookmarks.Bookmarks)
            {
                Console.WriteLine($"{bookmark.User} : {bookmark.Comment}");
            }

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

        static async Task<EntryBookmarks> GetPopularBookmarks(string url)
        {
            var httpClient = new HttpClient();

            httpClient.DefaultRequestHeaders
                .Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

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

            var requestUri = $"http://b.hatena.ne.jp/api/viewer.popular_bookmarks?url={url}";

            var json = await httpClient.GetStringAsync(requestUri);

            var bookmarks = JsonConvert.DeserializeObject<EntryBookmarks>(json);

            return bookmarks;
        }

        static async Task<EntryBookmarks> GetAllBookmarks(string url)
        {
            var httpClient = new HttpClient();

            httpClient.DefaultRequestHeaders
                .Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

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

            var requestUri = $"http://b.hatena.ne.jp/entry/jsonlite/?url={url}";

            var json = await httpClient.GetStringAsync(requestUri);

            var bookmarks = JsonConvert.DeserializeObject<EntryBookmarks>(json);

            return bookmarks;
        }
    }

    [JsonObject]
    public class EntryBookmark
    {
        /// <summary>
        /// ブックマーク ID を取得または設定します。
        /// </summary>
        [JsonProperty("eid")]
        public string Id { get; set; }

        /// <summary>
        /// タイトルを取得または設定します。
        /// </summary>
        [JsonProperty("title")]
        public string Title { get; set; }

        /// <summary>
        /// 記事の URL を取得または設定します。
        /// </summary>
        [JsonProperty("url")]
        public string Url { get; set; }

        /// <summary>
        /// ブックマークしたユーザーを取得または設定します。
        /// </summary>
        [JsonProperty("user")]
        public string User { get; set; }

        /// <summary>
        /// ブックマークコメントを取得または設定します。
        /// </summary>
        [JsonProperty("comment")]
        public string Comment { get; set; }

        /// <summary>
        /// ブックマークした時間を取得または設定します。
        /// </summary>
        [JsonProperty("timestamp")]
        public string Timestamp { get; set; }
    }

    [JsonObject]
    public class EntryBookmarks
    {
        /// <summary>
        /// ブックマーク件数を取得または設定します。
        /// </summary>
        [JsonProperty("count")]
        public int Count { get; set; }

        /// <summary>
        /// エントリの ID を取得または設定します。
        /// </summary>
        [JsonProperty("eid")]
        public string EntryId { get; set; }

        /// <summary>
        /// エントリのタイトルを取得または設定します。
        /// </summary>
        [JsonProperty("title")]
        public string Title { get; set; }

        /// <summary>
        /// エントリの URL を取得または設定します。
        /// </summary>
        [JsonProperty("entry_url")]
        public string EntryUrl { get; set; }

        /// <summary>
        /// ブックマークの URL を取得または設定します。
        /// </summary>
        [JsonProperty("url")]
        public string Url { get; set; }

        /// <summary>
        /// ブックマーク一覧を取得または設定します。
        /// </summary>
        [JsonProperty("bookmarks")]
        public List<EntryBookmark> Bookmarks { get; set; } = new List<EntryBookmark>();
    }
}

実行結果はこちら。

f:id:griefworker:20170405172557p:plain

はてなブックマークが提供する API は、REST APIAtom API や件数取得 API などなど、 たくさんあって分かりにくい。 REST API に一本化して、機能を充実してほしいところだ。

指定した URL の人気ブックマーク一覧を取得する

はてなブックマークネタ第2弾。 今度は、指定した URL に付けられたブックマークのうち、 人気のヤツを取得してみた。

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace HatenaBookmarkSample
{
    class Program
    {
        static void Main(string[] args)
        {
            var bookmarks = GetPopularBookmarks("http://hatenablog.com")
                .GetAwaiter().GetResult();

            foreach(var bookmark in bookmarks.Bookmarks)
            {
                Console.WriteLine($"{bookmark.User} : {bookmark.Comment}");
            }

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

        static async Task<EntryBookmarks> GetPopularBookmarks(string url)
        {
            var httpClient = new HttpClient();

            httpClient.DefaultRequestHeaders
                .Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

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

            var requestUri = $"http://b.hatena.ne.jp/api/viewer.popular_bookmarks?url={url}";

            var json = await httpClient.GetStringAsync(requestUri);

            var bookmarks = JsonConvert.DeserializeObject<EntryBookmarks>(json);

            return bookmarks;
        }
    }

    [JsonObject]
    public class EntryBookmark
    {
        /// <summary>
        /// ブックマーク ID を取得または設定します。
        /// </summary>
        [JsonProperty("eid")]
        public string Id { get; set; }

        /// <summary>
        /// タイトルを取得または設定します。
        /// </summary>
        [JsonProperty("title")]
        public string Title { get; set; }

        /// <summary>
        /// 記事の URL を取得または設定します。
        /// </summary>
        [JsonProperty("url")]
        public string Url { get; set; }

        /// <summary>
        /// ブックマークしたユーザーを取得または設定します。
        /// </summary>
        [JsonProperty("user")]
        public string User { get; set; }

        /// <summary>
        /// ブックマークコメントを取得または設定します。
        /// </summary>
        [JsonProperty("comment")]
        public string Comment { get; set; }

        /// <summary>
        /// ブックマークした時間を取得または設定します。
        /// </summary>
        [JsonProperty("timestamp")]
        public string Timestamp { get; set; }
    }

    [JsonObject]
    public class EntryBookmarks
    {
        /// <summary>
        /// ブックマーク件数を取得または設定します。
        /// </summary>
        [JsonProperty("count")]
        public int Count { get; set; }

        /// <summary>
        /// エントリの ID を取得または設定します。
        /// </summary>
        [JsonProperty("eid")]
        public string EntryId { get; set; }

        /// <summary>
        /// エントリのタイトルを取得または設定します。
        /// </summary>
        [JsonProperty("title")]
        public string Title { get; set; }

        /// <summary>
        /// エントリの URL を取得または設定します。
        /// </summary>
        [JsonProperty("entry_url")]
        public string EntryUrl { get; set; }

        /// <summary>
        /// ブックマークの URL を取得または設定します。
        /// </summary>
        [JsonProperty("url")]
        public string Url { get; set; }

        /// <summary>
        /// ブックマーク一覧を取得または設定します。
        /// </summary>
        [JsonProperty("bookmarks")]
        public List<EntryBookmark> Bookmarks { get; set; } = new List<EntryBookmark>();
    }
}

実行結果はこちら。

f:id:griefworker:20170405164319p:plain