ChatGPTと協力して「AIによる概要」を消すChrome拡張機能を作る

# この記事の概要
- Google検索結果の「AIによる概要」を消すChrome拡張機能を作りたい
- でもコーディングはしたくない
- GPTに指示を出して作らせてみよう！
- 作った拡張機能はストアで配布中！
    - [Hide Google AI Overview](https://chromewebstore.google.com/detail/hide-google-ai-overview/ejjophcbmhoibfofndfdomaojpgppehd?authuser=0&hl=ja)

# はじめに
最近Googleの検索結果に出てくる「AIによる概要」、一番上に出てくる割にデタラメだらけでイライラしますよね。これをChrome拡張機能で消してやりたいと思います。しかし、自分で作るのはちょっと面倒です。
そこで、GPTくんに全てを任せて作ってもらおうと思います。この記事は、ひたすらGPTと対話してほぼコーディングなしで拡張機能の完成まで持っていきます。

# 使うもの
- ChatGPT o1（有料プラン）
- Google Chrome
- VSCode（エディタならなんでもOK）

# 作ってみる
早速、何も考えずに作ってみましょう。
とりあえず、GPTに丸投げしてみます。
## 丸投げ
### 質問１
>Google検索で何か適当に検索してみてください。検索結果の画面の最初に、「AIによる概要」という欄が表示されると思います。この結果が邪魔なので、これを表示しなくするようなChrome拡張機能を作成したいのですが、できますか？
### 回答１
以下は、Google 検索結果に表示される「AIによる概要」(Search Generative Experience: SGE) の要素を非表示にするための、シンプルな Chrome 拡張機能の作り方の一例です。実際の DOM 構造は Google が変更する可能性が高く、要素名やクラス名などは固定ではありません。そのため、実際に動かす際には自分の環境のブラウザで要素を特定して、CSS セレクタを適宜修正してください。また、このサンプルは manifest v3 を想定しています。

### 1. 拡張機能のファイル構成例

```
my-hide-sge-extension/
├─ manifest.json
└─ content.js
```

### 2. manifest.json の例

```json
{
  "name": "Hide Google AI Overview",
  "version": "1.0",
  "manifest_version": 3,
  "description": "Google検索結果ページからAIによる概要パネルを非表示にします。",
  "content_scripts": [
    {
      "matches": ["*://www.google.com/search*"],
      "js": ["content.js"],
      "run_at": "document_idle"
    }
  ]
}
```

ポイント：

- `matches` は、Google検索結果ページにマッチするように設定します。  
  例: `*://www.google.com/search*` で www.google.com の検索結果ページに対応します。(必要に応じて国別ドメインや言語設定に合わせてください)
- `run_at`: `document_idle` によりページ読み込み後に実行します。

### 3. content.js の例

```javascript
(function() {
  // 定期的に対象要素をチェックして見つかれば非表示にする処理
  // （GoogleのSGEは動的に読み込まれる場合があるため、MutationObserverを使うのも手です）

  const hideSGE = () => {
    // ここで実際に SGE の概要部分に相当する要素を特定します。
    // 実際の名称やクラス名はブラウザの開発者ツールで確認してください。

    // 以下は例示用のセレクタであり、実際には正しく動かない場合があります。
    // 2023年後半から2024年にかけてのSGEは動的要素や shadow DOM を利用していることがあるので、
    // セレクタを特定するためには開発者ツールで実際のHTML構造を確認してください。

    const sgeBox = document.querySelector("div[data-sfe-context-bar]"); 
    // これはあくまで例です。「AIによる概要」の枠を持つ要素をdevツールで見つけ、 
    // そのための特徴的なクラス名や属性をここに設定してください。

    if (sgeBox) {
      sgeBox.style.display = "none";
    }
  };

  // ページロード後すぐに実行
  hideSGE();

  // 動的変更にも対応するため、 MutationObserver でDOM変化を監視
  const observer = new MutationObserver(hideSGE);
  observer.observe(document.body, { childList: true, subtree: true });

})();
```

### 4. 拡張機能の読み込み手順

1. Chrome で `chrome://extensions/` にアクセス。
2. 画面右上あたりの「デベロッパーモード」を有効化。
3. 「パッケージ化されていない拡張機能を読み込む」をクリックし、上記で作成したフォルダ (my-hide-sge-extension) を選択。

### 5. 動作確認

- Googleで検索して、SGEによるAI概要（緑や白背景の要約ボックス）が表示されるか確認。
- 正しくセレクタを指定できていれば、その要約ボックスが表示されなくなるはずです。

### 参考

- 要素を正しく隠すには、Google検索結果ページ上で開発者ツール（F12キーや右クリックから「検証」）を使い、実際の対象要素のHTML構造やクラス名、IDなどを特定してください。
- Googleは頻繁にDOM構造やクラス名を変える可能性があります。そのため、このスクリプトは恒久的な対策とはなりにくく、必要に応じて定期的なメンテナンスが必要です。

---

このように、シンプルな Chrome 拡張機能で、指定したDOM要素を非表示にすることは可能です。実際の動作にはセレクタ調整が必要ですが、基本的にはこの手順で十分実現できます。

## 動作確認
今作ってくれたものを実際に作ってみましょう。GPTが指定してくれたファイル構成で実際に各ファイルを作成していきます。普通にVSCodeなどで記述していきましょう。
GPTが教えてくれている通りに拡張機能を読み込み、いざ試してみると......

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3661623/155e55f1-ed13-deee-f050-96962ee1b19b.png)

