madokaのブログ

勉強したことのoutput先として使ってます。内容はpythonがらみが多いかもです。

Notionのススメ

半年前くらいに機能の多さに惚れて、Dropbox PaperからNotionに移行しました。なかでもNotionの推しであるデータベースとメモを結びつける管理がとても便利ですっかりNotionに惚れ込んでしまいました。今回はそのNotionのデータベースの使い方について紹介していきたいと思います。

Notion とは

メモやドキュメント管理からタスク、プロジェクトの管理まであらゆるものを集約して管理できるアプリケーションです。このNotionの強みは何と言ってもデータベースの機能で、作成日やステータスに限らず様々なデータを合わせて保存することができます。

www.notion.so

Notionのデータベース とは

エクセルの表のように作成日やステータスなど様々な情報について保存しておくことができ、それに加えてその一行ごとにページを関連づけることができます。また、このデータの表示方法は表形式に限らず、リスト形式やボード形式あるいはギャラリーのようにも表示ができます。 この一行ごとにページを関連づける機能がとても秀逸で、これによって欲しいページが探しやすくなり利便性を上げているように思います。

わたしのNotionの使い方

まず、データベースの旨みを生かすためにプロジェクトごとにそれぞれデータベースを作るのではなく、全て同じデータベースでステータスにプロジェクトを入力するという管理方法がオススメです。Notionではデータベースに複数の表示方法を定義でき、それらについてソートやフィルター情報を保存しておくことができるため、表示方法を変えることでプロジェクトを絞り込んだリストを表示したり、プロジェクトに関わらず最近編集したページを表示したりできます。この表示方法の定義によりむしろ一緒くたにした方が自由度が高い表示ができるというわけです。

今回はこれまで試行錯誤してきて最終形態になったと感じている、議事録とタスクの管理について紹介していきたいと思います。

議事録を管理

先ほど述べたように議事録は一緒くたに管理しています。データ項目にプロジェクト、作成日、変更日という項目を作り、プロジェクトでも日付でも絞り込みができるようにもしています。表示方法はリスト形式もいいですが、意外と気に入っているのがギャラリー表示です。ギャラリー表示だとページの上部が表示されるので、ページの上部に概要を書いておけば議事録をざっと見回すことができます。

Notionで議事録をギャラリー表示
Notionで議事録をギャラリー表示

タスクを管理

例のごとくタスクも一緒くたに管理しています。データ項目にはプロジェクト、作成日、変更日の他に、ステータス、締め切り日、議事録とのリレーション、子タスクも作れるようにタスク自身とのリレーションを作っています。

まずこのタスク管理の素晴らしいところは何よりもタスクとメモを同時に管理できるところです。タスクを探し出すことができればメモを参照することができるのです。タスク名がわかればタスク名で検索できますし、日付による検索も可能です。データを自由につけることができるため、様々な角度から検索ができ検索が捗ります。

またこのリレーションという機能、関連するドキュメントを結びつけることができるため、記憶を辿るのにとても便利になっています。MTGでの仕様変更時にタスクとの関連付けを行なっておけば、タスクを行う際に見返しやすくなります。

子タスクの関連付けには見積もりに使っています。それぞれの子タスクで見積もりをすれば、rollupという機能で親タスクで総見積もりがわかるようになっています。Notionならデータの集計もできるというわけです!

Notionでタスク管理
Notionでタスク管理

まとめ

今回はNotionのデータベースの使い方について紹介しました。Notionがドキュメントを管理する一つの候補になれば良いなと思います。

yarnとnpmの違いって?

新しく配属されたプロジェクトでパッケージマネージャはyarn使ってるからーと言われて、今までnpmしか使ったことがなかったのでなんなのそれってことで調べてときの記録を書くます。ついでにnode.js使ってるわりにサーバ側のjsくらいの知識しかなかったのでこちらも一緒に。 ​

Node.jsとは

JavaScriptは本来クライアントサイドで動く言語であり、HTMLで書かれたページに動きをつけたりします。 しかしこれに対してNode.jsはサーバサイドで動くJavaScriptです。 ​ https://nodejs.org/ja/about/

Node.js はスケーラブルなネットワークアプリケーションを構築するために設計された非同期型のイベント駆動の JavaScript 環境です。 以下の「Hello World」の例では、たくさんの接続を同時に処理することができます。 各接続ごとにコールバックは発火され、何もすることがない場合、Node.js はスリープします。

とあり、サンプルコードは下記のように記されていました。

const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

その他利点としては下記のものが挙げられているようです。

  • 大量のデータ処理が可能
    • ノンブロッキングI/Oモデル
    • C10K問題(クライアント1万台問題)に対応
  • メモリの消費が少ない
    • イベントループ
  • 処理速度が早い
  • サーバサイドでjsが使える

​ ちょっと待って、C10K問題ってなに、、ということで↓。

クライアント1万台問題

https://ja.wikipedia.org/wiki/C10K%E5%95%8F%E9%A1%8C

