ProtoPediaの作品データを簡易的に取得して指標分析など


## ヒーローズリーグ2023お疲れ様でした！

ヒーローズリーグは老舗のものづくり開発コンテストって感じで2023年の決勝が先日終わりました。

ヒーローズリーグとは？はこちらの記事を読んでみましょう。

https://note.com/otama_kum_akero/n/nd43ffa03aba4

僕も出しましたけど賞に引っかからず... 

https://protopedia.net/prototype/4902


## 288作品の情報を一覧化してみる

個人的な興味（理由はあるけど深くは言わない）で作品の情報を抜き出しつつソートしたりしたいなと思いました。

https://protopedia.net/event/hl2023


今回の応募作品は288作品でした。

この情報を手で取るのは大変で、ProtoPediaのAPIはあるけどなんか **挙動が怪しかったのでAPIは使わず**にサイトから抜き出してみるチャレンジをしてみます。

### サイトの構成

こんな感じで作品がdivタグ要素のBOXに入っています。 **"proto-box"というclass**が付いていたので分かりやすかったです。

> ![スクリーンショット 2023-12-05 10.46.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/2b7b3172-9aef-0d1f-77d6-dbfb257ab5f4.png "スクリーンショット 2023-12-05 10.46.17.png")

### デフォだと30件しか表示されず、もっと見るボタンが必要

30件しか表示されないのでサーバー側で生成された初期状態だと30件のレンダリングになってる模様でした。

> ![スクリーンショット 2023-12-04 19.08.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/cfb8bfe0-1129-ccb4-2da3-ab1e3cd3dafa.png "スクリーンショット 2023-12-04 19.08.23.png")

**もっとみるボタンがページ下部にあり、これを押すと追加で30件がロードされる仕様っぽいです。**

> ![スクリーンショット 2023-12-05 10.47.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/241fb58a-d267-bdb3-ef23-03ea6e0852f7.png "スクリーンショット 2023-12-05 10.47.59.png")

Fetch()などのHTTP Requestで抜き出すことはできず、ボタンエミュレートなどが必要になりますね。

## Puppeteerさんを使ってボタンぽちぽち押させる

### もっと見るボタン再帰関数

もっと見るボタンは`nextBtn`というidが振ってあったので特定は楽でした。

```html
<button id="nextBtn" type="button" class="btn-reset btn primary">もっと見る</button>
```

1. clickMore関数を作りつつボタンを押す、これを再帰的に呼び出します。
2. 要素が最後までいくともっと見るボタンが表示されなくなる
3. もっと見るボタンを押そうとするけどボタンが無くてエラーがでて異常終了

こんな流れにして288件を表示させました。

件数指定にするかなど悩みましたが一旦ここに行きつきました。

```js
//もっとみるをクリック
async function clickMore(page, selector){
    try {
        console.log(`...`);
        await page.click(selector.moreButton); // もっとみるボタン s#nextBtnをクリック
        await setTimeout(1000 * 1); // 待つ
        return clickMore(page, selector); //再帰実行
    } catch (error) {
        //エラーが出たら終了
        console.log(`もっとみる終わり`);
    }
}
```

> ![](https://i.gyazo.com/ec42b055bee1d540956e7de814733afa.gif)

### 528件とれてた

試してたら、proto-boxの要素が528件取れてました。
ヒーローズリーグ2023の応募件数は288件でしたね。528件って何かおかしい。。

> ![スクリーンショット 2023-12-05 1.42.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/e674878c-497f-cbc8-304a-acce7ec4cd96.png "スクリーンショット 2023-12-05 1.42.42.png")

おそらく賞を取ったときの表示が追加されたりしてるのかクリックしたときに何かDOM生成が過剰にされるようなバグが発生しているのか... といった感じかなと予想します。

こちらの記事を参考にさせていただき、Mapを使ってユニークにしました。

https://qiita.com/allJokin/items/28cd023335641e8796c5

```js
const uniqueProtoItems = Array.from(
    new Map(protoItems.map((item) => [item.title, item])).values()
);
```

> ![スクリーンショット 2023-12-05 1.47.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/2699bf96-7115-7361-4980-a75ffc2dbad9.png "スクリーンショット 2023-12-05 1.47.11.png")

予想はあたりでタイトルでユニークにしてみたら288件になりました。

### 要素を取ってみる

情報として取れそうなのは

- 作品タイトル
- URL
- ビュー数
- いいね数
- コメント数
- サムネイル画像URL

> ![スクリーンショット 2023-12-05 11.02.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/dece9cee-736c-cba6-f954-25eb33fbea3d.png "スクリーンショット 2023-12-05 11.02.29.png")

各要素はこんな感じでとれます。

`<span>`要素の1個目が閲覧数、2個目がいいね数、3個目がコメント数になってますね。

```js
    const protoItems = await page.evaluate((selector) => {
        let protoItems = [];
        document.querySelectorAll(selector).forEach(item => {
            const protoItem = {
                url: item.href,
                title: item.querySelector("H2").textContent,
                view: item.querySelectorAll(".count1 span")[1].textContent,
                like: item.querySelectorAll(".count2 span")[1].textContent,
                comment: item.querySelectorAll(".count3 span")[1].textContent
            }
            protoItems.push(protoItem);
        });

        return protoItems;
    },selector.protoBox);
```



## 上だけ数字で一覧化

ヒーローズリーグは趣旨的にあえてランキングは出してないと思います。

みんなの1位があるのが良いところなので、順位つけるのよくない気もしますが、
ただ個人的な興味というところでページ上で見えてるデータから算出はできるのでかるく出してみました。

| 順位 | ビュー数   | Like数    | Comment数 |
|------|------------|-----------|-----------|
| 1位  | [Type-C 危機一発 (18884 view)](https://protopedia.net/prototype/3877) | [TechSeebo (49 Like)](https://protopedia.net/prototype/3975) | [架空の奥さんが料理して待ってくれる装置 (14 comment)](https://protopedia.net/prototype/4707) |
| 2位  | [サービス終了になった LINE CLOVA 達に第二の人生を歩ませる (8825 view)](https://protopedia.net/prototype/3761) | [Cheifoon (49 Like)](https://protopedia.net/prototype/3978) | [やる気急上昇！航空電子機器風MTGヘルパー 「とぐるちゃん」 (13 comment)](https://protopedia.net/prototype/4714) |
| 3位  | [ショップバリュー ~長さを売る自販機~ (4199 view)](https://protopedia.net/prototype/3857) | [Type-C 危機一発 (47 Like)](https://protopedia.net/prototype/3877) | [柿 : ピー (かきたいぴー) (10 comment)](https://protopedia.net/prototype/4715) |

こうみると、案外被りがないというのがすごいですね。

ビュー数、いいね数、コメント数でそれぞれ盛り上がり方が違うんだろうなと思ったりします。

**それぞれ面白い作品ばかりなので↑のURLからページを見てみてください！**

( もう少し多めのデータは[こちら](https://gist.github.com/n0bisuke/89fff58b95c2c532b6b871807257d216)にメモ的においてます。 )

### 蛇足

このテーブル作るのだるいなって感じだったんですけど、ChatGPTさんありがたいです。

> ![スクリーンショット 2023-12-05 12.38.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/96fd31fe-b62e-2907-202f-5687bb8e019d.png "スクリーンショット 2023-12-05 12.38.07.png")

## まとめ

APIを使わずにProtoPediaの作品情報を簡易的に取ってみました。

APIが重かったのですが一旦これで取れそうな雰囲気でてきたのでDOM構造あまり変えないで欲しいなと思ってたりします。

おしまい。