40
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

40
Posted at

Qiitaアドベントカレンダーには、ひとりで25記事以上を投稿すると賞品をもらえる完走賞があります。

02.png

中でも一人でひとつのアドベントカレンダーを埋め尽くす、ひとりアドベントカレンダーを走っている人が毎年散見されます。
2025年には他者が参加することのできない専用のカレンダーまで用意されていました。

ということで、これは何名がひとりアドベントカレンダーに挑戦し、そのうちどれほどが完走したかを調べてみたという嫌がらせ記事になります。

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

調査結果

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

うち、参加者がひとりのカレンダー、550件。
うち、完走数、165件。
うち、ぬいぐるみGET、141件・118名。

完走率は30%でした。
2023年が30.6%、2024年が30.2%と、だいたい3割で安定していますね。
完走数は昨年の122件からさらに増えて165件になりました。
順調に規模が拡大しつつありますね。

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

ロジック

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

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

HTMLが自動生成なせいでclass="style-1dw8kp9"みたいな無機質なCSS名となっており抽出が非常につらいことになっています。
ちゃんと意味のある名前にしてくれ。

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

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

・アイレット株式会社 by @personal_linyang
・アイレット株式会社 by @na-tamura
・ひとりアドカレ | 2025に学んだことの振り返り by @benjuwan
・凡庸なシニアフロントエンドエンジニアが考えていること by @h-kinocoboy
・Rails基礎固め25のTips by @gon0821
・社内ソフトウェア開発 by @u1murayama
・React HooksをHackしよう! by @JavaLangRuntimeException
・「リクエスト&レスポンス理解シリーズ」 by @chdkm
・picoCTF by @OzakiRoy
・初アドカレにひとりで挑戦🎄🦌🎁 by @Ryuki4648
・プロダクトエンジニアへの道 by @saka2jp
・fiord by @fiord
・初挑戦カレンダー by @silvia_hacks
・IoTっぽいこと by @gzock
・ふぐおの配信関係多めひとり by @boxfish_jp
・わかっている「つもり」を叩き直す by @mattsu_mocha
・ひとりでつくるSaaS - 設計・実装・運用の記録 by @pipipi-dev
・アセンブラ by @ohisama@github
・アセンブラ by @ohisama@github
・個人 by @lamela
・Hugo+BlowfishでWebサイトを作ってみた。ひとり by @dokokanoradio
・GLSL by @ohisama@github
・2025年の技術的な取り組みまとめ by @noissefnoc
・Structive by @mogera551
・はじめてのひとり by @omunomi
・ワイの最強セルフホスティング by @sinsky
・【リアルタイム電力予測】需要・価格・電源最適化ダッシュボード構築記 by @nakamin
・七夕凛 by @ysmreg1
・七夕凛 by @ysmreg1
・七夕凛 by @ysmreg1
・七夕凛 by @ysmreg1
・ゆっくりテックウォッチ by @sumihiro3
・クリスマスに✝️俺の考えた最強のCMS✝️をつくる by @yuriacats
・Wanna be a GameCreator by @darknes_henohenon
・My Engineer Journey by @future_kame
・使っているクラウドなどの小ネタ Advent Calendar 2025 by @Keiji_otsubo
・Gforth by @ohisama@github
・【完走チャレンジ】2025年わたしが学んだことまとめ by @Maruhoppe8
・『IBM愛』で綴る IBM i アドベントカレンダー by @yanagih
・DF Yamashita by @yam_dev
・ひとりアドカレbyタカハシ by @tttol777
・ゼロから始めるAI駆動開発+Claude Code by @rf_p
・HonoのマージされたPRを読んでいく by @MIDO-ruby7
・技術書しっかりやり込もう2025 by @tatataichi
・手探りしてみる CV/ ML/ NN by @masahiroteraoka
・AI Coding Challenge by @zzzzico
・25日間でCocos Creatorでゲームを作る by @riku_pa
・Tech888’s Calendar by @tech888
・インフォマティカ・ジャパン株式会社 by @shunsuzu
・K4e Linux Distros by @Kanade147359
・AWS初歩ミス図鑑 by @ei_540
・anyプロダクトチーム by @ad-sho-loko
・25日間でC言語をマスターしよう by @edamame2525
・ひとりアドカレ@yS by @yshimizu
・25日で分かるAIの基礎 by @pandausa
・オレオレ開発環境をシェアして、自分の派閥を広げよう by @nattyan_tv
・yua by @sasachi
・【Laravel + Inertia + Vue】VILT Stackを流行らせる by @sigeta
・mqtt by @heyanaohiro
・やきとりの一人アドカレ by @IMattheZOO
・科学と神々株式会社 by @jasagiri
・科学と神々株式会社 by @jasagiri
・科学と神々株式会社 by @jasagiri
・科学と神々株式会社 by @jasagiri
・科学と神々株式会社 by @jasagiri
・2025 年の個人的な登壇+αを振り返る by @hmatsu47
・初参戦完走 by @38MIYA
・AWS 無料枠使い倒し by @ShingoKamiya
・競技プログラミングに関する知見をシェアしよう|2025 by @SHori358Plus
・ぼっち論文サーベイメモ2025 by @matumu20
・techno-robocup by @rotarymars
・今年のQiitanはスヤスヤver!?Qiitanは何個あっても欲しくなる by @tanimoto-hikari
・Unityでつくる2DRPG by @ShrineGate3544
・Daily CTF Upsolve Reversing Edition by @colza_
・2025年の振り返り by @chi1180
・あくあ by @Aqua-218
・あくあ by @Aqua-218
・あくあ by @Aqua-218
・あくあ by @Aqua-218
・あくあ by @Aqua-218
・あくあ by @Aqua-218
・AWS Lambda 実践入門 by @mamono210
・マルチテナントSaaSにおけるエンジニアリング大全 by @ad-sho-loko
・がちもとさん by @SatoshiGachiFujimoto
・Daily Crypto CTF by @potyamaaaa
・めんどい太郎の by @mendoitarou_
・ひとりCloudflareを使い倒す by @toreis
・miriwoお一人様 by @miriwo
・ラクスパートナーズ by @H-Iida
・ラクスパートナーズ by @mzuk
・AWS Community Buildersになるために書きまくる by @yama3133
・Direct3D Basic Programming in DirectXTK/XTK12 by @YKVKDX12
・kazuedaの活動記録2025 by @kazueda
・Rustでずんだもんが代理でしゃべるSIP電話を作る by @Massy0127
・ぺんぎん by @penguin425
・今年こそQiitanを貰う by @mementotacopi
・Python×Plotly 3Dチャレンジ(高橋のカレンダー) by @Tadataka_Takahashi
・学生ひよこ界隈が送るGo/Javaで実現する「はじめてのバックエンド」 by @mamenz752
・EDPC by @kaz_tsu
・Rust+SvelteKit+CDKでRSS要約アプリを作ってみる by @k4nd4
・秘密じゃない花園 by @EgaSaQA
・IBMが提供する製品に関する情報やナレッジを共有する_第2弾 by @yanagih
・真のエンジニアを目指して2025 by @devmatsuko
・私がこっそり教える「見てよかった」リスト by @itaya
・数学アートギャラリーをつくっていく by @cocoakamen
・やくも by @yakumo_09
・Atsushi's by @AS_atsushi
・鎮宮's by @sigure_sizumiya
・すやすやverが欲しいよ by @omu58n
・パイソニスタの一人アドカレ by @Pythonista_
・生成AIセキュリティ by ナレコム by @akiraokusawa
・keitaMax by @keitaMax
・Elixir by @the_haigo
・FORTH ベーシックマスター by @spumoni
・LISP ベーシックマスター by @spumoni
・kkfactory by @koji0705
・技術同人誌執筆の軌跡 by @Kerdy
・claustra01's Daily CTF by @claustra01
・日本語プログラミング仕様記述言語 Re:Mind(リマインド) by @mylifewithviolin
・【個人用】競プロ関係+α by @comet725
・生成AIで遊びながら学ぶ by @nao_manabitan
・デジタル創作サークルUniProject by @Aqua-218
・デジタル創作サークルUniProject by @ysmreg1
・デジタル創作サークルUniProject by @Aqua-218
・新社会人のSalesforce学習記録とその他 by @Aruhimanahito
・俺の2025年 by @nomurasan
・STM32×AIで「3軸倒立振子」を作る25日間(ひとりアドカレ) by @RamTuckey
・Qiita株式会社 by @degudegu2510
・ひとりアドベント(Affinity) by @harycurl
・ひとりアドベント(Affinity) by @harycurl
・Jibakure by @Jibakure
・AtCoderでプログラミング初心者にPythonを教える by @azubiwa
・Rosetta Code で楽しむプログラミング by @gotoki_no_joe
・3 年で飽きる by @e99h2121
・エンジニアのながらキャッチアップ by @ennagara128
・ZOZO by @shiozaki
・ktrkが頑張る by @ktrk2002
・ハウインターナショナル by @torifukukaiou
・中部大学 新谷研究室 Advent Calendar 2025:サルコメア動態(HSOs/S4C)と Chaordic Homeodynamics by @Seine_A_Shintani
・中部大学 新谷研究室 Advent Calendar 2025:サルコメア動態(HSOs/S4C)と Chaordic Homeodynamics by @Seine_A_Shintani
・hooq by @namn1125