C10K問題(英語: C10K problem)とは、Apache HTTP ServerなどのWebサーバソフトウェアとクライアントの通信において、クライアントが約1万台に達すると、Webサーバーのハードウェア性能に余裕があるにも関わらず、レスポンス性能が大きく下がる問題である。 ​

https://ja.wikipedia.org/wiki/Node.js

非同期処理のNode.jsではクライアント1万台問題は起きない。Node.jsでこの問題を解決した技術の中核は、シングルスレッドにおける非同期処理を容易に実装可能にしたイベント駆動型プログラミング環境である。 ​

node.js 上の有名なフレームワーク

この辺もNode.jsのフレームワークと言えるはず

requireってなんなん

https://codezine.jp/article/detail/10967

一応、新しいモジュールシステムが出てきた経緯から説明すると、もともとNode.jsにはCommonJSという昔ながらのモジュールの仕組みがあったんですね。これは、requireを使って特定のファイルやフォルダを取ってくるというものです。 これまではずっとそれを使ってモジュールの仕組みを実現していたんですが、ES2015からES Modulesという仕様が新しく定義されて、言語側の機能でモジュールのimport/exportができるようになりました。 ​

普通に使っているimportやexportの位置付けだったようです。 ​ ​

npm

  • Node.jsのパッケージマネージャ
  • Node Package Manager(npm)

yarnとは

ここでやっとyarnについてです。

https://qiita.com/lelouch99v/items/c97ff951ca31298f3f24

  • 2016年にFaceBookが公開したJavaScriptのパッケージマネージャです。
  • npmとの互換性があり、package.jsonが使えます。

yarnのメリット

https://qiita.com/lelouch99v/items/c97ff951ca31298f3f24#yarn%E3%81%AE%E3%83%A1%E3%83%AA%E3%83%83%E3%83%88

  • npmよりインストールが速い
    • 約半分になる場合もあるそう
  • npmより厳密にモジュールのバージョンを固定できる
    • yarn.lockファイルで、各パッケージのインストールバージョンを固定できる。
  • npmと一緒に使える
    • npmと同じのpackage.jsonが使えるため、同一プロジェクトでnpm or yarnで固定しなくて良い。 ​

yarnのインストール

$ sudo npm install -g yarn

まさかのnpmでinstallできます。 もちろんhomebrewなどでも可能です。 ​

npmとyarnのコマンド対応

  • npm init -> yarn init
  • npm install -> yarn
  • npm install [パッケージ名] --save -> yarn add [パッケージ名]
  • npm install [パッケージ名] --save-dev -> yarn add [パッケージ名] --dev
  • npm uninstall [パッケージ名] -> yarn remove [パッケージ名]
  • npm install -g [パッケージ名] -> yarn global add [パッケージ名] ​

https://engineering.fb.com/web/yarn-a-new-package-manager-for-javascript/

npmはインストールパッケージの一貫性、パフォーマンス、セキュリティに問題があり、この問題を解決することを目的として開発されたそうです。 Yarn開発者が直面したnpmの問題解決を目的としています。 ​

https://www.webprofessional.jp/yarn-vs-npm/

npmはパッケージごとにインストールが完了するまで待つのに対し、yarnは並行して実行するためパフォーマンスが向上しています。

まとめ

yarnはnpmの苦手なところを克服したパッケージマネージャでした。こういった背景を知ったことで、これからはnpmよりもyarnの方を積極的に使っていこうと思いました。

pyenv+matplotlibで発生するエラーを回避する

タイトルの通り、pyenvで取得したpythonでmatplotlibを使おうとしたときの話です。

  • macOS Mojave 10.14.6
  • Homebrew 2.2.11
  • pyenv 1.2.15
  • python 3.7.5
import matplotlib.pyplot as plt
...
plt.show()

このようなグラフ表示を伴うコードを書くと以下のようなエラーが発生します。

...(省略)
ImportError: Python is not installed as a framework. The Mac OS X backend will not be able to function correctly if Python is not installed as a framework. See the Python documentation for more information on installing Python as a framework on Mac OS X. Please either reinstall Python as a framework, or try one of the other backends. If you are using (Ana)Conda please install python.app and replace the use of 'python' with 'pythonw'. See 'Working with Matplotlib on OSX' in the Matplotlib FAQ for more information.

このエラーで検索をかけると大体が、~/.matplotlib/matplotlibrc

"backend" : "TkAgg"

を追加してくださいという記述が見つかります。これはグラフの表示にTkinterを使うという指定をしており、これで上記のエラーが発生しなくなるのはpython3はインストール時にtkinterも(基本は)同梱しているからです。しかし、pyenvを使用しているとうまくいかない場合があります。実際にわたしの環境では以下のエラーが発生しました。

...(省略)
import _tkinter # If this fails your Python may not be configured for Tk
ModuleNotFoundError: No module named ‘_tkinter’