流石に消えませんでした。これで消えてしまったら記事が終わってしまうので、よかったです（？）。
では、何がおかしいのか確認してみましょう。

## おかしな部分を探す
ちゃんとGPTの回答を確認してみると、実はGPTも「このままでは動かないかもしれない」と言及しています。下記の部分ですね。
```JavaScript
    // ここで実際に SGE の概要部分に相当する要素を特定します。
    // 実際の名称やクラス名はブラウザの開発者ツールで確認してください。

    // 以下は例示用のセレクタであり、実際には正しく動かない場合があります。
    // 2023年後半から2024年にかけてのSGEは動的要素や shadow DOM を利用していることがあるので、
    // セレクタを特定するためには開発者ツールで実際のHTML構造を確認してください。

    const sgeBox = document.querySelector("div[data-sfe-context-bar]"); 
    // これはあくまで例です。「AIによる概要」の枠を持つ要素をdevツールで見つけ、 
    // そのための特徴的なクラス名や属性をここに設定してください。
```
これはつまり、「このセレクタは適当だから、ちゃんと自分で正しいセレクタに書き換えてね」と言われているわけですね。
ではここからは、頑張って自分でセレクタを探してみる......のは面倒なので、GPTに適切なセレクタを探してもらいましょう。とはいえ、流石に多少は絞って探してもらいます。


## デベロッパーツールで該当部分のHTMLを探す
まずは、Chromeでデベロッパーツールを開きます。
F12キー、もしくは
Windows: Ctrl + Shift + i
Mac: Command + Option + i
で開けるはずです。開けたら、デベロッパーツールの左上の、四角に矢印が重なったようなアイコンをクリックしてみてください。これをクリックした状態でサイト上でカーソルを動かしてクリックすると、要素に対応するHTML要素が簡単にわかります。

![スクリーンショット 2024-12-16 18.11.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3661623/0b270fb7-551c-a8a7-cbf9-95a5a606dd3f.png)

今回はこの辺り（青色になっている部分）が怪しいですね。

![スクリーンショット 2024-12-16 18.14.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3661623/997fdd22-6266-8a0b-5bfb-ab30f2574a29.png)

あとはこのあたりのHTML要素に適当に`hidden`と書き加えてみて、いい感じに消えてくれるHTML要素を探してみましょう。今回はコイツでした（デベロッパーツールのハイライトされている行）。
しかし内部を覗いてみると、自動生成されているようなidが多いように見え、何をセレクタとして使用すれば良さそうかわかりません。そこで、適切なセレクタをGPTに探してもらうことにします。

![スクリーンショット 2024-12-16 18.21.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3661623/7dbd5a2c-d709-8517-d112-d3e409a80b05.png)

まずは、この犯人のHTMLをコピーします。
右クリックして、Copy > Copy outerHTML からコピーできます。

![スクリーンショット 2024-12-16 18.25.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3661623/3e63263b-9ae0-9f66-2f20-1c1644939264.png)

これをGPTにぶん投げていい感じに消せるセレクタを考えてもらいましょう！

