Qiita API と Google Apps Script を使って投稿データのリストを作成する

# Qiita API と Google Apps Script を使って投稿データのリストを作成する

こんにちは、@studio_meowtoon です。今回は、Qiita API と Google Apps Script を使って投稿データのリストを作成する方法を紹介します。
![qiita-api_with_google-apps-script.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/e6f37ec6-1888-e1ea-7012-bad3a540ea12.png)

## 目的

Qiita の投稿データを効率的に管理することを目的とします。

## 実現すること

Qiita API を使って、自分の投稿データ (タイトル、閲覧数、いいね数、保存数など) を取得します。取得したデータを Google スプレッドシートに保存します。

## 必要な準備

Qiita API トークンの取得

[アクセストークンの発行](https://qiita.com/settings/tokens/new)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/2ffeb0e1-f2bc-c160-6e2d-e429e4587b8b.png)

Qiita の 設定ページ で新しいアクセストークンを作成し、トークンをメモしておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/682bea91-e866-8b34-511a-1ef6fadce97c.png)

## Google スプレッドシートの作成

新しいスプレッドシートを作成し、「list」という名前のシートを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/bd646c4c-54cd-b12e-e6c9-2e77727d06b4.png)

## 手順

### Google Apps Script プロジェクトの作成

Google スプレッドシートを開きます。
メニューから [拡張機能] > [Apps Script] を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/5f5552fe-f85c-e4a3-5dc2-9f9b68d55f84.png)

新しいプロジェクトを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/f75d28db-1b21-f75f-068b-25628fcb023d.png)

### スクリプトの記述

以下のスクリプトを Code.gs ファイルに貼り付けます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/57d3ca62-b703-94ba-4147-b10caf7d311e.png)

```js:コード.gs
// Qiita API のトークンを設定
const QIITA_API_TOKEN = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; // 自分のQiita APIトークンに置き換えてください
const QIITA_API_URL = 'https://qiita.com/api/v2/authenticated_user/items';

// Qiita API から投稿データをページネーションを使って取得する関数
function fetch_all_qiita_posts() {
  let page = 1; // 初回ページ
  const per_page = 100; // 1リクエストあたりの最大取得件数
  let all_posts = []; // すべての投稿を格納する配列
  
  while (true) {
    const url = `${QIITA_API_URL}?page=${page}&per_page=${per_page}`; // APIのリクエストURL
    const options = {
      method: 'get',
      headers: {
        'Authorization': 'Bearer ' + QIITA_API_TOKEN, // APIトークンをヘッダーに設定
      }
    };
    
    try {
      // Qiita API からデータを取得
      const response = UrlFetchApp.fetch(url, options);
      const posts = JSON.parse(response.getContentText()); // レスポンスをJSONとしてパース
      
      if (posts.length === 0) {
        break; // 投稿が0件ならループを終了
      }
      
      // プライベートが false の投稿のみフィルタリング
      const public_posts = posts.filter(post => !post.private);
      all_posts = all_posts.concat(public_posts); // 取得した投稿をすべての投稿に追加
      Logger.log(`Fetched page ${page}: ${public_posts.length} public posts`); // ログに取得件数を出力
      page++; // 次のページへ
    } catch (error) {
      // エラーが発生した場合の処理
      Logger.log('Error fetching posts: ' + error); // エラーログを出力
      break; // ループを終了
    }
  }
  
  Logger.log('Total posts fetched: ' + all_posts.length); // 総取得件数をログに出力
  return all_posts; // すべての投稿を返す
}

// 取得したデータをスプレッドシートに書き込む関数
function save_posts_to_sheet() {
  try {
    // スプレッドシートからシートを取得
    const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('list');
    if (!sheet) {
      Logger.log('Sheet "list" not found.'); // シートが見つからない場合の処理
      return;
    }
    Logger.log('Sheet "list" found.'); // シートが見つかった場合の処理
    
    const posts = fetch_all_qiita_posts(); // 投稿データを取得
    
    if (posts.length === 0) {
      Logger.log('No posts to write to the sheet.'); // 投稿がない場合の処理
      return;
    }
    
    // シートをクリア
    Logger.log('Clearing sheet.');
    sheet.clear();
    
    // ヘッダー行を設定
    const headers = [
        'id', // 投稿ID
        'title', // 投稿タイトル
        'tags', // タグ
        'created_at', // 作成日時
        'updated_at', // 更新日時
        'views', // 閲覧数
        'likes', // いいね数
        'stocks', // 保存数
        'url' // 投稿URL
    ];
    sheet.appendRow(headers); // ヘッダーをシートに追加
    Logger.log('Headers written to the sheet.'); // ヘッダー書き込み完了のログ
    
    // 投稿データを配列に変換
    const rows = posts.map(post => {
      const id = post.id; // 投稿ID
      const title = post.title; // 投稿タイトル
      const tags = post.tags.map(tag => tag.name).join(', '); // タグをカンマ区切りで連結
      const created_at = Utilities.formatDate(new Date(post.created_at), 'Asia/Tokyo', 'yyyy-MM-dd HH:mm'); // 作成日時をフォーマット
      const updated_at = Utilities.formatDate(new Date(post.updated_at), 'Asia/Tokyo', 'yyyy-MM-dd HH:mm'); // 更新日時をフォーマット
      const views = post.page_views_count || '0'; // 閲覧数（存在しない場合は0）
      const likes = post.likes_count || '0'; // いいね数（存在しない場合は0）
      const stocks = post.stocks_count || '0'; // 保存数（存在しない場合は0）
      const url = post.url; // 投稿URL
      return [id, title, tags, created_at, updated_at, views, likes, stocks, url]; // 配列として返す
    });
    
    // データ行を一括でシートに追加
    sheet.getRange(2, 1, rows.length, rows[0].length).setValues(rows);
    Logger.log('All posts written to the sheet.'); // すべての投稿が書き込まれたログ
  } catch (error) {
    // エラーが発生した場合の処理
    Logger.log('Error in save_posts_to_sheet: ' + error); // エラーログを出力
  }
}
```

