2022年8月20日土曜日

GAS で青果物卸売市場調査(日別調査)を自動取得してスプレッドシートに記録する。

青果物卸売市場調査(日別調査)のデータを自動取得してスプレッドシートに記録する GAS (Google Apps Script) を作成してみました。

元となるデータは農林水産省が公表しているものですが、(おそらく)国外からのアクセス制限が設けられているようで GAS で直接取得することができません(403 ERROR になります)。そこで cultivationdata.net の Web API を利用します。JSON 形式でデータを取得し、スプレッドシートに保存します。非常に便利ですねぇ(笑)。


・スプレッドシートの準備


1行目に見出しを作ります。A 列から H 列に「日付」「都市名」「品目コード」「産地コード」「数量 (Kg)」「価格 (円/Kg)」「対前日比数量 (%)」「対前日比価格 (%)」となります。

シート名は「data」としています。


・GAS スクリプトの作成


今回は「主要卸売市場計」の「野菜」から「生しいたけ」(品目名)の「総量」(産地名)を取得します。

function getWmrData() {
  let url = "https://api.cultivationdata.net/wmr?cc=0000&cat=v"; //「主要卸売市場計」「野菜」
 
  //JSONデータの取得
  let response = "";
  try {
    response = UrlFetchApp.fetch(url);
  } catch(e) {
    return response;
  }
  let data = JSON.parse(response.getContentText());

  if(data["Error"] == "都市のデータが見つかりませんでした。") {
    return
  }
  
  let wmr = [];

  //品目名「生しいたけ」
  //産地名「総量」
  wmr.push(data["Date"]); //日付
  wmr.push(data["CityName"]); //都市名
  wmr.push(data["生しいたけ"]["ItemCode"]); //品目コード
  wmr.push(data["生しいたけ"]["総量"]["ProductionAreaCode"]); //産地コード
  wmr.push(data["生しいたけ"]["総量"]["TradingVolume"]); //数量 (Kg)
  wmr.push(data["生しいたけ"]["総量"]["AveragePrice"]); //価格 (円/Kg)
  wmr.push(data["生しいたけ"]["総量"]["VolumeVersusPreviousDay"]); //対前日比数量 (%)
  wmr.push(data["生しいたけ"]["総量"]["PriceVersusPreviousDay"]); //対前日比価格 (%)

  return wmr;
}

function setWmrData() {
  const spreadsheet = SpreadsheetApp.openById("スプレッドシート ID");
  const sheet = spreadsheet.getSheetByName("data");
  let lastRow = sheet.getLastRow();
 
  if(getWmrData() != null) {
    //900行以上なら1行追加
    if(lastRow >= 900) {
      sheet.insertRows(lastRow + 1);
    }

    let wmrData = getWmrData();
    if(lastRow == 1) {
      sheet.getRange(lastRow + 1, 1, 1, 8).setValues([wmrData]);
    } else {
      let lastDate = sheet.getRange(lastRow, 1).getValue(); //前回の日付
      lastDate = Utilities.formatDate(lastDate, "JST", "yyyy/M/d");
      //日付が重複しなければスプレッドシートに入力
      if(wmrData[0] != lastDate) {
        sheet.getRange(lastRow + 1, 1, 1, 8).setValues([wmrData]);
      }
    }
  }
}

34行目の「スプレッドシート ID」を書き加えてください。


ちなみに、別のデータを取得したい場合は、2行目の「0000」「v」部分、及び23行目から28行目「生しいたけ」「総量」部分を書き換えてください。


・実行結果


トリガーを設定して毎日 setWmrData 関数を実行した結果が次の画像になります。8/14のデータが抜けているのは休市日のためです。



取得したデータの数量と価格を2軸の折れ線グラフにしたものが次の画像です。



いろいろ使えそうですね。


***

ということで、GAS による cultivationdata.net の Web API を利用した青果物卸売市場調査(日別調査)の自動取得方法でした。

青果物市況情報についても Web API がありますので似たような GAS で取得が可能です。

ご参考になれば幸いです。


詳解! Google Apps Script完全入門 [第3版]
詳解! Google Apps Script完全入門 [第3版]

by SimpleImageLink


