【Qiita】ひとりアドベントカレンダー2024年の完走率を調査してみたよ

[Qiitaアドベントカレンダー](https://qiita.com/advent-calendar/2024)には、ひとりで25記事以上を投稿すると賞品をもらえる[完走賞](https://qiita.com/advent-calendar/2024/present-calendar#qiita_award)があります。

![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26088/791d7f2a-4c8e-bdd9-300b-c10e6a844253.png)


中でも一人でひとつのアドベントカレンダーを埋め尽くす、ひとりアドベントカレンダーを走っている人が毎年散見されます。
ということで、これは何名がひとりアドベントカレンダーに挑戦し、そのうちどれほどが完走したかを調べてみたという嫌がらせ記事になります。

調査のためのデータ抽出を行ったのは2024/12/30です。


# 調査結果

全アドベントカレンダー数、1314件。

うち、参加者がひとりのカレンダー、402件。
うち、完走数、122件。
うち、完走賞ぬいぐるみGET、98件・98名。

完走率は30.2%でした。
[昨年](https://qiita.com/rana_kualu/items/755240df5a4d456edc70)の30.6%とほぼ同率です。
しかし完走者数は98名から122名へ、ぬいぐるみゲット者も77名から98名へと増加しています。
前回より参加者が増えているということですばらしいことですね。

ぬいぐるみはQiita内に記事を投稿していないといけないため、外部サイトに投稿しているひとはもらえません。
最終的にぬいぐるみをGETできる人は98名となりました。


# ロジック

アドベントカレンダーを全て拾い出す → 走者が一人しかいないアドベントカレンダーを抽出する → 全日程にリンクがあれば完走、という単純なロジックです。

ただソースコードがとっても酷いので抽出が面倒でした。
はっきりいってHTMLがぐちゃぐちゃです。
なーにが最新ライブラリで綺麗なコードじゃ。
HTMLも綺麗にしろ。

プログラム自体は最後に載せておきます。

# ぬいぐるみGETカレンダー一覧

以下の98カレンダー、98名です。

・[ひとりアドカレ@yshimizu](https://qiita.com/advent-calendar/2024/adventcalendar_yshimizu) by @yshimizu
・[SVG](https://qiita.com/advent-calendar/2024/svg) by @Tadataka_Takahashi
・[はな丸&#39;s QA](https://qiita.com/advent-calendar/2024/hanamaruqa) by @EgaSaQA
・[protobuf.devのAPI Best Practicesを読んでいく全部俺](https://qiita.com/advent-calendar/2024/protobuf_api-best-practices) by @bushiyama
・[Sequence Maker](https://qiita.com/advent-calendar/2024/sequencemaker) by @sequencemaker
・[ひとりJavaScript](https://qiita.com/advent-calendar/2024/nanasi1-javascript) by @nanasi-1
・[適応型ソフトウェア開発](https://qiita.com/advent-calendar/2024/adaptive-software-development) by @h-kinocoboy
・[sphero boltで遊ぶ](https://qiita.com/advent-calendar/2024/sphero-bolt) by @nakmura
・[コンピューターシステム株式会社](https://qiita.com/advent-calendar/2024/jcsc) by @nimzo6689
・[manabian](https://qiita.com/advent-calendar/2024/manabian) by @manabian
・[ひとりアドベントカレンダー WSLで学ぶLINUXコマンド](https://qiita.com/advent-calendar/2024/pig) by @yu1016
・[ Hamster Output](https://qiita.com/advent-calendar/2024/game_hamster) by @game_hamster
・[競プロ用ライブラリを作る](https://qiita.com/advent-calendar/2024/yukkku-ribitol) by @Yukkku
・[新人ウェブエンジニア向けセキュリティー教育](https://qiita.com/advent-calendar/2024/security-training) by @mamono210
・[ひとりアドベントカレンダー 初めてのチャレンジ編](https://qiita.com/advent-calendar/2024/mymt_aggw2208) by @Mymt_aggw2208
・[【完走チャレンジ】2024年わたしが学んだことまとめ](https://qiita.com/advent-calendar/2024/maruhoppe2024) by @Maruhoppe8
・[科学と神々株式会社](https://qiita.com/advent-calendar/2024/godsgoelminc) by @jasagiri
・[claustra01&#39;s Daily CTF](https://qiita.com/advent-calendar/2024/claustra01-dailyctf) by @claustra01
・[完走カレンダー](https://qiita.com/advent-calendar/2024/complete) by @chi1180
・[kazuedaの活動記録2024](https://qiita.com/advent-calendar/2024/kazueda_records_2024) by @kazueda
・[X のアルゴリズム・ルール・専門用語について](https://qiita.com/advent-calendar/2024/x-tips) by @haihaikazuma
・[えーえす](https://qiita.com/advent-calendar/2024/as) by @AS_atsushi
・[Gakken LEAP](https://qiita.com/advent-calendar/2024/gleap) by @GleapPost
・[ex-crowdworks](https://qiita.com/advent-calendar/2024/ex-cw) by @nisyuu
・[RCC (立命館コンピュータークラブ)](https://qiita.com/advent-calendar/2024/rits_rcc) by @bearl27
・[RCC (立命館コンピュータークラブ)](https://qiita.com/advent-calendar/2024/rits_rcc) by @JavaLangRuntimeException
・[RCC (立命館コンピュータークラブ)](https://qiita.com/advent-calendar/2024/rits_rcc) by @anapausis
・[RCC (立命館コンピュータークラブ)](https://qiita.com/advent-calendar/2024/rits_rcc) by @maooz4426
・[ひとりアドベントカレンダーチャレンジ](https://qiita.com/advent-calendar/2024/hikagami) by @hikagami
・[ひとりアドカレ_私の本棚から、「思考法」や「プロジェクト管理系」](https://qiita.com/advent-calendar/2024/akm2024_best25books) by @AkmIOtUE4
・[Cloud Run functions × TypeScript 開発をしよう！](https://qiita.com/advent-calendar/2024/cloud-run-functions-typescript) by @KyongminS
・[使っているクラウドなどの小ネタ](https://qiita.com/advent-calendar/2024/keiji_otsubo) by @Keiji_otsubo
・[Java](https://qiita.com/advent-calendar/2024/java) by @_mi
・[Yamashita](https://qiita.com/advent-calendar/2024/yamashita) by @yam_dev
・[TypeScriptで学べるデザインパターン](https://qiita.com/advent-calendar/2024/typescript-design-patterns) by @hato_code
・[【ひとりカレンダー】Clojure](https://qiita.com/advent-calendar/2024/clojure) by @maaaashi
・[keitamax](https://qiita.com/advent-calendar/2024/keitamax) by @keitaMax
・[LINEミニアプリ](https://qiita.com/advent-calendar/2024/line-mini-app) by @non_watanabe
・[ひとりアドベントカレンダー](https://qiita.com/advent-calendar/2024/tamutamuta) by @tamutamuta
・[脱After Effects](https://qiita.com/advent-calendar/2024/no-more-ae) by @potistudio
・[フロントエンドの世界](https://qiita.com/advent-calendar/2024/frontend-assort-2024) by @nuinteedev
・[一歩ずつRustに慣れていくTypeScriptエンジニアの記録](https://qiita.com/advent-calendar/2024/rust-from-ts) by @k4nd4
・[Rickyアドカレ2024](https://qiita.com/advent-calendar/2024/ricky) by @akiraarika932
・[1人フロントエンド](https://qiita.com/advent-calendar/2024/k8o-frontend) by @KokiSakano
・[FoundingBase](https://qiita.com/advent-calendar/2024/foundingbase) by @mziyut
・[米田の補題](https://qiita.com/advent-calendar/2024/yonedalemma) by @zanjibar
・[インフォマティカ・ジャパン株式会社](https://qiita.com/advent-calendar/2024/informatica) by @shunsuzu
・[JavaとPythonを基礎から学びたい私のための](https://qiita.com/advent-calendar/2024/aruhitonocalendar) by @Aruhimanahito
・[一年を振り返る](https://qiita.com/advent-calendar/2024/kwtc001) by @kwtc_
・[🦀ひとりRustとBevyでゲーム開発🕊️](https://qiita.com/advent-calendar/2024/rust-bevy) by @hiruberuto
・[kaggle入門](https://qiita.com/advent-calendar/2024/kaggle_learn) by @leafeon00000
・[一人アドベントカレンダー！主にPythonに関して投稿します。](https://qiita.com/advent-calendar/2024/saka) by @shun_sakamoto
・[俺の2024年](https://qiita.com/advent-calendar/2024/nomuraya_oreno) by @nomurasan
・[個人的アウトプットカレンダー](https://qiita.com/advent-calendar/2024/kenny-m-output) by @kenny-m
・[技術書一冊やり込もう](https://qiita.com/advent-calendar/2024/tech-book) by @tatataichi
・[なんかかく](https://qiita.com/advent-calendar/2024/pineapple) by @e99h2121
・[ひとりアドベントカレンダー by Mas](https://qiita.com/advent-calendar/2024/massy_0127) by @Massy0127
・[ぼっちアドベントカレンダー by bon10](https://qiita.com/advent-calendar/2024/only-at-bon10) by @bon10
・[真のエンジニアを目指して](https://qiita.com/advent-calendar/2024/devmatsuko) by @devmatsuko
・[Goで学ぶコンピューターサイエンス](https://qiita.com/advent-calendar/2024/go-cs) by @engineer_tacky
・[Python(Django) x AWS 一人アドベントカレンダー](https://qiita.com/advent-calendar/2024/citrus) by @Ryo-0131
・[セキュリティごった煮一人完走チャレンジ](https://qiita.com/advent-calendar/2024/sigma_security) by @sigma_devsecops
・[Machiken’s](https://qiita.com/advent-calendar/2024/machiken) by @ken-1200
・[ひとりアドベントカレンダー](https://qiita.com/advent-calendar/2024/makenai-ml-2024) by @ei_540
・[転機となった一年の出来事を振り返る](https://qiita.com/advent-calendar/2024/does_not_exist) by @does_not_exist
・[Tauriでエンジンからゲームを作ってみる](https://qiita.com/advent-calendar/2024/tauri-engine-game) by @nattyan_tv
・[Delphi](https://qiita.com/advent-calendar/2024/delphi) by @ht_deko
・[ひとりアドカレ2024 byもんすん](https://qiita.com/advent-calendar/2024/phibi-soon) by @phibi-soon
・[nem / symbol](https://qiita.com/advent-calendar/2024/nem) by @ccHarvestasya
・[今年作った勉強動画🕊！＠飛鷹しずか](https://qiita.com/advent-calendar/2024/shizuka-hidaka) by @ShizukaHidaka
・[ただただアウトプットを癖付けるための](https://qiita.com/advent-calendar/2024/newbie2024) by @yotabouya
・[ZOZO](https://qiita.com/advent-calendar/2024/zozo) by @shiozaki
・[k.k.Factory](https://qiita.com/advent-calendar/2024/kkfactory) by @koji0705
・[Power Query へそのゴマ](https://qiita.com/advent-calendar/2024/pqsesame) by @spumoni
・[Elixir](https://qiita.com/advent-calendar/2024/elixir) by @Yoosuke
・[ひとりアドベント（グラフ電卓，関数電卓，Python）](https://qiita.com/advent-calendar/2024/hary_calc_py) by @harycurl
・[kaggle初心者に送る](https://qiita.com/advent-calendar/2024/kagglenoob) by @v_one5
・[Basic Study Log](https://qiita.com/advent-calendar/2024/basic-study-log) by @mattsu_mocha
・[アウトプットアドカレ2024](https://qiita.com/advent-calendar/2024/outadvcal-2024) by @ysit
・[文章題で身に付くプログラミング思考入門️ by TheWaggle](https://qiita.com/advent-calendar/2024/thewaggle) by @yuki_thewaggle
・[まっさらな新人研修コーチ](https://qiita.com/advent-calendar/2024/zero-to-coach) by @uekkie
・[Webフロントエンド](https://qiita.com/advent-calendar/2024/axoloto210) by @axoloto210
・[一人ゲーム開発Tips](https://qiita.com/advent-calendar/2024/loneliness-game-knowledges) by @4_mio_11
・[競技プログラミングなんでも](https://qiita.com/advent-calendar/2024/any-competitive-programming) by @comet725
・[技術記事未経験で個人完走を目指す](https://qiita.com/advent-calendar/2024/first-challenge) by @thule
・[ilasm](https://qiita.com/advent-calendar/2024/ilasm) by @ohisama@github
・[My Infrastructure Journey](https://qiita.com/advent-calendar/2024/my-infrastructure-journey) by @future_kame
・[Qiitanがほしい人の一人アドカレ](https://qiita.com/advent-calendar/2024/nikawamikan) by @nikawamikan
・[個人的アドカレ](https://qiita.com/advent-calendar/2024/enumura-2024) by @enumura1
・[Rails学習者向けのLaravel](https://qiita.com/advent-calendar/2024/lololol) by @chdkm
・[Java Silver確実合格を目指すカレンダー](https://qiita.com/advent-calendar/2024/javasilvercalendar) by @pandausa
・[ひとりで完走_C# is GOD](https://qiita.com/advent-calendar/2024/c-sharp_is_god) by @C-Sharp_is_GOD
・[API Lab for LINE WORKS](https://qiita.com/advent-calendar/2024/apilablw) by @iwaohig
・[一人アドカレ](https://qiita.com/advent-calendar/2024/kk) by @kk0128
・[kurogoma939のひとりアドベントカレンダー](https://qiita.com/advent-calendar/2024/kurogoma939) by @kurogoma939
・[個人的なまとめ](https://qiita.com/advent-calendar/2024/0_terarin_0-individualrun-24) by @0_terarin_0
・[midPoint by OpenStandia](https://qiita.com/advent-calendar/2024/midpoint-by-openstandia) by @wadahiro
・[ぼっち論文サーベイメモ2 ](https://qiita.com/advent-calendar/2024/bocchi_papersurveymemo_2024) by @matumu20

ただし[『最終的な達成者は、コミュニティガイドラインに則していない記事か運営が内容確認・審査をした上で選定いたします』](https://blog.qiita.com/adventcalendar-2024-qiitapresents/)という条文があるので、中身のないユーザは弾かれる可能性があります。

また、正確なぬいぐるみゲット条件は『Qiita記事に1人で25記事投稿しきった方』なので、複数のアドベントカレンダーで合わせて25記事投稿した場合も対象となります。
その対象者は、このリストには含まれていません。
全リストはあるので単純に合計すれば簡単に出せるのですが、面倒なのでまあいいや。

# 感想

完走者のみなさんおめでとうございます。

約一ヶ月毎日投稿し続けるという、相当な情熱がないとやりきれないことをやってのけた人がこれだけいるということは驚きですね。
彼らはきっとこれからも活躍してくれることでしょう、期待しましょう。

さて手元には三日坊主の人のリストとか、アドカレを作るだけ作ったのに1件すらも投稿がない人のリストなんかもあったりするわけですが、はてさてどうしたものかな。


# プログラム

一回動けばそれでいいの精神で作られています。

まずアドベントカレンダーをぜんぶダウンロードしてローカルに保存する。

```php
(new A())->run();
class A
{
    public function run()
    {
        // 新着カレンダーを全取得
        $this->getCalendarsList();

        // 新着カレンダーを順に処理
        $files = glob('./list/*');
        foreach ($files as $file) {
            $this->getCalendar($file);
        }
    }

    /**
     * 新着カレンダーページから個々のカレンダーを取得してローカル保存する
     * @param string $file ファイル名
     */
    public function getCalendar(string $file)
    {
        $html = file_get_contents($file);

        // カレンダーへのリンクを取得
        $pattern = '|<li class="style-1a7kcgn"><div class="style-11zreon"><div><div class="style-1xn5a1f"><a href="(.*?)" class|us';
        preg_match_all($pattern, $html, $matches1);
        foreach ($matches1[1] as $tmp) {
            // ページを取得
            $url = 'https://qiita.com' . $tmp;
            $html = file_get_contents($url);
            // ローカルに保存
            file_put_contents('./calendar/' . basename($tmp) . '.html', $html);
            sleep(1);
        }
    }

    /**
     * 新着カレンダーを全取得してローカル保存する
     */
    public function getCalendarsList()
    {
        /*
            1-52ページまで取得
            ページ数はわかっているので手抜き。
        */
        $urlp = 'https://qiita.com/advent-calendar/2024/calendars?page=';
        $page = 0;
        while ($page++ < 52) {
            // ページを取得
            $url = $urlp . $page;
            $html = file_get_contents($url);
            // ローカルに保存
            file_put_contents('./list/page' . $page . '.html', $html);
        }
    }
}
```

ローカルファイルを読み込んで分析する。

```php
(new A())->run();
class A
{
    public function run()
    {
        $output = [];
        // ファイル
        $files = glob('./calendar/*');

        // カレンダーごとに中身を分析
        foreach ($files as $file) {
            echo 'Analyzing ' . $file . "\n";
            $html = file_get_contents($file);
            $url = substr($file, 11);
            $output[] = $this->analyze($url, $html);
        }
        // 終了
        file_put_contents(
            './analyse.json',
            json_encode(
                $output,
                \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE
            )
        );
    }

    /**
     * HTMLを調べる
     * @param string $url  URL
     * @param string $html HTML
     */
    public function analyze($url, $html)
    {
        $ret = [];
        // タイトル
        $pattern = '|<title>(.*?) - Qiita Advent Calendar 2024|us';
        preg_match($pattern, $html, $matches1);
        $title = $matches1[1];

        // 大ざっぱに切り取り
        $pattern = '|<section class="style-1covvrn">(.*)<section class="style-196d0fg"><div class="style-1i27l4i">|us';
        preg_match($pattern, $html, $matches1);
        $html = $matches1[1];

        // シリーズに分割
        $pattern = '|<section class="style-t7g594">(.*?)</div></div></dialog></div></li></ol></div></section>|us';
        preg_match_all($pattern, $html, $matches);

        // シリーズごとにループ
        foreach ($matches[1] as $k => $calendar) {
            // 判定
            $tmp = $this->check($calendar);
            $tmp['title'] = $title;
            $tmp['series'] = $k + 1;
            $tmp['url'] = 'https://qiita.com/advent-calendar/2024/' . basename($url, '.html');
            $ret[] = $tmp;
        }
        return $ret;
    }

    /**
     * シリーズごとに判定
     * @param  string $calendar カレンダーのHTML
     * @return array  [user, complete, qiita, count]
     */
    private function check($calendar)
    {
        $ret = [
            'user' => null,
            'complete' => true, // 完走
            'qiita' => true,    // Qiitaか
            'count' => 0,
        ];

        // 記事ごとに分割
        $pattern = '|<td class="style-1dw8kp9"><div class="style-1ssbn0c">(.*?)aria-labelledby="OtherCalendarInfo-label"|us';
        preg_match_all($pattern, $calendar, $posts);

        // 記事でループ
        foreach ($posts[1] as $post) {
            // ユーザ名
            $pattern = '|@<!-- -->(.*?)</a>|us';
            preg_match($pattern, $post, $matches);
            
            if (isset($matches[1])) {
                if ($ret['user'] === null) {
                    $ret['user'] = $matches[1];
                } elseif ($ret['user'] !== $matches[1]) {
                    $ret['complete'] = $ret['qiita'] = false;
                    return $ret;
                }
            }

            // 記事リンク
            $pattern = '|<div class="style-mpez5z"><a href="(.*?)"|us';
            preg_match($pattern, $post, $matches);
            if (!isset($matches[1])) {
                $ret['complete'] = $ret['qiita'] = false;
                return $ret;
            }

            // Qiita内か
            if (!str_starts_with($matches[1], 'https://qiita.com')) {
                $ret['qiita'] = false;
            }

            ++$ret['count'];
        }

        // 最後まで進んだ
        if ($ret['count'] < 25) {
            // 登録が足りない
            $ret['complete'] = $ret['qiita'] = false;
            return $ret;
        }
        return $ret;
    }
}
```

このクラス名とかさあ、ちゃんと意味のある名前にしてくれんかね。
ほんと開発者には優しいのかもしれないが利用者には優しくないフレームワークばっかりで困る。