どうやらtkinterがないと言っているようなのです。 pyenvでpythonをインストールした時にtkinterがうまく入らなかったということになります。実際にそう言った症例もネットで見つかりました。 Python not configured for Tk - Stack Overflow ここではpyenvでpythonをインストールする時に必要なものが書かれていました。zlib、tcl-tkが必要だそうです。

brew install zlib
brew install tcl-tk

以上でインストールしましょう。 上記のページでは、この後にpyenvをインストールするよう書いてありましたが、わたしの場合はそのままでも大丈夫でした。 この操作が終わった後にpyenvでのpythonのインストールするとtkinterが入っているので、あとは先ほどのmatplotlibrcの設定があればmatplotlibの描画にtkinterを使えるようになります。

ちなみにこれを入れただけでもtkinterがうまく入らないというケースもあるようで、その場合は.bashrcにパスを記入する必要があるみたいです。 pyenv install doesn't work with homebrew installed tcl-tk · Issue #1375 · pyenv/pyenv · GitHub

おまけ

わたしはこれからさらに仮想環境を作ってやっていたのですが、その場合はホームディレクトリは汚したくないので、仮想環境の方にmatplotlibrcの設定を書きました。以下を仮想環境のpythonで実行すれば、matplotlibrcのファイルの場所がわかります。

import matplotlib
matplotlib.matplotlib_fname()

まとめ

matplotlibの仕様とpyenvの仕様?が絡んでいる問題だったので、解決まで時間がかかりました。。pyenvのこと信頼しきっていたけど、こんな問題があったとはと驚かされた事件でした。

pythonでデコレーターを作るときに気をつけたいこと

sphinxでドキュメントを作成しようと思ったのですが、デコレーターをつけた関数の説明が軒並みおかしいので調べたところ、functools.wrapsというものの存在を知ったので記事にします。

デコレーターの書き方

pythonではデコレーターは以下のように作ります。

def my_decorator(func):
    """
    デコレート対象の関数の前にstart、後にendを標準出力する関数を返します。

    :param func: デコレート対象の関数
    :return: デコレートされた関数
    """
    def wrapper(*args, **kwargs):
        """
        デコレート対象の関数の前にstart、後にendを標準出力します。

        :param args:
        :param kwargs:
        :return:
        """
        print("start")
        func(*args, **kwargs)
        print("end")
    return wrapper


@my_decorator
def my_method(val1, val2):
    """
    val1, val2を標準出力します。

    :param val1:
    :param val2:
    :return:
    """
    print(val1, val2)


if __name__ == '__main__':
    my_method(1, 2)

これを実行すると以下が出力されます。

start
1 2
end

このようにmy_method前後で処理をするという目的ではこれだけも良いのですが、logやdocを使うときに問題があるのです。。

問題点

例えば、my_methodのdocstringを見てみましょう。print(my_method.__doc__)で表示させてみると以下が出てきました。

    デコレート対象の関数の前にstart、後にendを標準出力します。

    :param args:
    :param kwargs:
    :return:

my_decorator内のwrapperに書いたはずの記述が出てきてしまいました。 これはmy_methodをmy_decoratorでデコレートしたため、pythonからはwrapperという関数を実行しているようにしか見えないという状況ということかと考えられます。

もちろんpythonではこれを回避するための仕組みが用意されています。

改善策: functools.wraps

https://docs.python.org/ja/3/library/functools.html#functools.wraps

functools.wrapsが以上のことを回避するpythonで用意されている関数になります。使い方はとっても簡単です。 先ほどの例に追加してみます。

from functools import wraps


def my_decorator(func):
    """
    デコレート対象のメソッドの前にstart、後にendを標準出力するメソッドを返します。

    :param func: デコレート対象のメソッド
    :return: デコレートされたメソッド
    """

    @wraps(func) # 追加部分
    def wrapper(*args, **kwargs):
        """
        デコレート対象のメソッドの前にstart、後にendを標準出力します。

        :param args:
        :param kwargs:
        :return:
        """
        print("start")
        func(*args, **kwargs)
        print("end")

    return wrapper


@my_decorator
def my_method(val1, val2):
    """
    val1, val2を標準出力します。

    :param val1:
    :param val2:
    :return:
    """
    print(val1, val2)


if __name__ == '__main__':
    my_method(1, 2)

print(my_method.__doc__)で表示させてみると以下が出てきました。

    val1, val2を標準出力します。

    :param val1:
    :param val2:
    :return:

これは確かにmy_methodに書いたdocstringです。 ということで、pythonでデコレータを使うのは良いけどfunctools.wrapsも忘れずに!というお話でした。

shellでtry-catchをする

例えば以下のようなコードで、移動先のディレクトリがなかったらhelloはechoされないとわたしは思っていたのですが、実際のところはhelloまで出でしまいます。

#!/bin/sh

cd hoge
echo hello

↓出力

sample.sh: line 3: cd: hoge: No such file or directory
hello

そもそもshellはエラーが出たことを認識できているのでしょうか?

コマンドの成否を確認する

