【ブラウザ拡張機能】chrome拡張で便利ツールを作成

# ➊ はじめに
[**PowerAutomete for Desktop**](https://qiita.com/PoodleMaster/items/0a1fef8d37ed11840629)で簡易なスクレイピングやってみましたが、大まかな原理は一緒なので、**ブラウザ拡張機能**からできたら楽しそうだなと思ったので、こちらもやってみたいと思います。

# ➋ どんな感じ
「 https://qiita.com/PoodleMaster 」に表示されている記事を、Chrome拡張機能のアイコンボタンを押下することにより、「**記事URL、記事題名、記事LGTM数を収集し、一覧化**」します。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/0e2ac42d-3e34-4b58-e2ff-5bc1d9d22e33.gif">

# ➌ 用意するもの
まずは、[Chrome拡張機能開発者向けのドキュメント](https://developer.chrome.com/docs/extensions/)を熟読し全てを理解します。（嘘です）

|用意するもの|説明|
|:--|:--|
|Chrome/Edge|webブラウザ。|
|manifest.json|主にアプリの基本的な情報が含まれているファイル。|
|background.js|バックグラウンドサービスワーカー。イベントをリッスンして応答することができます。本アプリでは、拡張機能ボタンの押下をトリガにメッセージを送信します。|
|script.js|拡張ツール本体。background.jsからのメッセージをトリガとしてツールを実行します。|
|icon-32x32.png|拡張機能のアイコン。32x32pxのPNGを適当に作ってください。|

# ➍ manifest.json
マニフェストは、[`Version3`](https://developer.chrome.com/docs/extensions/mv3/intro/)で記述しました。見様見真似でOKです。

```json:manifest.json
{
    "manifest_version": 3,
    "author": "PoodleMaster",
    "homepage_url": "https://qiita.com/PoodleMaster",
    "name": "Qiita情報収集",
    "description": "Qiitaから情報を収集します",
    "version": "0.1",
    "icons": {
        "32": "icon-32x32.png"
    },
    "content_scripts": [{
        "matches": ["https://qiita.com/PoodleMaster"],
        "js": ["script.js"]
    }],
    "background": {
        "service_worker": "background.js"
    },
    "action": {
        "default_icon": "icon-32x32.png",
        "default_title": "Qiita情報収集"
    },
    "permissions": [
        "tabs",
        "background"
    ]
}
```

# ➎ background.js
## (1) クリックイベント
[`chrome.action.onClicked`](https://developer.chrome.com/docs/extensions/reference/action/#event-onClicked)を使用して、GoogleChromeツールバーの拡張機能のアイコンを制御します。アクションアイコンがクリックされたときに発火し、`content script`へメッセージ送信を行います。

```javascript:background.js
chrome.action.onClicked.addListener(function(tab) {
  chrome.tabs.sendMessage(tab.id, "Action");
});
```

# ➏ script.js
## (1) メッセージ受信イベント
[`chrome.runtime.onMessage`](https://developer.chrome.com/docs/extensions/reference/runtime/#event-onMessage)を使用して、メッセージ受信待ちを行います。メッセージ受信時発火し、`qiita_getter関数`を実行します。

```javascript:script.js&nbsp;(snippet)
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
  if (request == "Action") {
    qiita_getter();
  }
});
```

## (2) 記事URL/題名/LGTMの要素取得
`window.document.querySelectorAll`にて、指示されたCSSセレクターに一致する文書中の要素のリストを取得します。

```javascript:script.js&nbsp;(snippet)
  // 記事URLと記事題名収集
  const docList10 = window.document.querySelectorAll("a.css-1j37wyh");
  console.log(docList10);

  // 記事LGTM収集
  const docList20 = window.document.querySelectorAll("div.css-1laxd2k");
  console.log(docList20);
```
CSSセレクターの調べ方は、調べたい箇所を[`右クリック`]し、出てきたメニューから[`検証`]を選択すると、「デベロッパーツール」が起動するので、それで調べることができます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/91c5874b-7f9f-0531-adcb-8d147a86b692.png">

## (3) まとめ
それでは、**(1)と(2)をまとめていきますよー**。
あとは、取得した全データから表示するものを切り出したり、テーブルを作成したり、見やすくするためのstyleなども付け加えるため、ゴニョゴニョしていきます。

```javascript:script.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
  if (request == "Action") {
    qiita_getter();
  }
});

function qiita_getter() {

  var arrUrl = ["URL"];     // 記事URL
  var arrSub = ["Subject"]; // 記事題名
  var arrLgtm = ["LGTM"];   // 記事LGTM

  // 記事URLと記事題名収集
  const docList10 = window.document.querySelectorAll("a.css-2p454n");
  for( const item of docList10) {
    arrUrl.push(item.href);
    arrSub.push(item.innerText);
  }

  // ピックアップ記事を排除
  arrUrl.splice(1, 3);
  arrSub.splice(1, 3);

  // 記事LGTM収集
  const docList20 = window.document.querySelectorAll("span.css-a4xrgo");
  for( const item of docList20) {
    arrLgtm.push(item.innerText);
  }

  // ピックアップ記事を排除
  arrLgtm.splice(1, 3);

  // 新規作成ページ生成
  newPage = window.open("", "");

  // タイトル生成
  var titleJs = newPage.document.createElement('title');
  titleJs.innerText = "Qiitaデータ収集";

  // タイトル追加
  var headJs = newPage.document.head;
  headJs.appendChild(titleJs);

  // TABLE構成
  var r_end = arrUrl.length;  // 行数
  var c_end = 3;              // 列数

  // table要素を生成
  var tableJs = newPage.document.createElement('table');
  for (var r = 0; r < r_end; r++) {
      var trJs = newPage.document.createElement('tr');
      for (var c = 0; c < c_end; c++) {
          if (r == 0) {
            var tdJs = newPage.document.createElement('th');
          } else {
            var tdJs = newPage.document.createElement('td');
          }
          switch (c){
            case 0:
              str = arrUrl[r];
              break;
            case 1:
              str = arrSub[r];
              break;
            case 2:
              str = arrLgtm[r];
              break;
          }    
          tdJs.innerHTML = str;
          trJs.appendChild(tdJs);
      }
      tableJs.appendChild(trJs);
  }

  // table要素を追加
  var bodyJs = newPage.document.body;
  bodyJs.appendChild(tableJs);

  // style要素を生成
  const styleJs = newPage.document.createElement('style');
  styleJs.innerHTML = `
  table{
    width: 100%;
    border-collapse:separate;
    border-spacing: 0;
  }
  
  table th:first-child{
    border-radius: 5px 0 0 0;
  }
  
  table th:last-child{
    border-radius: 0 5px 0 0;
    border-right: 1px solid #3c6690;
  }
  
  table th{
    text-align: center;
    color:white;
    background: linear-gradient(#829ebc,#225588);
    border-left: 1px solid #3c6690;
    border-top: 1px solid #3c6690;
    border-bottom: 1px solid #3c6690;
    box-shadow: 0px 1px 1px rgba(255,255,255,0.3) inset;
    padding: 10px 0;
  }
  
  table td{
    border-left: 1px solid #a8b7c5;
    border-bottom: 1px solid #a8b7c5;
    border-top:none;
    box-shadow: 0px -3px 5px 1px #eee inset;
    padding: 10px 0;
  }
  
  table td:last-child{
    border-right: 1px solid #a8b7c5;
  }
  
  table tr:last-child td:first-child {
    border-radius: 0 0 0 5px;
  }
  
  table tr:last-child td:last-child {
    border-radius: 0 0 5px 0;
  }
`
  // style要素を追加
  headJs.appendChild(styleJs);

}
```

## ※Qiita HP改修による対応（2023/08/23対応）

:::note warn
Qiita HP改修によりclassの変更が合った場合は、下記を参考に適宜変更してください。
:::

```js:script.js
    // 記事URLと記事題名収集
    const docList10 = window.document.querySelectorAll("a.css-2p454n");
        ↓↓↓↓
    const docList10 = window.document.querySelectorAll("a.style-10d3113");
```

```js:script.js
    // 記事LGTM収集
    const docList20 = window.document.querySelectorAll("span.css-a4xrgo");
        ↓↓↓↓
    const docList20 = window.document.querySelectorAll("span.style-mvmd7d");
```

# ➐ インストール
コードができたら、拡張ツールをインストールしていきます。このツールは「[`Chromeウェブストア`](https://chrome.google.com/webstore/category/extensions?hl=ja)」には公開はしていませんので、**ブラウザの拡張機能をデベロッパーモードからインストールする必要があります**。

※ちなみに、細かい手順は省きますが、同じ様な手順でインストールするれば、Edgeからでも動作します。（確認済み）

## (1) ソースコードの入手
上記からソースコードを作ってもよいですが、作成済みのものを **github** にて公開しています。
必要に応じて、こちらから入手してください。

> **■ 公開ソースコード**
> https://github.com/PoodleMaster/QiitaExtention

## (2) 拡張機能管理ページ
**拡張機能をデベロッパーモード**にするため、**拡張機能管理ページ**にアクセスします。
アクセスの仕方はいくつかありますが、以下の手順でできます。

1. Chromeの右上にある三点リーダ[`︙`]をクリックしてメニューを表示させる。
1. 表示したメニューにある[`その他のツール`]－[`拡張機能`]をクリックし、**拡張機能管理ページ**を表示させる。

> **■ 他の方法1**
> [chrome://extensions/](chrome://extensions/)
> 上記へアクセスしてもOKです。
>
> **■ 他の方法2**
> ブラウザの右上にあるパズル[🧩]のようなアイコンをクリックすると、メニューが表示されます。メニューの最下部にある「`拡張機能を管理`」を押下してもOKです。

## (3) デベロッパーモードON
1. **拡張機能管理ページ**の右上にあるスイッチを[`ON`]にします。
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/c43c9180-38b2-3a24-8cdc-b66a4202a5d8.png">

## (4) パッケージ化されていない拡張機能を読み込む
1. 先程作成したmanifest.json、background.js、script.js、icon-32x32.pngをインストールホルダに格納します。**インストールホルダは、拡張機能が動作している間は削除したり、移動したりできません**ので、後々を考えて適当な場所に作成してください。win10であれば、例えば「%HOMEPATH%/QiitaExtention」あたりで良いかと思います。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/bf13152c-74c3-c6e1-3aae-5b75441cf381.png">
1. 左上にある[`パッケージ化されていない拡張機能を読み込む`]ボタンを押下します。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/a1de25f2-01ad-dd90-c50d-28fc6b5d2d34.png">

1. 先程のインストールホルダを選択すると、「**Qiita情報収集**」という名の拡張機能が追加されます。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/94d49e0b-5d8d-089f-37f1-d02ff58b88f5.png">

## (5) ブラウザにアイコンを表示させる
今回の拡張機能は、アイコンをボタン化していますので、ブラウザに表示させます。表示させ方は以下でできます。

1. ブラウザの右上にあるパズル[`🧩`]のようなアイコンをクリックすると、メニューが表示されます。
1. メニューの中に「**Qiita情報収集**」というのがあるので、これをピン留め[`📌`]して固定するとブラウザ上部に拡張機能のアイコンが表示されます。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/ca08f9cd-e563-2ed6-7440-5344ef15a429.png">

# ➑ 実行結果
1. まずブラウザから、以下のURLへアクセスします。
> https://qiita.com/PoodleMaster

1. 拡張機能のアイコンを押下します。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/bbecb9d5-32ca-dc86-660c-076d2ba851c9.png">

1. 記事のURL、題名、LGTMを取得し、一覧化できました😉！
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/3ce4cd07-7c54-db39-9267-b3111e8f7010.png">
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405376/75967e47-811b-7248-be72-10d6b01d6bd8.gif">

# ➒ 以上
Chromeの拡張機能は、できたら楽しいだろうなと思いきや、特にやることがないので敬遠していました。やってみると意外と簡単に作ることができました。一番の問題は、拡張機能で何をやりたいのかなどの「**アイデア**💡」ですね😅

お疲れ様でした😊