・関連投稿
青果物卸売市場調査(日別調査)を JSON 形式で取得する Web API を作りました。

2022年8月17日水曜日

徒然じゃない日々(17)

農業(とその周辺分野)に役立つICT活用と開発にちょこちょこと取り組んでいるのですが、基本的に自分が使うものが多いので(費用を掛けないことに全振りして)収支は気にしていませんでした。

なんですが、最近、Web API のサービスなんかも始めてしまったので継続性の担保のためにも多少収益化を考えた方がいいのかもという気がしてきました。

それならそれで、せっかくなら「より便利」と「収益化」を両立したいな、とかとか考えた結果……とりあえず、栄養成分表示(推定値)の計算用スプレッドシートに商品へのリンクを追加してみました。

探り探りやっていきたいと思います。


田んぼ


Twitter (@nkkmd) 日々更新中です。

2022年8月14日日曜日

青果物卸売市場調査(日別調査)を JSON 形式で取得する Web API を作りました。(更新:CSV 形式での取得も可能になりました)

-----
・追記(2022-9-14)

CSV 形式での取得も可能になりました。詳しくは cultivationdata.net をご覧ください。
-----

先日作った青果物市況情報の Web API をちょっといじって、青果物卸売市場調査(日別調査)も Web API 化してみました。

この Web API を通せば国外にサーバーのある GAS (Google Apps Script) 等のサービスを使ったデータの取得が可能になります。公開しているので、どなたでも無料でお使いいただけます。


以下、今回作成した青果物卸売市場調査(日別調査) Web API の説明です。

https://api.cultivationdata.net/wmr?cc=値1&cat=値2

パラメーターやキーの説明はこちら → 青果物卸売市場調査(日別調査)API説明書

都市ごとの野菜 or 果実のデータを JSON 形式で取得できます。例えば、「仙台市」の「野菜」であれば、パラメーター1の都市 (cc) が「0401」、パラメーター2の種別 (cat) が「v」となります。



現状、最新データの取得のみ対応しています。更新は15時頃です。

エラー回避のため、本来は値が数値になる部分を含め全て文字列として扱っています。

今後、日付を遡れるようにしたり、JSON の構造を見直したりしていこうと思っています。その辺は随時 cultivationdata.net 上でやっていきます。


***

今回は、青果物市況情報の Web API のコードがほぼ使えたので、台風穏便に過ぎ去ってくれるといいなぁと思いながら数時間程度で作ることができました。




・関連投稿


Twitter (@nkkmd) 日々更新中です。

2022年8月10日水曜日

Flask + uWSGI + Nginx を使って Web アプリを作る【後編】 - Web アプリの作成

前編では WebARENA Indigo の設定を行いました。続きです。

Flask + uWSGI + Nginx を使って Web アプリを作っていきます。


▼ Web アプリ作成の下準備


必要なパッケージをインストール。
$ sudo apt update
$ sudo apt upgrade
$ sudo apt install python-is-python3
$ sudo apt install python-pip
$ sudo apt install python3-pip
$ sudo pip install flask
$ sudo pip install uwsgi
$ sudo apt install nginx

ホームディレクトリに app ディレクトリを作成。
$ mkdir app


▼ 常時 SSL 化(HTTPS 化)の下準備


私がドメインを取得している Xserver Domain と Xserver SSL での設定例になります。

Xserver Domain の DNSレコード設定で Web アプリに使うドメインに A レコードを追加(インスタンスの IP アドレス)。

SSL 証明書を Xserver SSL で申し込み。利用環境は他社サーバー。「取得申請」から「DNS認証」(TXT レコードを DNSレコード設定に追加)。

「SSL証明書」と「秘密鍵」をダウンロード。

「SSL証明書」と「秘密鍵」を app ディレクトリに設置。
> scp -P 2222 -i ~/.ssh/private_key.txt ~/Downloads/letsencrypt12345678.crt ユーザー名@IPアドレス:/home/ユーザー名/app/ (SSL証明書)
> scp -P 2222 -i ~/.ssh/private_key.txt ~/Downloads/letsencrypt12345678.key ユーザー名@IPアドレス:/home/ユーザー名/app/ (秘密鍵)



▼ Web アプリの作成


