LoginSignup
44
5
記事投稿キャンペーン 「2024年!初アウトプットをしよう」

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

Last updated at Posted at 2023-12-31

2022年から人数上限なしのひとりアドベントカレンダー完走賞が新設されたことに伴い、2023年はひとりでアドベントカレンダーを走る人がそこそこ見受けられました。

01.png

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

なお調査のためのデータ抽出は2023/12/30に行っています。

調査結果

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

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

完走率は30.6%でした。

てっきり完走率10%くらいかと思っていたのですが、1/3も完走しているとはなかなかなかなか高い完走率ですね。

また最初から一人を目指したわけではなく、参加者を募集したけど最終的に一人しかいなかったという悲しきカレンダーも含まれているので、実質完走率はもう少し高くなるかもしれません。

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

ロジック

アドベントカレンダーを全て拾い出す → 走者が一人しかいないアドベントカレンダーを抽出する → 全日程にリンクがあれば完走、という単純なロジックになります。
しかしHTMLがぐちゃぐちゃで抽出に苦労しました。
なーにが最新ライブラリで綺麗なコードじゃ。
HTMLも綺麗にしろ。

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

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

以下の78カレンダー、77名です。
おめでとうございます。

個人的なまとめのカレンダー by @0_terarin_0
ここのえのカレンダー by @99no_exit
aibo ers-11x/210で遊ぶのカレンダー by @nakmura
自動運転AIチャレンジRacing大会(仮称)参加奮闘記のカレンダー by @Massy0127
最新のRuby on Railsに一人で迫ってみる挑戦のカレンダー by @eichann
AWS完全初心者によるアウトプットのカレンダー by @pandausa
学びや気づきの備忘録のカレンダー by @Takaharu_01
ぼっち論文サーベイメモのカレンダー by @matumu20
ぐらだけのカレンダー by @c9h11o2
「ChatGPTとPythonで学ぶ」シリーズのカレンダー by @maskot1977
Python(Django) x Docker x AWSのカレンダー by @Ryo-0131
コンポーネントごとに考えるアクセシビリティのカレンダー by @degudegu2510
CoreBluetoothForUnityのカレンダー by @Teach
○○作ってみない?のカレンダー by @nattyan_tv
daily articleのカレンダー by @dev-satoshi
PostgreSQL 入門のカレンダー by @dai_chi
【完走賞めざす!】データベースとSQLのススメのカレンダー by @n_yamadamadamada
日々の隙間で何かを学ぶのカレンダー by @eikidesu
Elixirのカレンダー by @the_haigo
Elixirのカレンダー by @Yoosuke
ERC・EIPのカレンダー by @cardene
firesignのカレンダー by @firesign2023
はじめてのアドベントカレンダーのカレンダー by @kaedeee
はじめてのアドベントカレンダーのカレンダー by @tech4anyone
フロントエンド開発で役に立つTipsのカレンダー by @tanimoto-hikari
1人フロントエンドのカレンダー by @KokiSakano
がちもとさんのカレンダー by @SatoshiGachiFujimoto
ゲーミフィケーションのカレンダー by @aoinakanishi
@gi-ra-ffeのカレンダー by @gi-ra-ffe
インフォマティカ・ジャパン株式会社のカレンダー by @masatoshimada-max
海外の転職面接関連で完走を目指すのカレンダー by @KitaDaro
JointJSのカレンダー by @acnaman
軽深(かるしん なんちゃって圏論)のカレンダー by @zanjibar
Keisuke Death Marchのカレンダー by @ke_sukesakuma
k.k.Factoryのカレンダー by @koji0705
Laravel とPHP Tipsのカレンダー by @sgrs38
Go 言語を学ぶのカレンダー by @rapirapi
Go 言語を学ぶのカレンダー by @shuyaeer
shellgei160を通じて言語習得のカレンダー by @mechten
Webアプリ構築カレンダーのカレンダー by @maaaashi
TIPSのカレンダー by @lowking
1年間の活動を振り返るのカレンダー by @does_not_exist
【完走チャレンジ】2023年わたしが学んだことまとめのカレンダー by @Maruhoppe8
めんどい太郎ののカレンダー by @mendoitarou_
MicroPython/CircuitPythonのカレンダー by @inachi
持ってる知識全般を棚卸するのカレンダー by @Killinneko
milk-V Duoのカレンダー by @kazueda
miriwoお一人様のカレンダー by @miriwo
Oktaに関するアウトプットのカレンダー by @mtbRck
ぽんぬの個人的のカレンダー by @MURAMASA2470
なんでもアウトプットのカレンダー by @marurusan
なりかくんのカレンダー by @narikakun
生成AIのハードルを下げたい!のカレンダー by @Isaka-code
negishi_tako完走したい!!のカレンダー by @Negishi_tako
にわのわさんのカレンダー by @niwanowa
俺の2023のカレンダー by @nomurasan
備忘録のカレンダー by @nyanyacyan
エンジニア歴3ヶ月のプログラミング初心者による初めての挑戦のカレンダー by @odendayoko
ORANGE picoのカレンダー by @mikecat_mixc
Microsoft Power BIのカレンダー by @akihiro_suto
Python「Hello, world!」のカレンダー by @bucks
一分で読める小ネタのカレンダー by @raki
RuruCun個人開発のカレンダー by @RuruCun
Qiita全国学生対抗戦のカレンダー by @SNQ-2001
SuiSuiのカレンダー by @suipy
Symfony Componentのカレンダー by @ippey_s
初学者でも完走賞をとりたい!!のカレンダー by @skm_bnn
土山竜輝のカレンダー by @tatsuki-tsuchiyama
【学習備忘録】アウトプットの習慣化を目指したいのカレンダー by @TKY_study
ここ2年くらいで学んだことをまとめて完走したいのカレンダー by @KitaDaro
UEFN / Verseのカレンダー by @eisuke114
完走賞のQiitanぬいぐるみをお迎えするためにUnityでゲーム作ってみるのカレンダー by @usomaru
UWSCRのカレンダー by @stuncloud
wslでnervesのカレンダー by @ohisama@github
ここ最近のおもひでぽろぽろのカレンダー by @yaikawa227
開発のお役立ち情報のカレンダー by @yam_dev
今年の振り返りのカレンダー by @youfuku
完走賞ゲットのため小ネタ 25記事を投稿しようとチャレンジ v2のカレンダー by @youtoy