終了ステータス | UNIX & Linux コマンド・シェルスクリプト リファレンス

コマンド終了時には「終了ステータス (exit-status)」と呼ばれるコマンドの成否を表す数値が特殊変数 $? に自動で設定される。

終了ステータスとして設定される値は一般的には

  • 成功:0
  • 失敗:1 (コマンドやエラーの種類によっては 0 以外)

が設定されるそうです。

ということなのでエラーを認識できているのか確認は以下で行います。

#!/bin/sh

cd hoge
echo $?
echo hello
echo $?

↓出力

sample.sh: line 3: cd: hoge: No such file or directory
1
hello
0

どうやらエラーであることは認識できているようです。 ならばtry-catchしたいということでshellでtry-catchする方法を調べてみました。

shellでtry-catch

shell try-catchで検索したらこちらのサイトが見つかりました。

シェルスクリプトでtry-catch-finally - Qiita

こちらのサイトによるとtry-catchするなら以下のように書けば良いことがわかりました。

#!/bin/sh

set -eu
function catch {
  echo Catch
}
function finally {
  echo Finally
}
trap catch ERR
trap finally EXIT

cd hoge
echo hello

これを出力すると以下になりました。

sample.sh: line 13: cd: hoge: No such file or directory
Catch
Finally

先ほどまで出力されていたhelloの文字が出力されなくなり、Catch、Finallyの文字が出力されたことから、cd hogeでエラーが発生したタイミングで以降の処理がスキップされ、trapで設定したcatch、finallyを通過して終了したことがわかります。

同様に成功するパターンも確認しておきましょう。

#!/bin/sh

set -eu
function catch {
  echo Catch
}
function finally {
  echo Finally
}
trap catch ERR
trap finally EXIT

cd .
echo hello

この出力は以下のようになりました。

hello
Finally

以上より、trapおよびset -euによってどういうわけかtry-catchができるようになりました。

set -euとは

shellのコマンドのsetとそのオプションであるeuからなっているように見えるのでshellのsetについて調べてみました。見つかったのがこちらです。

【 set 】コマンド――シェルの設定を確認、変更する:Linux基本コマンドTips(205) - @IT

まずはsetというコマンドはshellの設定の確認、変更を行うコマンドとなっています。 そして、euは以下のように説明されています。

option option 名 説明
e errexit パイプやサブシェルで実行したコマンドが1つでもエラーになったら直ちにシェルを終了する
u nounset パラメーター展開中に、設定していない変数があったらエラーとする(特殊パラメーターである「@」と「*」は除く)

今回エラー発生タイミングで以降の処理をスキップをするようになったのは、set -eのおかげであるということですね。

ちなみにset -uの恩恵を受けるようなコードは以下になります。

#!/bin/sh

set -u

$hoge

echo hello

↓出力

sample.sh: line 5: hoge: unbound variable

hogeという設定していない変数を利用しようとした結果エラーになりました。この設定によるエラーはeのオプションなしでも以降の処理はスキップして終了するのですね。念の為オプションなしの方も確認しておきましょう。

#!/bin/sh

$hoge

echo hello

↓出力

hello

エラーが吐かれることなく最後まで実行していました。

trapとは

trap - シグナルをトラップする

trap [ action condition ... ]という構文からなっています。

trap ユーティリティーは、 condition が発生したときに qsh によって実行される action を設定します。 qsh は、trap を実行しているときに action を展開し、condition が発生したときに再度展開します。 また、trapにはl,pというオプションがあり、それぞれ以下のようにことができます。

option 説明
l すべてのシグナル名とその対応する番号のリストを表示します。
p 各トラップを再入力可能な形式で表示します。

conditionにはシグナルの名または番号を指定します。有効なシグナルのリストはtrap -lで参照することができるようです。 また、デフォルトで存在するのは以下です。

condition 説明
ERR エラー時に(コマンドの返り値が0以外)の時に実行
EXIT (0) 終了時に実行
DEBUG 各種コマンドの後で実行

まとめ

shellでも他のプログラミング言語でできることはできるんだなあと思いました。

デデンネの総合順位をスクレイピングして求めた話

2月の初旬に好きなポケモンgoogleで投票できましたが、皆さんはデデンネに投票してくださいましたでしょうか?

さて、2020/02/27はポケモンの日。前述した投票の結果も、この日の23時に公開されました。公開された情報は、総合TOP30および各地方のTOP30のみです。残念ながらデデンネは総合順位30位内には入ることができず、総合順位はわかりませんでした。しかし、カロス地方にて7位に輝き、その投票数は21,691でした。他の地方のTOP30の子たちの投票数を見るに、デデンネと同等数の投票数を獲得しているポケモンたちはどうやら15位以内に収まっているようでした。デデンネの総合順位が出せる...!ということで出してみました。

f:id:xmadoka:20200307022822p:plain
デデンネ 2020年ポケモン投票結果

ついでに学べたこともあったので、合わせてこの記事に載せようと思います。

