画像RSS

2025年12月31日水曜日

禍話リライトまとめ、12月報: ジャンル分け、切抜き追加など

これまでの振り返り

現在、禍話リライトをまとめたページを作成しています。 現状、noteにあるリライトはほぼすべてまとめを終えているはずです。

最初、配信ごとにリライトをまとめたページを用意しました。

その後、特殊な形式でまとめたページも作成しました。

続いて、シリーズごとにまとめたページも作成しました。

ここまでが、過去の記事で紹介した内容です。以降は最近に追加した新規のまとめです。

FEAR飯関係者のまとめ

FEAR飯関係者に関するまとめを作成しました。FEAR飯の関係者が登場する話や、提供した話を個別にまとめたものです。

このまとめですが、かなり抜けが多いと思われます。抜けを見つけた際には指摘のコメントをいただけますと幸いです。 また、イソロクさん (五十六さん?) や介護職のIさん、アオさんについては存在は認識していますが、まとめに含めていません。どの話に登場するのか忘れてしまった、そもそも表記がよく分からないなどの理由があります。

話の提供者についてのまとめ

禍話ではFEAR飯の方々が自分で集めた話を語るだけでなく、視聴者がダイレクトメールを通じて話を送ることがあります。最近は「創作ホラー劇場 ミッドナイトギャラリー」という企画もあります。 そこで、分かる範囲で話の提供者ごとにまとめたページも作成しました。提供した話や関係する話を2話以上確認した方が対象です。

広義の「禍話関係者」ではありますが、先程のページには含めないことにします。酢豆腐さんは分類が難しいですが、ひとまずはこちらに入れています。

こちらも抜けが多いと思われます。ご指摘いただけますと幸いです。

note以外のリライトのまとめ

禍話リライトといえばnote。noteといえば禍話リライトとニンジャスレイヤー。

そう言っても過言ではないほどに禍話リライトはnoteに投稿されることが多いです。ただ、note以外にも少数ですがリライトは投稿されています。

このページではnote以外のウェブサイトで投稿されたリライトをまとめています。ただし、noteに同じ内容のリライトがある場合は対象外とします。

noteにないリライトは等閑視されがちなのではないか、という懸念からこのページを作成しました。むくろ幽介さんのリライトは多くの人が読んでいると思いますが、個人サイトに投稿されたものは見逃している人も多いのではないかと思います。

逆に言えば、このページに書かれていないものは私も見逃しています。抜けがあればご報告いただけますと幸いです。

ジャンルの分類

ジャンルごとにまとめたページも作成しました。怪談でありがちなジャンルごとに分類しています。

このページはリライトがある話を優先してまとめました。リライトがない話についても、徐々に整備していく予定です。

切抜き動画へのリンクを追加

禍話の配信の一部を切抜いた動画が投稿されています。そこで、切抜き動画へのリンクをまとめの各話に追加しました。 以下のチャンネルの切抜き動画を対象としています。同一の話の切抜き動画が複数存在する場合、上から順に優先して選択しています。

  1. FEAR飯
  2. 『禍話』切り抜きチャンネル【公認】
  3. 禍話の分家

以上です。来年も禍く過ごしましょう。

2025年12月14日日曜日

C#でnoteの検索結果を全て取得する

noteで「禍話」に関する記事を全て取得するためにC#でプログラムを作成しました。noteのAPIの扱い方の参考になるかと思い、汎用的になるように加工して公開します。

noteでは、次のURLから検索結果をJSON形式で取得できます。

https://note.com/api/v3/searches?context=note&q=(検索ワード)&sort=(ソート方法)&size=(結果の数)&start=(開始番号)

各種のパラメータの意味は以下の通りです。

q=(検索ワード)
検索したい言葉。URL用にエンコードが必要です。「禍話」を検索する場合は「%E7%A6%8D%E8%A9%B1」となります。
sort=(ソート方法)
検索結果のソート方法。新しい順であれば「new」と書きます。
size=(結果の数)
検索結果の取得数。最大値は20のはずです。
start=(開始番号)
検索結果全体のうち、どこから取得するか。

次のプログラムは、noteの検索結果をすべて取得し、テキストファイルに出力します。 短期間で連続してnoteのAPIを叩くと、noteのサーバから怒られてしまいます。そのため、時間はかかりますが、Thread.Sleepを挟む必要があります。