ただし『最終的な達成者は、コミュニティガイドラインに則していない記事が含まれていないか、運営が投稿内容を確認・審査の上で選定いたします』という条文があるので、中身のない記事しか書いていないユーザは弾かれる可能性があります。

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

感想

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

今回は一人で何シリーズも走っている人が目立ちますね。
どれだけ書きたいことがあるというのだ。

プログラム

一回動けばそれでいいの精神で作られています。
とか言いつつ3年使い回してますけどね。

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

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

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

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

        // カレンダーへのリンクを取得
        $pattern = '|<a aria-label="Open calendar" href="(.*?)" tabindex=|us';
        
        preg_match_all($pattern, $html, $matches);
        foreach ($matches[1] as $tmp) {
            // ページを取得
            $url = 'https://qiita.com' . $tmp;
            echo 'Downloading ' . $url . "\n";
            $html = file_get_contents($url);
            // ローカルに保存
            file_put_contents('./calendar/' . basename($tmp) . '.html', $html);
            sleep(1);
        }
    }
    private $cnt = 1;

    /**
     * 新着カレンダーを全取得してローカル保存する
     */
    public function getCalendarsList()
    {
        // ページ数は直接確認したので手抜き
        $urlp = 'https://qiita.com/advent-calendar/2025/calendars?page=';
        $page = 0;
        while ($page++ < 55) {
            // ページを取得
            $url = $urlp . $page;
            $html = file_get_contents($url);
            // ローカルに保存
            file_put_contents('./list/page' . $page . '.html', $html);
        }
    }
}

中身を分析する。

(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 2025|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">(.*?)</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/2025/' . basename($url, '.html');
            $ret[] = $tmp;
        }

        return $ret;
    }

    /**
     * シリーズごとに判定
     * @param  string $calendar カレンダーのHTML
     * @return array  [user, complete, qiita]
     */
    private function check($calendar)
    {
        $ret = [
            'user' => null,
            'only' => true,     // 一人か
            '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'] = $ret['only'] = false;
                }
            }

            // 記事リンク
            $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;
    }
}

正規表現パターンをCopilotに書いてもらおうとしたけどてんで駄目だった。

40
2
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
40
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?