BeautifulSoupでは取得できない?

requests + BeautifulSoupを使ってスクレイピングをした経験があるので、安易に今回もこちらを使ってスクレイピングをしようとしました。

from urllib.request import urlopen
from bs4 import BeautifulSoup

soup=BeautifulSoup(urlopen(url))

上記のコードで拾ってこれで図鑑番号、ポケモン名と投票数が入っているものが取れると思ったのですが、実際に図鑑番号、ポケモン名と投票数が入るはずのところには違うものが入っていました。

<div class="ranking__text">
 <p class="ranking__number">No.<span>{{data.no}}</span></p>
 <h3 class="ranking__name">{{data.jp}}</h3>
 <p class="ranking__vote"><span>{{data.vote}}</span></p>
</div>

すごい何処かで見たことあるやつだと思いました。vueと同じようなやつだなと思いました。(jsのテンプレートエンジンですね。)

jsのテンプレートエンジンを使っている画面は取得できない...?

ひとまず確認のため、最近触っているvueでサンプルページを作ってBeautifulSoupに読ませてみました。

f:id:xmadoka:20200307003719p:plain
vue-cliを使ってできたページ

読み込んでみると以下が帰ってきました。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8"/>
    <meta content="IE=edge" http-equiv="X-UA-Compatible"/>
    <meta content="width=device-width,initial-scale=1.0" name="viewport"/>
    <link href="/favicon.ico" rel="icon"/>
    <title>sample</title>
    <link as="script" href="/js/app.js" rel="preload"/>
    <link as="script" href="/js/chunk-vendors.js" rel="preload"/>
</head>
<body>
<noscript>
    <strong>We're sorry but sample doesn't work properly without JavaScript enabled. Please enable it to
        continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
<script src="/js/chunk-vendors.js" type="text/javascript"></script>
<script src="/js/app.js" type="text/javascript"></script>
</body>
</html>

<div id="app"></div>がそのままなところから、確かに図と同じものは読み込めてなさそうなことがわかりました。ソースコード見るに、index.htmlを読んだだけのように見えます。

We're sorry but sample doesn't work properly without JavaScript enabled. Please enable it to continue. ここも重要ですね、表示されてはいないものの、JavaScriptがないと十分に動かないとの注意書きがされています。(index.htmlにある文章なので、スクレイピングしたから出たわけではないです。)

requests_html

先ほどの調査で、requests + BeautifulSoupだけではindex.htmlなるjsの実行前のものが取得されてしまい、jsで書き換えているようなページはきちんと取得できないということがわかりました。見てるページをきちんと取得するためには、index.htmlだけでなくjsも読み込み反映させたものを取得する必要があります。

pythonにはこのようなことをしてくれるライブラリがいくつかあり、Seleniumが特に有名ですが、今回色々用意しなくても楽に実装できそうなrequests_htmlを使うことにしました。

from requests_html import HTMLSession

session = HTMLSession()
r = session.get(url)

# ブラウザエンジンでHTMLを生成させる
r.html.render()

ちょっとBeautifulSoupと比べると手間ですが、これでjs反映後のhtmlが取得できるはずです。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="/favicon.ico">
    <title>sample</title>
    <link href="/js/app.js" rel="preload" as="script">
    <link href="/js/chunk-vendors.js" rel="preload" as="script">
    <style type="text/css">
        h3[data-v-469af010] {
            margin: 40px 0 0;
        }

        ul[data-v-469af010] {
            list-style-type: none;
            padding: 0;
        }

        li[data-v-469af010] {
            display: inline-block;
            margin: 0 10px;
        }

        a[data-v-469af010] {
            color: #42b983;
        }
    </style>
    <style type="text/css">
        #app {
            font-family: Avenir, Helvetica, Arial, sans-serif;
            -webkit-font-smoothing: antialiased;
            -moz-osx-font-smoothing: grayscale;
            text-align: center;
            color: #2c3e50;
            margin-top: 60px;
        }
    </style>
</head>
<body>
<noscript>
    <strong>We're sorry but sample doesn't work properly without JavaScript enabled. Please enable it to
        continue.</strong>