アプリの本体 main.py を app ディレクトリに作成。
from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return '<h1>WebARENA Indigo (Ubuntu) で Flask + uWSGI + Nginx を使って Web アプリを作る。</h1>'

if __name__ == "__main__":
    import ssl
    ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
    ssl_context.load_cert_chain(
        '/home/ユーザー名/app/letsencrypt12345678.crt', '/home/ユーザー名/app/letsencrypt12345678.key'
    )
    app.run(host='0.0.0.0', port=443, ssl_context=ssl_context)
※ ローカルでアプリのテストを行う場合は、10~15行目を「app.run()」に置き換えてください。インスタンス上での確認には curl や wget を使います。


▼ uWSGI の設定


アプリを呼び出す WSGI のエントリポイント wsgi.py を app ディレクトリに作成。
from main import app

if __name__ == '__main__':
    app.run()

uWSGI の設定ファイル app.ini を app ディレクトリに作成。
[uwsgi]
module = wsgi:app

master = true
processes = 5

socket = /tmp/myapp.sock
chmod-socket = 666
vacuum = true

die-on-term = true

wsgi-file = /home/ユーザー名/app/wsgi.py
logto = /home/ユーザー名/app/uwsgi.log
※ sock や log ファイルは自動で作成されます。


▼ Nginx の設定


サーバーブロック構成ファイル myapp.conf を /etc/nginx/sites-available ディレクトリに作成。
server {
    listen 80;
    server_name サーバー名(Web アプリのドメイン);
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    ssl on;
    server_name サーバー名(Web アプリのドメイン);
    ssl_certificate /home/ユーザー名/app/letsencrypt12345678.crt;
    ssl_certificate_key /home/ユーザー名/app/letsencrypt12345678.key;

    location / {
        include uwsgi_params;
        uwsgi_pass unix:///tmp/myapp.sock;
    }
}
※ HTTP (80番ポート) へのアクセスは HTTPS (443番ポート) にリダイレクトされます。

myapp.conf を /etc/nginx/sites-enabled ディレクトリにリンク。デフォルトの構成ファイルのリンクを削除。
$ sudo ln -s /etc/nginx/sites-available/myapp.conf /etc/nginx/sites-enabled
$ sudo unlink /etc/nginx/sites-enabled/default
※ nginx.conf から読み込まれるのは sites-enabled ディレクトリ内のファイルになります。

セキュリティ上好ましくないので Nginx のバージョンを表示させなくする。
$ sudo nano /etc/nginx/nginx.conf

http {
    ~
    server_tokens off; (コメントアウトを外す)
    ~
}

Nginx を起動。自動起動を有効化。
$ sudo systemctl start nginx
$ sudo systemctl enable nginx


▼ サービス化(デーモン化)

myapp.service を /etc/systemd/system ディレクトリに作成。
[Unit]
Description=uWSGI instance for myapp
After=syslog.target

[Service]
ExecStart=/usr/local/bin/uwsgi --ini /home/ユーザー名/app/app.ini
WorkingDirectory=/home/ユーザー名/app/

User=ユーザー名
Group=www-data
RuntimeDirectory=uwsgi
Restart=always
KillSignal=SIGQUIT
Type=notify
StandardError=syslog
NotifyAccess=all

[Install]
WantedBy=multi-user.target

myapp を起動。自動起動を有効化。
$ sudo systemctl start myapp
$ sudo systemctl enable myapp




ということで完成です。

uWSGI、Nginx あたりの理解がまだいまいちなのですが、とりあえず動かしながら学習していければと思います。




・関連投稿


・広告


Twitter (@nkkmd) 日々更新中です。

2022年8月9日火曜日

Flask + uWSGI + Nginx を使って Web アプリを作る【前編】 - WebARENA Indigo の設定

Flask + uWSGI + Nginx を使って 青果物市況情報 Web API を作りました。

それほど難しくないような気もしつつ、雰囲気で作ったらやはりところどころ突っかかったので、備忘録がてらざっくり設定手順を書いておこうと思います。

サーバーには、WebARENA Indigo という VPS を使用しました。OS は Ubuntu 22.04、クライアントは Windows 10 です。

同じような感じで突っかかっている方の参考になれば幸いです。


▼ WebARENA Indigo の設定