## 丸投げ（２）
### 質問２
> HTMLのこの部分が怪しいです。汎用性に注意して、適切なセレクタを探して選んでください。
> （ここに先ほどコピーしたHTMLをペースト、長いので省略）

### 回答２
以下のHTML構造から「AIによる概要」セクションを特定する際、なるべく変更に強い（耐久性のある）セレクタを使うことが望まれます。しかし、Google側でクラス名・ID・属性名が変更される可能性は常にあります。そのため、絶対的な保証はできませんが、以下のような方針でセレクタを選ぶことができます。

1. **テキスト（"AI による概要"）に依存しない**：  
   将来的に文言が変わる可能性があります。また、海外版や別の言語設定で文言が異なる可能性もあるため、`h1.VW3apb` などクラスを用いた上でテキストマッチングに頼るのは避ける方がよいでしょう。

2. **IDは避ける**：  
   `id="eKIzJc"` のようなIDは動的・一時的に変わる可能性があります。このため、IDに依存したセレクタは後々使えなくなる可能性が高いです。

3. **特徴的な属性に注目**：  
   コード内にある `data-lhcontainer="1"` などのカスタム属性は比較的希少で特徴的です。このような属性は内部実装変更があるまで残りやすいと考えられます。

4. **ユニークで上位の親要素を指定**：  
   `data-lhcontainer="1"` は目立つ属性であり、この要素以下に「AIによる概要」が含まれています。

そのため、例えば以下のようなセレクタを試すことが可能です。

```css
/* 「AIによる概要」全体を含む可能性が高い上位要素を非表示 */
div[data-lhcontainer="1"] {
    display: none !important;
}
```

また、万が一 `data-lhcontainer` の値が変わった場合に備えて、`h1.VW3apb` との組み合わせを使うことも考えられます。例えば、`div[data-lhcontainer="1"] h1.VW3apb` のように、`"AI による概要"` のタイトルを持つエリアを狙うこともできます。

```css
div[data-lhcontainer="1"] h1.VW3apb {
    display: none !important;
}
```

しかし、この場合は `h1.VW3apb` が他の要素で使われていないことを確かめる必要があります。

**総合的なおすすめ**:  
`div[data-lhcontainer="1"]` という属性に依存するセレクタは比較的シンプルで、かつ変更が少ない可能性があるため、
```css
div[data-lhcontainer="1"] {
    display: none !important;
}
```
このセレクタから始めることをお勧めします。  

最終的には、実際に拡張機能でテストし、もし将来的にDOMが変われば再度セレクタを修正する、といったメンテナンスが必要になる点はご了承ください。

### 質問３
> では、これを踏まえてcontent.jsを書き直してください。

### 回答３
以下は、前回のコード例を「AIによる概要」要素を特定する際に、`div[data-lhcontainer="1"]` という属性を利用した、より汎用的なバージョンです。必要に応じてセレクタは変更してください。

**content.js**

```javascript
(function() {
  // Google検索結果ページ上の「AIによる概要」セクションを非表示にする
  // ターゲット：div[data-lhcontainer="1"]

  const targetSelector = 'div[data-lhcontainer="1"]';

  function hideSGE() {
    const sgeBox = document.querySelector(targetSelector);
    if (sgeBox && sgeBox.style.display !== "none") {
      sgeBox.style.display = "none";
    }
  }

  // 初回実行（ドキュメントロード完了後）
  document.addEventListener('DOMContentLoaded', hideSGE);
  window.addEventListener('load', hideSGE);

  // 動的変化にも対応するためMutationObserverでDOM変化を監視
  const observer = new MutationObserver(hideSGE);
  observer.observe(document.body, { childList: true, subtree: true });

})();
```

**ポイント**:

- `div[data-lhcontainer="1"]` というセレクタで `AIによる概要` 領域を特定。  
- ページロード後（`DOMContentLoaded`, `load`）に初回実行。
- `MutationObserver` を使い、ページの動的な変化にも対応。  
- 万が一、Google側で構造が変わった際は、セレクタや処理箇所を随時修正してください。