using System.IO;
using System.Net.Http;
using System.Text.Json;
using System.Web;

// 待機時間生成用
var random = new Random();

// HTTPクライアント
using var httpClient = new HttpClient();

// note検索の基準値
int start = 0;

// startは20ずつ増加する
const int START_STEP = 20;

// 検索ワード
string? searchQuery = HttpUtility.UrlEncode("検索ワード");

// 出力先
using var sw = new StreamWriter("output.txt");

while(true)
{
    // 検索APIを呼び出す
    string noteUrlText = $@"https://note.com/api/v3/searches?context=note&q={searchQuery}&sort=new&size=20&start={start}";
    HttpResponseMessage httpResponseMessage = await httpClient.GetAsync(noteUrlText);

    Console.WriteLine(noteUrlText);

    // ステータスコードを出力
    Console.WriteLine($"Status Code: {httpResponseMessage.StatusCode}");

    // JSONデータを取得
    string? jsonContent = await httpResponseMessage.Content.ReadAsStringAsync();

    // noteの検索結果を解析する
    JsonDocument searchResult = JsonDocument.Parse(jsonContent);

    // note_cursor
    int cursor = 0;

    // dataエレメントがあるか確認
    if (searchResult.RootElement.TryGetProperty("data", out JsonElement dataElement))
    {
        // note_cursorエレメントを確認
        if (dataElement.TryGetProperty("note_cursor", out JsonElement noteCursorElement))
        {
            if (Int32.TryParse(noteCursorElement.GetString(), out int cursorConv))
            {
                cursor = cursorConv;
            }
        }
        else
        {
            // note_cursorエレメント取得失敗時はエラーと見なし、中断
            Console.WriteLine("失敗");

            break;

        }

        Console.WriteLine($"cursor: {cursor}");

        // notesエレメントを確認
        if (dataElement.TryGetProperty("notes", out JsonElement notesElement))
        {
            // contentsエレメントを確認
            if (notesElement.TryGetProperty("contents", out JsonElement contentsElement))
            {
                for (int i = 0; i < (cursor - start); i++)
                {
                    JsonElement contentElementChild = contentsElement[i];
                    string? authorId = null;

                    // 題名を取得
                    if (contentElementChild.TryGetProperty("name", out JsonElement nameElement))
                    {
                        sw.WriteLine($"題名\t{nameElement}");
                    }

                    // 記事投稿日時を取得
                    if (contentElementChild.TryGetProperty("publish_at", out JsonElement publishAtElement))
                    {
                        if (publishAtElement.TryGetDateTimeOffset(out DateTimeOffset dateTime))
                        {
                            sw.WriteLine($"投稿日時\t{dateTime.ToString("yyyy-MM-ddTHH:mm:sszzz")}");
                        }
                    }

                    // userエレメントを取得
                    if (contentElementChild.TryGetProperty("user", out JsonElement userElement))
                    {
                        // ユーザ名を取得
                        if (userElement.TryGetProperty("nickname", out JsonElement nicknameElement))
                        {
                            sw.WriteLine($"作者名\t{nicknameElement.ToString()}");
                        }

                        // ユーザのnoteページを取得
                        if (userElement.TryGetProperty("urlname", out JsonElement urlNameElement))
                        {
                            sw.WriteLine($"作者noteページ\thttps://note.com/{urlNameElement}");
                            authorId = urlNameElement.ToString();
                        }
                    }

                    // note記事のURLを取得
                    if (contentElementChild.TryGetProperty("key", out JsonElement keyElement))
                    {
                        sw.WriteLine($"noteのURL\thttps://note.com/{authorId}/n/{keyElement}");
                    }
                }
            }
        }
    }

    // cursorはstartから20増加する値になるはず
    if (cursor >= (start + START_STEP))
    {
        // 次のstartは20増加
        start = start + START_STEP;
    }
    else
    {
        // 20増加した値にならなかった場合、この周回で検索を終える
        break;
    }

    // 短期間でのnoteへのアクセスを防止するため、ランダム時間だけ待機
    Thread.Sleep(random.Next(10000, 15000));

    sw.WriteLine();
}