37
7

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】ひとりアドベントカレンダー2024年の完走率を調査してみたよ

Last updated at Posted at 2024-12-30

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

Qiita ポエム PHP アドベントカレンダー アドベントカレンダー2024

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

01.png

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

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

調査結果

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

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

完走率は30.2%でした。
昨年の30.6%とほぼ同率です。
しかし完走者数は98名から122名へ、ぬいぐるみゲット者も77名から98名へと増加しています。
前回より参加者が増えているということですばらしいことですね。

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

ロジック

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

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

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

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

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

ひとりアドカレ@yshimizu by @yshimizu
SVG by @Tadataka_Takahashi
はな丸's QA by @EgaSaQA
protobuf.devのAPI Best Practicesを読んでいく全部俺 by @bushiyama
Sequence Maker by @sequencemaker
ひとりJavaScript by @nanasi-1
適応型ソフトウェア開発 by @h-kinocoboy
sphero boltで遊ぶ by @nakmura
コンピューターシステム株式会社 by @nimzo6689
manabian by @manabian
ひとりアドベントカレンダー WSLで学ぶLINUXコマンド by @yu1016
Hamster Output by @game_hamster
競プロ用ライブラリを作る by @Yukkku
新人ウェブエンジニア向けセキュリティー教育 by @mamono210
ひとりアドベントカレンダー 初めてのチャレンジ編 by @Mymt_aggw2208
【完走チャレンジ】2024年わたしが学んだことまとめ by @Maruhoppe8
科学と神々株式会社 by @jasagiri
claustra01's Daily CTF by @claustra01
完走カレンダー by @chi1180
kazuedaの活動記録2024 by @kazueda
X のアルゴリズム・ルール・専門用語について by @haihaikazuma
えーえす by @AS_atsushi
Gakken LEAP by @GleapPost
ex-crowdworks by @nisyuu
RCC (立命館コンピュータークラブ) by @bearl27
RCC (立命館コンピュータークラブ) by @JavaLangRuntimeException
RCC (立命館コンピュータークラブ) by @anapausis
RCC (立命館コンピュータークラブ) by @maooz4426
ひとりアドベントカレンダーチャレンジ by @hikagami
ひとりアドカレ_私の本棚から、「思考法」や「プロジェクト管理系」 by @AkmIOtUE4
Cloud Run functions × TypeScript 開発をしよう! by @KyongminS
使っているクラウドなどの小ネタ by @Keiji_otsubo
Java by @_mi
Yamashita by @yam_dev
TypeScriptで学べるデザインパターン by @hato_code
【ひとりカレンダー】Clojure by @maaaashi
keitamax by @keitaMax
LINEミニアプリ by @non_watanabe
ひとりアドベントカレンダー by @tamutamuta
脱After Effects by @potistudio
フロントエンドの世界 by @nuinteedev
一歩ずつRustに慣れていくTypeScriptエンジニアの記録 by @k4nd4
Rickyアドカレ2024 by @akiraarika932
1人フロントエンド by @KokiSakano
FoundingBase by @mziyut
米田の補題 by @zanjibar
インフォマティカ・ジャパン株式会社 by @shunsuzu
JavaとPythonを基礎から学びたい私のための by @Aruhimanahito
一年を振り返る by @kwtc_
🦀ひとりRustとBevyでゲーム開発🕊️ by @hiruberuto
kaggle入門 by @leafeon00000
一人アドベントカレンダー!主にPythonに関して投稿します。 by @shun_sakamoto
俺の2024年 by @nomurasan
個人的アウトプットカレンダー by @kenny-m
技術書一冊やり込もう by @tatataichi
なんかかく by @e99h2121
ひとりアドベントカレンダー by Mas by @Massy0127
ぼっちアドベントカレンダー by bon10 by @bon10
真のエンジニアを目指して by @devmatsuko
Goで学ぶコンピューターサイエンス by @engineer_tacky
Python(Django) x AWS 一人アドベントカレンダー by @Ryo-0131
セキュリティごった煮一人完走チャレンジ by @sigma_devsecops
Machiken’s by @ken-1200
ひとりアドベントカレンダー by @ei_540
転機となった一年の出来事を振り返る by @does_not_exist
Tauriでエンジンからゲームを作ってみる by @nattyan_tv
Delphi by @ht_deko
ひとりアドカレ2024 byもんすん by @phibi-soon
nem / symbol by @ccHarvestasya
今年作った勉強動画🕊!@飛鷹しずか by @ShizukaHidaka
ただただアウトプットを癖付けるための by @yotabouya
ZOZO by @shiozaki
k.k.Factory by @koji0705
Power Query へそのゴマ by @spumoni
Elixir by @Yoosuke
ひとりアドベント(グラフ電卓,関数電卓,Python) by @harycurl
kaggle初心者に送る by @v_one5
Basic Study Log by @mattsu_mocha
アウトプットアドカレ2024 by @ysit
文章題で身に付くプログラミング思考入門️ by TheWaggle by @yuki_thewaggle
まっさらな新人研修コーチ by @uekkie
Webフロントエンド by @axoloto210
一人ゲーム開発Tips by @4_mio_11
競技プログラミングなんでも by @comet725
技術記事未経験で個人完走を目指す by @thule
ilasm by @ohisama@github
My Infrastructure Journey by @future_kame
Qiitanがほしい人の一人アドカレ by @nikawamikan
個人的アドカレ by @enumura1
Rails学習者向けのLaravel by @chdkm
Java Silver確実合格を目指すカレンダー by @pandausa
ひとりで完走_C# is GOD by @C-Sharp_is_GOD
API Lab for LINE WORKS by @iwaohig
一人アドカレ by @kk0128
kurogoma939のひとりアドベントカレンダー by @kurogoma939
個人的なまとめ by @0_terarin_0
midPoint by OpenStandia by @wadahiro
ぼっち論文サーベイメモ2 by @matumu20

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

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

感想

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

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

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

プログラム

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

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

(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);
        }
    }
}

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

(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;
    }
}

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

37
7
1

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
37
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?