## 動作確認
きちんと、すぐに変わりそうなIDなどではなく汎用性の高そうなセレクタを探し出してくれました！（ただし、他の要素で使われていない保証はできないことに注意）
また、続けて、コードの書き換えまでしてもらいました。
いざ動作確認の時間です。先ほどの`content.js`を新しいコードに置き換えて、拡張機能を再読み込みします。
実行してみると、一瞬AIによる概要の残像が見えるものの、無事にAIによる結果が消え去りました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3661623/0e526a39-b9fc-606e-bf70-5741d330b26e.png)

他の検索結果でも試してみます。

拡張機能なし：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3661623/fe8010e5-48e5-657e-50a9-4e7ab1d78a2a.png)

拡張機能あり：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3661623/efa82d8a-e7df-e81d-b0f5-aad5f4d67942.png)

無事に消えています。

しかし、画面上部の余白まで無くなってしまって気持ち悪いですね。これについても聞いてみます。

## 丸投げ（３）
### 質問４
> 概要はうまく消えましたが、画面上に一瞬表示されてしまうのと、画面上部の余白が無くなってしまって少し気持ち悪いです。これを解決できますか？

### 回答４
`div[data-lhcontainer="1"]` 要素を JavaScript で後から非表示にすると、一瞬表示される「フラッシュ」が発生する場合があります。これを回避するには、`run_at: "document_start"` を使ってできるだけ早い段階で CSS を適用する、もしくは CSS で直接非表示にする方法がおすすめです。

また、「AIによる概要」セクションを隠したことで上部の余白がなくなり、表示が窮屈に感じる場合は、CSS で適度なマージンを追加してやることができます。

以下は改善案です。

### manifest.json (抜粋例)

```json
{
  "name": "Hide Google AI Overview",
  "version": "1.0",
  "manifest_version": 3,
  "description": "Google検索結果ページからAIによる概要を非表示にします。",
  "content_scripts": [
    {
      "matches": ["*://www.google.com/search*"],
      "css": ["content.css"],
      "js": ["content.js"],
      "run_at": "document_start"
    }
  ]
}
```

- `run_at: "document_start"` を指定することで、ページが読み込まれる前に CSS を適用し、一瞬の表示を防ぎます。

### content.css

```css
/* AIによる概要を全く表示しない */
div[data-lhcontainer="1"] {
  display: none !important;
}

/* 必要であれば、上部に余白を追加 */
#searchform {
  margin-top: 20px !important;
}
```

- これで「AIによる概要」要素はページ描画前から非表示になり、フラッシュが起こりにくくなります。
- #searchform に margin-top を追加してやることで、もともとAI概要が占めていたスペースを再現し、上部が詰まる感じを緩和します。（`#searchform` は例です。実際のDOM構造に合わせて調整してください）

### content.js

```javascript
(function() {
  // ここでは特に非表示化処理を記述しなくてもOK。
  // 必要なら追加でDOM監視をして、他の動的ロード分も消す等の処理を行える。
})();
```

- CSSで完全に表示前から要素を非表示にしているため、content.jsでの追加処理は不要かもしれません。ただし、Google側で構造が変わってしまった場合や、動的に要素が追加される場合はMutationObserverでの監視も有効です。

この方法であれば、一瞬表示される問題も緩和され、かつ余白問題もCSSで解決可能です。

## 動作確認
最初から狙い撃ちでcssを変更することで、そもそも表示させない方法を提案してくれました。では、指定の通りに書き直して動作確認です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3661623/98c3c8d4-c05a-dde8-8c75-8197a3e3235c.png)

たしかにAIによる概要の残像は表示されなくなりましたが、相変わらず検索結果の余白はありません。しかし、よく見ると画面最上部の余白（検索バーの上の余白）が増加していることがわかります。これは私の指定の仕方が悪かったですね。GPTに指示を出す場合、望んだ結果を実現するためには正確に、解釈の余地がなるべく狭まるように指示を出すことが重要です。
これを踏まえてもう一度指示を出してみます。先ほどまで「AIによる概要」があった部分に余白が欲しいと伝えてみます。

## 修正指示
### 質問５
> すみません、伝え方が正確ではありませんでした。「AIによる概要」が存在した位置に余白が欲しいです。

### 回答５
「AIによる概要」セクションが占めていたスペースを保持したい場合、`display: none;` ではなく `visibility: hidden;` を使うと要素の領域は残り、内容のみ見えなくなります。ただし、要素の中身を隠すと中身に応じた高さは無くなる可能性があります。その場合は、`min-height` などを指定してあげるとよいでしょう。