</noscript>
<div id="app"><img alt="Vue logo" src="/img/logo.82b9c7a5.png">
    <div data-v-469af010="" class="hello"><h1 data-v-469af010="">Welcome to Your Vue.js App</h1>
        <p data-v-469af010=""> For a guide and recipes on how to configure / customize this project,<br
                data-v-469af010=""> check out the <a data-v-469af010="" href="https://cli.vuejs.org" target="_blank"
                                                     rel="noopener">vue-cli documentation</a>. </p>
        <h3 data-v-469af010="">Installed CLI Plugins</h3>
        <ul data-v-469af010="">
            <li data-v-469af010=""><a data-v-469af010=""
                                      href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel"
                                      target="_blank" rel="noopener">babel</a></li>
            <li data-v-469af010=""><a data-v-469af010=""
                                      href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint"
                                      target="_blank" rel="noopener">eslint</a></li>
        </ul>
        <h3 data-v-469af010="">Essential Links</h3>
        <ul data-v-469af010="">
            <li data-v-469af010=""><a data-v-469af010="" href="https://vuejs.org" target="_blank" rel="noopener">Core
                Docs</a></li>
            <li data-v-469af010=""><a data-v-469af010="" href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a>
            </li>
            <li data-v-469af010=""><a data-v-469af010="" href="https://chat.vuejs.org" target="_blank" rel="noopener">Community
                Chat</a></li>
            <li data-v-469af010=""><a data-v-469af010="" href="https://twitter.com/vuejs" target="_blank"
                                      rel="noopener">Twitter</a></li>
            <li data-v-469af010=""><a data-v-469af010="" href="https://news.vuejs.org" target="_blank" rel="noopener">News</a>
            </li>
        </ul>
        <h3 data-v-469af010="">Ecosystem</h3>
        <ul data-v-469af010="">
            <li data-v-469af010=""><a data-v-469af010="" href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a>
            </li>
            <li data-v-469af010=""><a data-v-469af010="" href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a>
            </li>
            <li data-v-469af010=""><a data-v-469af010="" href="https://github.com/vuejs/vue-devtools#vue-devtools"
                                      target="_blank" rel="noopener">vue-devtools</a></li>
            <li data-v-469af010=""><a data-v-469af010="" href="https://vue-loader.vuejs.org" target="_blank"
                                      rel="noopener">vue-loader</a></li>
            <li data-v-469af010=""><a data-v-469af010="" href="https://github.com/vuejs/awesome-vue" target="_blank"
                                      rel="noopener">awesome-vue</a></li>
        </ul>
    </div>
</div>
<!-- built files will be auto injected -->
<script type="text/javascript" src="/js/chunk-vendors.js"></script>
<script type="text/javascript" src="/js/app.js"></script>

<div id="app"></div>の中身が埋まったことがわかります。

準備が整ったのでデデンネの総合順位を取得する

今回、順位を出すのにデータ取り扱いに強いpandasを使って計算を行いました。

import pandas as pd

from requests_html import HTMLSession

def get_region_rank_data(region):
    ls = list()
    url = 'https://pokemonday.pokemon.co.jp/jp/result/' + region + '/'

    # セッション開始
    session = HTMLSession()
    r = session.get(url)

    # ブラウザエンジンでHTMLを生成させる
    r.html.render()
    headers = r.html.find('.ranking__header')
    for header in headers:
        name = header.find('.ranking__name')[0].text
        vote = int(header.find('.ranking__vote > span')[0].text.replace(",", ""))
        number = int(header.find('.ranking__number > span')[0].text)
        ls.append([number, name, vote])
    return ls


if __name__ == '__main__':
    regions = [
        'kanto',
        'johto',
        'hoenn',
        'sinnoh',
        'unova',
        'kalos',
        'alola',
        'galar',
    ]
    data_list = list()
    for region in regions:
        data_list.extend(get_region_rank_data(region))

 # 集計
    array = pd.np.array(data_list)
    df = pd.DataFrame({
        "number": array[:, 0],
        "name": array[:, 1],
        "vote": array[:, 2]
    })
    df["vote"] = df["vote"].astype(int)
    df = df.sort_values("vote", ascending=False)
    df["rank"] = df["vote"].rank(0, ascending=False)
    df = df[["rank", "name", "number", "vote"]]
    df.to_csv('pokemon_vote_ranking.csv', header=True, index=False)

これによってpokemon_vote_ranking.csvに吐き出されたデータは以下のようになりました。

