GASでお手軽なAPIを作ってJSで叩いてみる

備忘録ついでに書いておきます。

無料で簡単にJSONを返してくれるAPIを作りたかったので、GAS（Google Apps Script）を利用してみました。全体的な流れとしては、weblioの英和辞書で、検索した単語の意味一覧をJSONで返すAPIを作り、それをローカルのJSから取得します。ライブラリは使いたくなかったのでaxiosなどは使用していません。

#GASでスクレイピング
まずは[GAS](https://script.google.com/home/start)へアクセスして新規プロジェクトを立ち上げましょう。そして、スクレイピングするために「Parser」というライブラリを導入します。「ライブラリの追加」から、スクリプトIDに「1Mc8BthYthXx6CoIz90-JiSzSafVnT6U3t0z_W3hLTAX5ek4w0G_EIrNw」で検索して追加することができます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/045af87b-91f5-bd39-3c7a-c274277a05d8.png" width=400 />

次に、スクレイピングする対象であるweblioの構造を見ていきます。
まず、URLに注目すると`https://ejje.weblio.jp/content/(単語名)`で、調べたい単語の意味一覧にに飛べるようです。
また、ページのソースを見ると、`<div class=level0>`のタグの中に意味が格納されていることが分かりました。
構造が大体わかったので、実際にスクレイピングしてみます。

```javascript
function doGet(e) {
  // スクレイピング対象のurlを定義
  let url = `https://ejje.weblio.jp/content/${e.parameter.word}`;

  // HTMLの取得
  let html = UrlFetchApp.fetch(url).getContentText();
  // 要素の抽出
  let data = Parser.data(html)
  .from('<div class=level0>')
  .to('</div>')
  .iterate();
}
```

`${e.parameter}`でGETリクエスト時のパラメータを受け渡すことができます。渡す際はURLの末尾に`?word=(検索したい単語)`を追加してアクセスします。
html要素の抽出は`UrlFetchApp.fetch(url).getContentText();`で行い、同一のタグが複数ある場合は`Parser.data(html).from(タグ名開始).to(タグ名終了).iterate()`で配列にまとめることができます。

#スクレイピングしたデータをJSONにして返す
前項の`data`ではまだ、不要なタグや配列の形式のままになっているので、正規表現で不要部分を削除する作業と、データを後で扱いやすいようにするためにオブジェクト形式にしてキーを追加する作業を行いたいと思います。
まずは正規表現を使ってタグを消し、リストにまとめていきます。

```javascript
const dataList = []
for (const content in data) {
  dataList.push(data[content].replace(/<("[^"]*"|'[^']*'|[^'">])*>/g,''));
}
```
次に、dataListをオブジェクトにして一つずつキーを追加していきます。

```javascript
const newDataList = dataList.map((word, key) => ({key, word}));
```
最後に、JSON形式に変換して返します。

```javascript
const json_data = JSON.stringify(newDataList);
return ContentService.createTextOutput(json_data).setMimeType(ContentService.MimeType.JSON);
```
保存したら「新しいデプロイ」から「ウェブアプリ」を選択し、アクセスできるユーザーを「全員」にして保存してください。
すると、このようなURLが発行されます。
![gas2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/2cd289a6-736b-5e44-210d-e1fa3633691b.png)

コピーして末尾に`?word=english`を追加して飛んでみます。
![gas4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/3f507610-5e70-a1f2-e5aa-c1bcda66a590.png)
無事、「english」の意味だけ抽出されました！（品詞名のノイズが入っていますが…）

#Javascriptから取得してみる
意味を抽出してJSONを返してくれるAPIは完成したので、今度はローカルのJSからフェッチしてみます。※下ではReactを使用していますが、関数部分は生のjsでも同じはずです

```react
const [answerList, setAnswerList] = useState([]);
const [word, onChangeWord] = useState('');
const url = 'https://script.google.com/macros/s/xxxxx/exec';

// urlに検索したい単語の文字列を追加してjsonをフェッチする
const fetchAnswer = async () => {
    const submitUrl = url + '?word=' + word;
    return fetch(submitUrl)
    .then(res => res.json());
}

// 検索ボタンを押したときにfetchAnswer関数を実行し、リストに入れる
const onPressFetch = () => {
    (async() => {
        setAnswerList(await fetchAnswer());
    })();
}

// 省略

<View>
    <Input onChangeText={onChangeWord} 
        value={word}
    />
    <Button title="検索" 
        onPress={onPressFetch} 
    />
</View>
```
Inputに入力された文字をパラメータとしてGASに渡し、GASが返却したJSONをfetchAnswerでフェッチしています。フェッチしたものをリストに入れて良い感じに表示すると下のようになりました！

![gas5.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1711764/0988998c-60cf-6b99-fbbf-31551d69b5c6.gif)