感想

私はいつものペースなので、最初から参加する気はありませんでした。
毎日記事を書くなんて面倒臭い。

でも実際、私が面倒臭いと思うようなことを情熱を持ってやり遂げる人たちがこれほど存在するというわけで、彼らのような人たちがいるかぎり、この業界もきっと安泰にちがいありませんね。
彼らのこれからの活躍にも期待しましょう。

あと手元には三日坊主で完走できなかった人、それどころか登録したけど1件すら投稿のない人のリストなんかもあったりするわけですが、まあ流石に正月一発目から下品なことをするのもあれなのでやめておくことにします。

プログラム

一回動けばそれでいい、の精神で作られているので適当です。

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

(new A())->run();

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

        // 新着カレンダーリストから個々のカレンダーを取得
        $files = glob('./list/*');
        foreach ($files as $file) {
            $this->getCalendar($file);
        }
    }

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

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

        // カレンダーへのリンクを抽出
        $pattern = '|<div class="style-1dkfpx"><div class="style-ltzqlx"><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);
        }
    }

}

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

(new A())->run();

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

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

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

        // シリーズに分割
        $pattern = '|<h3 class="style-r7hjq4">(.*?)style-1ey0ayb|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/2023/' . basename($url);
            $ret[] = $tmp;
        }
        return $ret;
    }

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

        // 記事ごとに分割
        $pattern = '|<div class="style-1ssbn0c">(.*?)<div class="st-Modal">|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['user'] = $ret['complete'] = $ret['qiita'] = false;
                    return $ret;
                }
            }

            // 記事リンク
            $pattern = '|<div class="style-1dctyxx"><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;
    }
}

順に実行すると分析結果がanalyse.jsonに保存されるので、後はそれを適当に集計したりするだけです。
なおダウンロードが分かれているのは、分析するたびにダウンロードを実行してQiitaに負荷をかけるのを避けるためです。

44
5
3

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
44
5