rank,name,number,vote
1.0,ゲッコウガ,658,140559
2.0,ルカリオ,448,102259
3.0,ミミッキュ,778,99077
4.0,リザードン,6,93968
5.0,ブラッキー,197,67062
6.0,ニンフィア,700,66029
7.0,ガブリアス,445,61877
8.0,レックウザ,384,60939
9.0,サーナイト,282,60596
10.0,ゲンガー,94,60214
11.0,ドラパルト,887,57973
12.0,バンギラス,248,56834
13.0,フシギダネ,1,56015
14.0,ストリンダー,849,55032
15.0,ルギア,249,53268
16.0,モクロー,722,52367
17.0,ギルガルド,681,51517
18.0,シャンデラ,609,50943
19.0,ピカチュウ,25,48060
20.0,イーブイ,133,47762
21.0,レントラー,405,46032
22.0,ジュナイパー,724,44011
23.0,ゾロアーク,571,43782
24.0,ルガルガン,745,42792
25.0,アーマーガア,823,41711
26.0,フライゴン,330,41420
27.0,サザンドラ,635,40054
28.0,ジュカイン,254,38724
29.0,バシャーモ,257,38307
30.0,ユキハミ,872,38034
31.0,ミズゴロウ,258,36920
32.0,カイリュー,149,36873
33.0,ミュウ,151,36266
34.0,メタグロス,376,35631
35.0,バクフーン,157,35184
36.0,オンバーン,715,34795
37.0,ハッサム,212,34691
38.0,ポッチャマ,393,34680
39.0,ミュウツー,150,34585
40.0,ゴウカザル,392,33267
41.0,デンリュウ,181,32009
42.0,ゼラオラ,807,31691
43.0,マホイップ,869,30612
44.0,ダークライ,491,30544
45.0,ヌメルゴン,706,30209
46.0,エーフィ,196,30052
47.0,ガオガエン,727,29925
48.0,ウインディ,59,29795
49.0,ジラーチ,385,29611
50.0,ヒノアラシ,155,28332
51.0,ミロカロス,350,28295
52.0,アブソル,359,27781
53.0,グソクムシャ,768,26975
54.0,エースバーン,815,26892
55.0,ラグラージ,260,26540
56.0,スイクン,245,26277
57.0,グレイシア,471,26161
58.0,ザシアン,888,26158
59.0,アシレーヌ,730,25953
60.0,ワンパチ,835,25695
61.0,ボーマンダ,373,24920
62.0,アルセウス,493,24502
63.5,ウルガモス,637,24389
63.5,ココドラ,304,24389
65.0,ラプラス,131,23411
66.0,オノノクス,612,22937
67.0,ワニノコ,158,22526
68.0,ファイアロー,663,22328
69.0,ジャローダ,497,22269
70.0,ミジュマル,501,21990
71.0,エンペルト,395,21773
72.0,デデンネ,702,21691
73.0,クロバット,169,21548
74.0,ゼクロム,644,21477
75.0,オオタチ,162,21447
76.0,ギラティナ,487,21366
77.0,ウールー,831,21266
78.0,ビクティニ,494,20957
79.0,リーフィア,470,20859
80.0,イベルタル,717,20852
81.0,インテレオン,818,20697
82.0,ドダイトス,389,20632
83.0,セレビィ,251,20492
84.0,ヌメラ,704,20299
85.0,ネギガナイト,865,20217
86.0,レシラム,643,20123
87.0,ヒバニー,813,20058
88.0,カビゴン,143,19768
89.0,ワルビアル,553,19628
90.0,キュウコン,38,19044
91.0,ガチゴラス,697,18778
92.0,ブリムオン,858,18581
93.0,チコリータ,152,18521
94.0,ゼニガメ,7,18476
95.0,ホウオウ,250,18278
96.0,オーダイル,160,18245
97.0,ボスゴドラ,306,18120
98.0,エルフーン,547,17855
99.0,ラティアス,380,17478
100.0,シェイミ,492,17465
101.0,ゼルネアス,716,17415
102.0,キテルグマ,760,17181
103.0,メッソン,816,17155
104.0,ツタージャ,495,17020
105.0,チルタリス,334,16814
106.0,カメックス,9,16795
107.0,ヘラクロス,214,16577
108.0,ソルガレオ,791,16274
109.0,メルタン,808,16077
110.0,タイレーツ,870,16009
111.0,タルップル,842,15989
112.5,ムゲンダイナ,890,15699
112.5,ネイティ,177,15699
114.0,ピチュー,172,15695
115.0,カイオーガ,382,15585
116.0,クチート,303,15523
117.0,ゾロア,570,14910
118.0,シャワーズ,134,14887
119.0,マッシブーン,794,14747
120.0,ヘルガー,229,14742
121.0,ルチャブル,701,14607
122.0,ペンドラー,545,14536
123.5,ナマコブシ,771,14358
123.5,ベトベトン,89,14358
125.0,ディアルガ,483,14292
126.0,トゲピー,175,14288
127.0,エルレイド,475,14144
128.0,ランクルス,579,14129
129.0,ムクホーク,398,14054
130.0,ヒトカゲ,4,14049
131.0,ニャビー,725,14005
132.0,モルペコ,877,13945
133.0,メロエッタ,648,13915
134.0,シルヴァディ,773,13897
135.0,メタモン,132,13843
136.0,ニャオニクス,678,13661
137.0,フォッコ,653,13508
138.0,サルノリ,810,13478
139.0,トゲキッス,468,13426
140.0,クワガノン,738,13375
141.0,ヌオー,195,13308
142.0,オーロンゲ,861,12923
143.0,エンニュート,758,12863
144.0,キノガッサ,286,12801
145.0,ジャラランガ,784,12790
146.0,グライオン,472,12676
147.0,ザマゼンタ,889,12641
148.0,アチャモ,255,12568
149.0,ラティオス,381,12487
150.0,ヤドン,79,12369
151.0,メルメタル,809,12356
152.0,マーシャドー,802,12107
153.0,グラードン,383,11982
154.0,ジガルデ,718,11943
155.0,マルヤクデ,851,11619
156.0,ニドキング,34,11586
157.0,ダイケンキ,503,11444
158.0,ウオノラゴン,882,11436
159.0,ミミロップ,428,11411
160.0,ドリュウズ,530,11376
161.0,ポリゴン,137,11311
162.0,ドレディア,549,11292
163.0,ロコン,37,11224
164.0,コダック,54,11212
165.0,ライチュウ,26,11196
166.0,カイロス,127,11162
167.0,ヒトモシ,607,11140
168.0,サンダース,135,11064
169.0,ハクリュー,148,11051
170.0,モスノウ,873,10988
171.0,イワンコ,744,10986
172.0,キリキザン,625,10975
173.0,ビッパ,399,10924
174.0,ディアンシー,719,10918
175.0,アマージョ,763,10900
176.0,マフォクシー,655,10889
177.0,ナエトル,387,10865
178.0,マニューラ,461,10854
179.0,デオキシス,386,10842
180.0,テールナー,654,10807
181.0,ジュプトル,253,10746
182.0,ジュペッタ,354,10579
183.0,フシギバナ,3,10454
184.0,フリーザー,144,10450
185.0,ユキメノコ,478,10408
186.0,エンテイ,244,10404
187.0,ニャスパー,677,10402
188.0,コオリッポ,875,10392
189.0,キュレム,646,10338
190.0,フーパ,720,10327
191.0,アマルルガ,699,10321
192.0,ヤンチャム,674,10187
193.0,マグマラシ,156,10032
194.0,コイル,81,9955
195.0,ポリゴン2,233,9902
196.0,チラチーノ,573,9892
197.0,ルナアーラ,792,9887
198.0,ドラミドロ,691,9882
199.0,エモンガ,587,9798
200.0,パチリス,417,9694
201.0,ロトム,479,9417
202.0,キモリ,252,9339
203.0,ウパー,194,9323
204.0,バケッチャ,710,9162
205.0,ロズレイド,407,9092
206.0,オーロット,709,9019
207.0,ヤミラミ,302,9012
208.0,ケルディオ,647,8915
209.0,ラランテス,754,8889
210.0,ゴロンダ,675,8859
211.0,ムウマージ,429,8833
212.0,バチュル,595,8796
213.0,チラーミィ,572,8739
214.0,ルンパッパ,272,8708
215.0,タチフサグマ,862,8705
216.0,ゴリランダー,812,8625
217.0,トゲデマル,777,8531
218.0,ウッウ,845,8436
219.0,コリンク,403,8373
220.0,ボクレー,708,8231
221.0,ドヒドイデ,748,8221
222.0,ムウマ,200,8156
223.0,ツボツボ,213,8129
224.0,カプ・コケコ,785,8090
225.0,ゴルーグ,623,8035
226.0,ゲノセクト,649,7905
227.0,バンバドロ,750,7787
228.0,ソーナンス,202,7745
229.0,ネクロズマ,800,7739
230.0,アシマリ,728,7542
231.0,コスモッグ,789,7515
232.0,キングドラ,230,7418
233.0,ゴンベ,446,7289
234.0,アブリボン,743,7126
235.0,ポリゴンZ,474,7040
236.0,ヌケニン,292,6703
237.0,カラマネロ,687,6503
238.0,グラエナ,262,6459
239.0,エレザード,695,6386
240.0,ケロマツ,656,6293

