Python × BeautifulSoup × Pandas で観光庁の公募情報をスクレイピングしてExcel出力した話

## はじめに
「観光庁の公募情報ページ」から **"募集中"** の公募情報を取得し、  
**BeautifulSoup** でデータをスクレイピングし、**Pandas** で Excel に出力するまでの試行錯誤をまとめました。

## 今回の課題設定の理由
補助金情報などは事業者、官公庁ともに関心の高い項目であり、簡単にまとめられたりすると有意義に感じたから。今の勉強段階では、一次情報の取得に留まるので、とっかかりとしては良いのでは？と感じたから。

今回、自分の勉強のために **ChatGPT に確認しながら** 実行しました。  
その過程で **自分の知らなかった調べ方や実装の工夫を学ぶことができ、非常に勉強になりました。**  
特に **HTMLの構造を解析する方法や、Excelでリンク付きデータを出力するテクニック** など、  
「スクレイピングを実装するだけでなく、どのように結果を扱うか」についても学べたのが大きな収穫でした。  

**最終的な出力が完成するまでに要した時間は約5時間。**  
思ったよりも試行錯誤するポイントが多かったですが、その分多くのことを学べました。  

---

## 事前の確認
スクレイピングを実施する前に、以下の点を確認しました。

1. **robots.txt の確認**  
   - **`https://www.mlit.go.jp/robots.txt`** をチェックし、スクレイピングが制限されていないことを確認。

2. **利用規約の確認**  
   - **著作権・リンク・免責事項、プライバシーポリシー** を確認し、  
     **個人情報の取得や、大量アクセスによるサーバー負荷をかける行為が禁止されていないかチェック** しました。

3. **負荷を考慮した実装**  
   - **1秒のスリープ (`time.sleep(1)`) を入れてアクセス頻度を制御** し、サーバー負荷を軽減。

---

## やりたかったこと
1. **観光庁の公募情報ページ** から、"募集中" の情報のみ取得する
2. **詳細ページに遷移し、公募期間・事業概要を取得**
3. **Excelに書き出す**
   - **詳細ページリンクをHYPERLINK付きで出力**
   - **添付ファイルをHP上に表示されている名前＋リンク付きで出力**

---

## 試行錯誤したポイントと解決策

### ① ページ構造が統一されていない
#### **問題**
- 公募一覧ページから **タイトル・募集開始日・詳細ページのリンク** を取得するのは簡単だった。
- しかし **詳細ページ** に行くと、ページによって **公募期間・事業概要の記載位置がバラバラ**。
  - **`<div class="text">` の中に情報が格納されているが、どの情報が "公募期間" なのか区別できない。**

#### **解決策**
- **`h2` タグ (`<h2 class="c-ttl-02">`) を基準にして、その次の `div.text` を取得する**
- 例えば **"公募期間"** の情報がどこにあるかがページごとに異なるため、`h2` のテキストを判別して対応。

```python
# `h2` タグを基準に関連する `div.text` を取得
sections = detail_soup.find_all("div", class_="st-article__elm")

for section in sections:
    h2_tag = section.find("h2", class_="c-ttl-02")  # h2 タグを取得
    text_div = section.find("div", class_="text")  # 直下の text クラスを取得

    if not h2_tag or not text_div:
        continue  # 必要な情報がなければスキップ

    heading = h2_tag.get_text(strip=True)  # h2 タグの内容

    if "公募期間" in heading or "申請受付期間" in heading:
        application_period = text_div.get_text(strip=True)
    elif "事業概要" in heading or "公募要領" in heading:
        summary = text_div.get_text(strip=True)
```
### ② Excel出力時に `False` になってしまう
#### **問題**
- `pandas.to_excel()` で出力すると、`FALSE` という値が入ることがあった。
- **`添付ファイル` の情報が `None` になっていた場合、自動的に `False` に変換されていた。**
- `pandas` は `None` や `bool` 型のデータを処理する際、自動的に `False` に変換してしまうことがあるため、  
  **適切にデータ型を統一する必要があった。**

#### **解決策**
- **`添付ファイル` のデータが `None` の場合は `"なし"` に変換** するように修正。
- **Excelに書き込む際に `False` や `None` が入らないように明示的に処理。**

```python
"添付ファイル": "\n".join(attachment_links) if attachment_links else "なし"
```

### ③ Excelでクリックできるリンクを作る
#### **問題**
- `pandas.to_excel()` で出力すると、URLがそのまま表示され、Excelではクリックできなかった。
- **Excelでリンクとして機能させるためには `HYPERLINK` 関数を使う必要があった。**
- `詳細ページ` や `添付ファイル` のリンクをそのまま出力しても、Excelではクリックできる形式にならなかった。

#### **解決策**
- **詳細ページのリンクを `HYPERLINK` 形式に変更することで、Excel上でクリック可能にした。**
- **添付ファイルのリンクも、HP上の表示名にし、HYPERLINK 形式で出力するようにした。**

この修正により、Excel上で以下のような形で表示されるようになった。(抜粋)

| タイトル | 募集開始日 | 添付ファイル |
|----------|------------|------------|
|**「宿泊施設サステナビリティ強化支援事業」の公募要領の公開と事業者公募の申請受付期間のお知らせ** | **[2025年3月10日]** | **"公募要領［PDF:1.8MB］** |

---

## まとめ
✅ **robots.txt や利用規約を確認してからスクレイピングを実施**  
✅ **ページのHTML構造を `h2` を基準に整理し、適切にデータを取得**  
✅ **Excelに `HYPERLINK` を用いて、クリック可能なリンクを出力**  
✅ **`False` を `"なし"` に統一し、Excelで適切な表示を実現**  
✅ **ChatGPTを活用しながら試行錯誤し、自分の知らない調べ方を学ぶことができた**  

---

## **終わりに**
この取り組みを通じて、単純なスクレイピングだけでなく、  
「**データを適切に扱い、最終的な出力をExcelに整形するまでをどう設計するか**」 についても考えることができました。  

また、ChatGPTに質問しながら進めたことで、  
「**どういうアプローチで問題を解決すればよいのか？**」という視点が身につきました。一方で、こちらの言うことが全然伝わらなかったり、出力されたものを別のプロンプトで指示をするのが大変と言うこともよくわかりました。  

結果的に、最終の出力をするまでに約６時間かかりましたが、それだけの価値がある学びが得られたと思います。丸投げで楽できるというのは大間違い💦それでも、この記事もchatGPTで助けてもらいながら書いているので、だいぶ楽できるなぁという実感はあります。   
今回の出力したファイルはこんな感じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4036728/5f6af2f5-1864-44d9-9598-6728a6a871b3.png)

---

### **今後の展望**
- **別の官公庁サイトの公募情報にも対応できるように、汎用性を持たせる**
- **データベース化し、定期的に新しい情報を取得するスクリプトを作る**
- **SlackやLINE通知と連携し、新しい公募が追加されたら通知が飛ぶようにする**

---