### スクリプトの実行

スクリプトエディタのツールバーから関数を選択し、 [save_posts_to_sheet] を選択します。実行ボタンをクリックしてスクリプトを実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/ae3d4bec-dd05-d304-c74e-dd62ce0b1aaf.png)

初回実行時には、スクリプトが必要な権限を要求するので、指示に従って許可します。

### 結果の確認

スクリプトが正常に実行されると、スプレッドシートの [list] シートに Qiita の投稿データが書き込まれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/ef00796e-f032-4fa8-2a7a-d1d24d4e7d0c.png)

## まとめ

:::note info
Google Apps Script を使用して Qiita API から投稿データを取得し、Google スプレッドシートに保存する方法がわかりました。
:::

## おまけ：Google Apps Script を VSCode で編集する

VSCode のインストール

https://qiita.com/studio_meowtoon/items/e3aaef417f93afb79066

npm のインストール

https://qiita.com/studio_meowtoon/items/111f556d8fb11a76430a

## Google Apps Script API をオンにする

[Apps Script 設定](https://script.google.com/home/usersettings)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/be9d7616-9066-074c-3148-c402240f324b.png)

## clasp のインストール

https://github.com/google/clasp

WSL の Ubunts に npm で clasp をインストールします。

```sh
$ sudo npm install -g @google/clasp
$ clasp -version
2.4.2
```

## Google アカウントでログイン

Google アカウントで clasp にログインします。

```sh
$ clasp login
```

## GAS プロジェクトのクローン

既存のプロジェクトを編集する場合、[プロジェクトのスクリプトID] を使用してクローンします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/5470f4b9-45fa-976c-be4c-807a8d7d9bf9.png)

ファイルの名前を [Program.gs] に変更しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/d916d04c-a368-5b8c-8aaa-3511d52ac4f2.png)

```sh
$ mkdir ~/qiita-posts
$ cd ~/qiita-posts
$ clasp clone {スクリプトID}
Warning: files in subfolder are not accounted for unless you set a '/home/$USER/qiita-posts/.claspignore' file.
Cloned 2 files.
└─ /home/$USER/qiita-posts/appsscript.json
└─ /home/$USER/qiita-posts/Program.js
Not ignored files:
└─ /home/$USER/qiita-posts/appsscript.json
└─ /home/$USER/qiita-posts/Program.js

Ignored files:
└─ /home/$USER/qiita-posts/.clasp.json
```

## VSCode で編集

スクリプトファイルを VSCode で開いて編集します。

```sh
$ ls -la
合計 24
drwxr-xr-x  2 $USER $USER 4096  7月 20 22:49 .
drwxr-x--- 13 $USER $USER 4096  7月 20 22:51 ..
-rw-------  1 $USER $USER  113  7月 20 22:49 .clasp.json
-rw-r--r--  1 $USER $USER 4593  7月 20 22:55 Program.js
-rw-r--r--  1 $USER $USER  118  7月 20 22:49 appsscript.json
$ code .
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/bde191a4-b94f-6cd7-d2d5-3f0a2e8ac96d.png)

## 変更のプッシュ

以下のコマンドでリモートにプッシュします。  

```sh
$ clasp push
```

:::note warn
Git 管理されている状況ではありません！
:::

## まとめ2

:::note info
VSCode から Google Apps Script のコードを編集してリモートにプッシュすることが出来ました。 これにより、VSCode の豊富な機能を使用して GAS の開発が可能となります。また、別途 Git で編集の履歴を管理することもできます。
:::