① 「SSH 鍵の作成」→「インスタンスの作成・起動」→「秘密鍵を使って SSH でログイン」

WebARENA Indigo のダッシュボードで SSH 鍵を作成・保存。インスタンスの作成・起動。秘密鍵をクライアントの「.ssh」に設置。

PowerShell にて SSH ログイン。初期ユーザー名は「ubuntu」。
> ssh ubuntu@インスタンスのIPアドレス -i ~/.ssh/private_key.txt


② タイムゾーンの変更と日本語化

タイムゾーンを Asia/Tokyo に変更。
$ sudo timedatectl set-timezone Asia/Tokyo

日本語パッケージをインストールして設定。
$ sudo apt install language-pack-ja-base language-pack-ja ibus-mozc
$ sudo localectl set-locale LANG=ja_JP.UTF-8


③ WebARENA Indigo でファイアーウォールを設定をする

WebARENA Indigo のダッシュボードからファイアーウォールを作成(「ネットワーク管理」内)。

SSH 及び Web アプリで使うポートを開けます(HTTP「80」、HTTPS「443」)。SSH はセキュリティ向上のため22番ポートから変更したいため、22番ポートと任意のポートを開けておきます。後ほど22番は閉じます。IP アドレスは「0.0.0.0」。

インスタンスに適用。


④ SSH のポートを変更

sshd_config のポートを変更して再起動。
$ sudo nano /etc/ssh/sshd_config

#Port 22
Port 2222 (任意のポート番号)

$ sudo systemctl restart sshd

PowerShell の別ウィンドウを開き、変更後のポートからアクセス。
> ssh -p 2222 ubuntu@IPアドレス -i ~/.ssh/private_key.txt

無事にログインできたら WebARENA Indigo のファイアーウォール設定で22番ポートを閉じます。


⑤ ufw でファイアーウォールを設定する

ufw をインストール。
$ sudo apt install ufw

incoming ポリシーを deny (破棄) に設定。
$ sudo ufw default deny

IPv6 を無効化。
$ sudo nano /etc/default/ufw

IPV6=no

必要なポートを開放。
$ sudo ufw allow 2222
$ sudo ufw allow 80
$ sudo ufw allow 443

ufw を有効化。
$ sudo ufw enable


⑥ 「ユーザーを作成」→「SSH ユーザー化」→「初期ユーザーの削除」

初期ユーザーのままだとセキュリティ上よろしくないので、新たにユーザーを作成します。パスワードを2回入力(その他は空欄でも問題なし)。sudo 権限を付与。
$ sudo adduser 新ユーザー名
$ sudo gpasswd -a 新ユーザー名 sudo

ubuntu (ユーザー名) の SSH 公開鍵を新しいユーザーにコピー。所有者を変更。
$ sudo cp -r .ssh/ /home/新ユーザー名/
$ sudo chown -R 新ユーザー名:新ユーザー名 /home/新ユーザー名/.ssh/

新ユーザーで SSH ログインが可能になります。
> ssh -p 2222 新ユーザー名@IPアドレス -i ~/.ssh/private_key.txt

scp はこうなります。(後編で使います)
> scp -P 2222 -i ~/.ssh/private_key.txt ./Desktop/test.txt 新ユーザー名@IPアドレス:/home/新ユーザー名/ (インスタンスへファイルをコピー)
> scp -P 2222 -i ~/.ssh/private_key.txt 新ユーザー名@IPアドレス:/home/新ユーザー名/test.txt ./Desktop/ (クライアントへファイルをコピー)

初期ユーザーの削除。他のプロセスで使用中とのメッセージが出た場合は sudo kill してから再度削除。
$ sudo userdel -r ubuntu




ということで、WebARENA Indigo の設定でした。

次の後編では、突っかかった Flask + uWSGI + Nginx の部分を作っていきます。




・関連投稿


Twitter (@nkkmd) 日々更新中です。

2022年8月6日土曜日

青果物市況情報を JSON 形式で取得する Web API を作りました。(更新:CSV 形式での取得も可能になりました)

-----
・追記(2022-9-14)

CSV 形式での取得も可能になりました。詳しくは cultivationdata.net をご覧ください。
-----

