3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ゆるゆる新卒1年生Advent Calendar 2019

Day 20

今日からできる定点観測するためのデータ集め

Last updated at Posted at 2019-12-21

はじめに

エンジニアだといろんな事を自動化したいと思う事があるけれど「どれからやったらいいかわからん」みたいな事があると思うので
ちょっとしたサンプルコードを交えながらGoogleAppsScript(GAS)+SpreadSheetでサクッとできる事について
「季節の変わり目で服装選びによくミスしてしまって、寒すぎて後悔しちゃう...」というユースケースに沿って書いていきます

GAS+SpreadSheetのいいところ

先に「なんでGASなの?」という疑問については理由が2つあって

  • 基本的に無料
  • Googleのサーバ上で動く

こちらでGASが1日で行える制限が記述されているのですが
基本的今回紹介する内容だと制限を大きく下回り、仮に今回以外の事でも大掛かりな事をしない限り超えることはないので基本的に無料で使用できると考えていいでしょう

あとは書いたプログラミングを実行する環境が必要ないので、色んな事を意識せずに実行できるのはいいことですね

データを貯める

「今日は昨日より晴れてるやん、絶対あったかいやん!!」

『寒すぎでは?』

割と天気を見て服装を決めがちですが、実際に外に出てみたら寒いってよくありませんか?
その時に皆さんは何で判断しますか?
賢い人は【気温】、もっと賢い人は気温に加えて【湿度】で判断すると思います。

じゃあ、気温の暑い寒いの判断って...?
「気温:15度が寒い」というのは人によって異なるはずで、「昨日が10度だったけれどコート着てちょうど良いから、今日は(昨日より)時薄手のジャケットにしておこう」などと考えるはず
じゃあ、今日の天気をデータとして貯めれば明日、あるいは明後日、来年の同じ季節に役に立つのでは?

### 手順①: GASと仲良くなろう
取得して来たデータを最終的にSpreadSheetに出力するのでGASを使ってSpreadSheetを操作を行いましょう

適当なシートを作成して[ツール]→[スクリプトエディタ]を選択肢、GASのエディタを開きましょう
<img width=700 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199819/01c9b5d8-7b96-e8a0-c898-392fd63159ed.png">

基本的にGASに様々な権限(シートへの書き込み、通信など)を与えて開発していきます、なので途中で権限を確認するダイアログが表示されますが確認して進んでいきましょう

実際にコードを確認するときは下記のようにメッセージボックスやログを表示させると良いでしょう