デデンネがいた!!!

72.0,デデンネ,702,21691

デデンネの図鑑番号702から0を抜いた順位になっていました。

まとめ

デデンネは総合順位72位!!!

Go 日時フォーマットについて

Goにて日時をフォーマットしようと思い、よくみる%y%M%dを書いたところ、上手くいかなかったため調べてみました。

Goでは20060102と書くと同様のフォーマットができるみたいです。

Goでは日時フォーマットに使う日時が決まっている

なんとGoでは日時フォーマットに使う日時が決まっているのです。

2006年1月2日午後3時4分5秒がその日時です。

実際に表記してほしい状態で指定できるので、わかりやすくていいですよね。(プログラマからしたら、他の言語と違うので逆にわかりにくい気はする...)

Junuaryとかも使える

上記に書いた数字以外もちろん使えます。例えばJunuaryやJunて書いても大丈夫です。

package main

import "fmt"
import "time"

func main() {
    n := time.Now()
    fmt.Println(n)                           // 2009-11-10 23:00:00 +0000 UTC m=+0.000000001
    fmt.Println(n.Format("20060102"))        // 20091110
    fmt.Println(n.Format("2006 Junuary 02")) // 2009 Junuary 10
    fmt.Println(n.Format("2006 Jun 02"))     // 2009 Jun 10
    fmt.Println(n.Format("2006年1月2日"))     // 2009年11月10日
    fmt.Println(n.Format("06/01/02"))        // 09/11/10
}

 

ちなみに

なんでこの日なんだろう、Goと何か関係がある日なの、、?と考えを巡らしてしまったのですが、googleさんに聞いたところ、Goができた日みたいな素敵な意味ではなく、単純に(1,2,3,4,5,6)の並びだからという話が有力そうです。(覚えやすさ大事...!)