青果物市況情報は農林水産省が公開しています。しかしながら、URL や HTML の構造等により若干扱いづらいのですよね(機械にとって)。そのため、スプレッドシートで直接読み込むというようなことはできません。

また、国外のサーバーからのアクセスはブロックされるようで、GAS (Google Apps Script) での利用もできません。

今回はそうした扱いづらさをなんとか突破し、公開データの中にある救いの CSV を利用して、より使いやすいように JSON 形式でデータを取得できる Web API を作ってみました。公開しているので、どなたでも無料でお使いいただけます。

同様に JSON 形式で青果物市況情報を取得できるサービスには WAGRI があります。ただし、データ利用には40,000円/月の利用料金がかかります(青果物市況情報以外にもデータはたくさんあります)。また、説明を読むに基本的には農家が個人で使うという想定はされていないようです。


以下、今回作成した青果物市況情報 Web API の説明です。

https://api.cultivationdata.net/mcdata?mc=値1&cat=値2

パラメーターやキーの説明はこちら → 青果物市況情報API説明書

-----
・追記(2022-8-13)

一部休市日のデータ取得の不具合を修正しました。パラメーターの仕様が変更になりました。
-----

市場ごとの野菜 or 果実のデータを JSON 形式で取得できます。例えば、「盛岡市中央卸売市場」の「野菜」であれば、パラメーター1の市場 (mc) が「03300」、パラメーター2の種別 (cat) が「v」となります。


取得できる JSON はこのような感じになります。



国外サーバーからのアクセスもできますので、GAS 等での利用も可能です。

現状、最新データの取得のみ対応しています。更新は15時頃です。

エラー回避のため、本来は値が数値になる部分を含め全て文字列として扱っています。

今後、日付を遡れるようにしたり、JSON の構造を見直したりしていこうと思っています。その辺は随時 cultivationdata.net 上でやっていきます。


***

ここからは作ってみての感想等々。

農林水産省の青果物市況情報が公開されているページの URL は「.do」という拡張子になっており、これは Java による Web アプリケーションとのこと。裏でいろいろと処理をしているということですね。なのでスプレッドシートの IMPORTXML 関数で素直に値を取得するといったことが(たぶん)できません。

そこで GAS(Google Apps Script) を使ってページの遷移を追いかけデータを取得しようと試みたところ、

"The Amazon CloudFront distribution is configured to block access from your country."(Amazon CloudFront ディストリビューションは、あなたの国からのアクセスをブロックするように設定されています)

というメッセージが出て弾かれました。おそらく、国内からしかアクセスできないようになっているのですね。GAS は Google のサービスですので、確認していませんがサーバーは米国ですかね。

ならばと Google Cloud Functions(GCP) でリージョンを東京に設定。Python でスクリプトを作り再度アクセスするも……またもやだめ。

なぜ……?と思って調べみると、


こういうことらしいです。根本的選択ミス。

かくなる上は、Raspberry Pi で自宅サーバーをたてるか、Amazon Lambda(AWS) か、さくらの VPS か……と、コストや学習にかかる時間を勘案しつつあれこれ検討していたところ、WebARENA Indigo という安価で使い方も簡単そうな VPS を発見。

インスタンスの料金(Linux)は、メモリ 1GB、1vCPU、SSD 20GB で1時間あたり0.55円(税込)、月で349円(税込)です。

このサービスの存在を一切知らなかったのですが、運営会社は NTTPCコミュニケーションズ という会社で、NTTコミュニケーションズの子会社でした。サーバーの設置場所は東京。大丈夫そう。早速契約。

Web API は、ネット上に参考になる情報がけっこうあった Flask + uWSGI + Nginx で作成。ここの設定で2日くらいエラーにはまったのですが、分かってしまえば本当に何てことのない原因で……動けばいいやと雰囲気で作っているとこういうことになるのですよねぇ。

何はともあれ、そんなこんなで無事に動かすことができたのでよかったです。この Web API を通すことで国外のサーバーからでもデータの取得が可能です。

しばらく様子を見て、調子が良さそうなら Heroku で動かしている気象データの Web API も移そうかと思っています。


今後も農家がデータを活用しやすいような環境をコツコツ作っていきます。


・関連投稿




Twitter (@nkkmd) 日々更新中です。