以下は一例です。

```css
/* 「AIによる概要」を非表示にしつつ、元の位置に余白を保持する */
div[data-lhcontainer="1"] {
  visibility: hidden !important;  /* 要素自体は残しつつ中身を非表示 */
  min-height: 200px !important;   /* 適宜、元の高さに近い値を指定 */
}
```

ここで `200px` はあくまで例です。実際にページを見ながら、ちょうど良い余白になるように `min-height` の値を調整してください。

これにより、「AIによる概要」を実質的に見えなくしながら、その部分のスペースを確保し、ページ全体のレイアウトや余白バランスを維持できます。

## 動作確認
指示を受けて、cssの書き換えを提案してくれました。早速試してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3661623/dd3d0d6f-196f-5d51-8786-da3bbf2b2f0e.png)

余白はできましたが、逆に広すぎますね。`min-height`を調整しても幅は変わらないようです。これを改善してもらいましょう。きちんと、`min-height`を調整しても変化がなかった旨も伝えておきます。

## 修正指示（２）
### 質問６
> ありがとうございます、余白が入りました。しかし、むしろ余白が広すぎます。
> これを狭くすることはできますか？min-heightを調整しても幅は変わりませんでした。

### 回答６
`min-height` を変更しても高さが変わらない場合、要素内部の別のスタイルが高さを決定している可能性があります。また、`visibility: hidden` は中身を見えなくするだけで、内部構造次第で高さが維持されていることがあります。

以下を試してみてください：

1. **`height` を直接指定する**  
   `height` プロパティで要素の高さを固定し、`overflow: hidden;` で余分な部分を隠します。

   ```css
   div[data-lhcontainer="1"] {
     visibility: hidden !important;
     height: 50px !important;   /* 希望する高さに調整 */
     overflow: hidden !important;
   }
   ```

   これにより、`div[data-lhcontainer="1"]` は中身が見えない上で高さを50pxに固定します。値は実際にページを確認しながら微調整してください。

2. **`display: block; margin: 0; padding: 0;` の明示的指定**  
   要素が何らかの余白やデフォルトのパディング・マージンを持っている可能性があるので、明示的に取り除きます。

   ```css
   div[data-lhcontainer="1"] {
     visibility: hidden !important;
     display: block !important;
     margin: 0 !important;
     padding: 0 !important;
     height: 50px !important;
     overflow: hidden !important;
   }
   ```

こうすることで、元々の「AIによる概要」要素のスペースを、希望の高さに縮めた形で残すことができるはずです。

## 動作確認
まず上の方から試してみます。`height`を調整することで余白は小さくできますが、どうやら下限があるようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3661623/9f34d061-c109-adcf-3be9-f78fb3fb4d96.png)

下の案を試してみると、`height`を`30px`で指定するとでちょうど良い余白を得られました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3661623/141da1c4-caa3-966d-e09e-bfde24422060.png)

このように、たった６回のラリーで望んだ動作をする拡張機能を作ることができました！

# おわりに
本記事では、GPTを用いたChrome拡張機能作成の一例を示しました。Chrome拡張を作る際に必要な要素をGPTがパパッと作り出してくれることから、作ったことがない人にも「意外と簡単に作れるんだな」と思ってもらえたのではないでしょうか。
また、今回の拡張機能で難しい点であったセレクタの指定も、GPTにお願いして簡単に解決することができました。GPTは、このような長い文字列から適切なものを選択する作業が比較的得意であるように感じます。少なくとも私には、何を指定すればよいかわからなかったと思います。

Chrome拡張機能に限らず、GPTを用いることで、これまで多少ハードル高かった実装の実践が容易に行えるようになりました。個人的には、何かに頼りきりであったとしても実践してみることで得られるものは多いと考えているので、このようにGPTの手を借りてとりあえず作ってみるということには意味があると考えています。

なお、本記事で作成した拡張機能は現在ストアで公開中です。よければ使ってみてくださいね！
[Hide Google AI Overview](https://chromewebstore.google.com/detail/hide-google-ai-overview/ejjophcbmhoibfofndfdomaojpgppehd?authuser=0&hl=ja)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3661623/39460515-9f18-ba45-0ff5-5f85ef11ed9d.png)