```javascript
function myFunction() {
  Browser.msgBox('スプレッドシートにダイアログ表示')
  Logger.log('GASのログ表示')
}
```
また今回使用するシートへの書き込みなどは[公式のガイド](https://developers.google.com/apps-script/guides/sheets)を見ればおおよそできると思いますがサンプルコードも載せておきます

```javascript
function myFunction() {
  const spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
  // sheet名を変更したため、名前で取得するようにしている
  const outputSheet = spreadSheet.getSheetByName('output_sheet');
  
  // Sheetのデータがある最終行 + 1(挿入位置)を取得
  const insertRowIndex = outputSheet.getLastRow() + 1;
  
  const testData = ['apple', 'banana', 'chocolate'];

  // getRange(x行目, y列目, x`行数, y`列数)
  const range = outputSheet.getRange(insertRowIndex, 1, 1, testData.length)

  // rangeで取得すると右記のようになるので二次元配列に整形 [[1行分のデータ],[1行分のデータ]]
  const insertData = [testData]
  range.setValues(insertData)
}
```

これを実行するたびに末尾にデータが入っていくので必要であれば`testData`の箇所を適宜変更していきましょう

### 手順②: 天気APIを見つけて来ましょう
では実際に挿入するデータを探していきましょう、今回は気温、湿度についてなので天気に関するAPIを探します
[天気 API]とかで調べるといくつか出てくると思いますが今回は[openweathermap](https://openweathermap.org/)を使います
(簡易に使えそうなのであればyahooの天気APIも良さそうです)

openweathermapだとアカウント作成 → プラン選択 → APIKEY取得という順番で行っていくはずです
関連URL https://openweathermap.org/appid

このサイトだと[API呼び出しの例](https://openweathermap.org/current)があるので一時的にそれで確認するのも人によってはあるのかもしれません

実際にレスポンス内容を確認すると下のようになっており、listの配列に今日から5日後までの3時間毎の天気の情報が入っています



<details><summary>レスポンスの例</summary><div>


```json:response
{
  "cod": "200",
  "message": 0,
  "cnt": 40,
  "list": [
    {
      "dt": 1576908000,
      "main": {
        "temp": 283.64,
        "feels_like": 279.72,
        "temp_min": 283.64,
        "temp_max": 283.81,
        "pressure": 1024,
        "sea_level": 1024,
        "grnd_level": 1011,
        "humidity": 45,
        "temp_kf": -0.17
      },
      "weather": [
        {
          "id": 804,
          "main": "Clouds",
          "description": "overcast clouds",
          "icon": "04d"
        }
      ],
      "clouds": {
        "all": 100
      },
      "wind": {
        "speed": 2.57,
        "deg": 274
      },
      "sys": {
        "pod": "d"
      },
      "dt_txt": "2019-12-21 06:00:00"
    },
    {}
  ]
}
```
</div></details>

### 手順③: GASで天気APIのデータを取得する
GASでリクエストを送るときは[UrlFetchApp](https://developers.google.com/apps-script/reference/url-fetch
)を使用すると良さそうです
例としては下記のように記述するようです

```javascript
const URL = 'https://qiita.com'
UrlFetchApp.fetch(URL)
```
また、通信する際にも権限を確認するので問題ない事を確認してOKしてあげましょう

`getContentText`とするとresponseのbodyにあたる部分を取得できるので試してみましょう

```javascript
const URL = 'https://qiita.com'
const responsBody = UrlFetchApp.fetch(URL).getContentText()
Logger.log(responsBody)
```

ただ、今回はopenweathermapはjson形式で返却してくれるので`Json.parse(responsBody)`と変換してあげ
欲しいデータを返却用の配列に追加しておきましょう
今回は「日時、気温、体感気温、最低気温、最高気温」の5つのデータを取得して配列で返してあげましょう
そうすると手順①の`testData`の箇所をこの関数を呼ぶだけで実行できます

実際に一連の流れを記述したコードは下記になります

```javascript:getData

function getData(){
  const CITY_ID = '適宜変更' 
  const API_KEY = '適宜変更'
  const URL     = 'http://api.openweathermap.org/data/2.5/forecast?' + 
                  'id=' + CITY_ID +
                  '&APPID=' + API_KEY

  const returnData = []
  try {
    const response = UrlFetchApp.fetch(URL).getContentText()
  }catch(e){
    Browser.msgBox(e)
    throw e
  }

  var convertData = JSON.parse(responseBody);
  const weatherInfo = convertData.list[0];
  const data = weatherInfo.main
  
  returnData.push(weatherInfo.dt_txt)
  returnData.push(data.temp)
  returnData.push(data.feels_like)
  returnData.push(data.temp_min)
  returnData.push(data.temp_max)
  
  return returnData
}
```

これで一通りのやりたいことはできましたが、いくつか足りない箇所があると思います
例えば、今回のAPIでは気温がケルビン表記になっているのでconvert関数みたいなのを作ったり
取得したUNIXタイムスタンプがおかしかったりと、そこについては最後にサンプルを載せるので参考にしてください



<details><summary>現在のソースコード</summary><div>


```javascript:main
function main() {
  const spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
  // sheet名を変更したため、名前で取得するようにしている
  const outputSheet = spreadSheet.getSheetByName('output_sheet');
  
  // Sheetのデータがある最終行 + 1(挿入位置)を取得
  const insertRowIndex = outputSheet.getLastRow() + 1;
  
  // ※ ここで下のgetDataを呼び出している
  const insertData = getData();

  // getRange(x行目, y列目, x`行数, y`列数)
  const range = outputSheet.getRange(insertRowIndex, 1, 1, insertData.length)

  // rangeで取得すると右記のようになるので二次元配列に整形 [[1行分のデータ],[1行分のデータ]]
  range.setValues([insertData])
}
```

```javascript:getData
function getData(){
  const CITY_ID = '適宜変更' 
  const API_KEY = '適宜変更'
  const URL     = 'http://api.openweathermap.org/data/2.5/forecast?' + 
                  'id=' + CITY_ID +
                  '&APPID=' + API_KEY

  const returnData = []

  // Errorが発生した際にダイアログ表示するように設定
  try {
    const response = UrlFetchApp.fetch(URL).getContentText()
  }catch(e){
    Browser.msgBox(e)
    throw e
  }

  var convertData = JSON.parse(responseBody);
  const weatherInfo = convertData.list[0];
  const data = weatherInfo.main
  
  returnData.push(weatherInfo.dt_txt)
  returnData.push(data.temp)
  returnData.push(data.feels_like)
  returnData.push(data.temp_min)
  returnData.push(data.temp_max)
  
  return returnData
}
```

</div></details>

## 分析する
データが手に入ったのであとはグラフにおこしたりして見やすくするだけです

色々操作して、4日分の天気を取得してきましたのでこのままグラフにしてみましょう
<img width="700" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199819/9141102d-8b3a-8382-b1d7-bb10d58ee37d.png" />

まあ、悪くはないですね
今回であれば日にち単位でみたいので、ピボットテーブルを使用して日毎に集計して表示させるようにしましょうか

まずはデータの最後に、日毎に集計できるように日付だけの列を作成します
<img width=400 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199819/231d8fa0-f45f-c5ac-1fda-393095610da6.png"/>

次に右下のデータ探索からピボットテーブルを作成しましょう
<img width=700 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199819/330e602d-d88f-5478-9c84-ff8980076ce5.png" />

あとは適宜列に追加して、再度グラフを作成したら完了です
<img width=700 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199819/f437cc35-0011-396e-cd6a-d3a5ac822dea.png" />


## 最後に
いかがだったでしょうか、GASはいろんな人が使っているので今更という内容ではありますが
こういったデータを日頃に役立てるのもっと生活を豊かにできるのではないかと思います

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?