pipenvをやめてvenvへ

pipenvで作った環境にはいってpythonスクリプトを実行するシェルスクリプトを書きました
シェルスクリプト単体なら正常に動作するのですが、cron越しだと以下の通りエラーがでました

cd /User/takuto/pytho_1/weather_pop
piping run python weather_pop.py

atc.hateblo.jp


Error_1

Permission denied

これは権限がないとのことなので、$ chmod +xで当該.shに権限を付与
続いてのエラー

Error_2

make: pipenv: Command not found

これはPATHが/usr/bin:/binしか設定されていなかったので、vimPATH=/usr/local/binを追記
続いてのエラー

Error_3

Warning: the which -a system utility is required for Pipenv to find Python installations properly. Please install it.
Error: the command python3 could not be found within PATH or Pipfile's [scripts].

これが全然わからなくて挫折しました
「なんでだ〜〜〜」と頭を抱えながら環境について考え直していたら、私は複数バージョンを使わないのでパッケージ管理さえできる最低限の環境でよいのではと思い、pipenvを削除してvenvに切り替えることにしました
エラーにはpipenvのpipfile'sがなにか影響してるっぽいので、venvに変えちゃえば解決するのではと
やっぱ初心者がいろいろなツールに手を出すのはもう控えよう...

pipenvのアンインストール

まずホームディレクトリ下のpytho_1というディレクトリで仮想環境を構築していたので、下記で仮想環境を削除

# virtualenvを削除します  
$ pipenv --rm
Removing virtualenv (/Users/takuto/.local/share/virtualenvs/pytho_1-kjK1izHD)...

# インストールしてたパッケージを全削除します 
$ pipenv uninstall —all-dev
Un-installing [dev-packages]...
Locking [dev-packages] dependencies...
Locking [packages] dependencies...
# HomebrewでPipenvをアンインストールしました
$ brew uninstall pipenv
Uninstalling /usr/local/Cellar/pipenv/2018.7.1... (2,111 files, 25.8MB)

venvを使う

$ cd /Users/takuto/python1/weathwe_pop

# 仮想環境を作成
$ python3 -m venv venv  #後者の`venv`がディレクトリ名

# 仮想環境に入る
$ source /venv/bin/activate.fish #fishを使ってるため

# pythonのバージョン確認とスクリプトの実行  
$python —version
python3.7.0
$ python weather_pop.py  #実行された

weather_pop.pyをcronで動かす

まずシェルスクリプトを書きます

PYTHON=/Usres/takuto/python1/weather_pop
source PYTHON/venv/bin/activate
python3 $PYTHON/weather_pop.py

crontab -eで設定

30 7 * * * /Usres/takuto/scr/weather_pop.sh

実行されました
pipenvだと動かなかった理由、なんか.shの書き方が悪かったような気がしてきた
わがんねぇな〜オィ〜~~~

pyenvからpipenvにしてみた

python環境構築の見直し

pythonのライブラリ管理と仮想環境の構築のためにpyenv``pyenv-virtualenv``anacondaを用いているのですが、まだまだ初心者の私にとっては複雑で「なぜこのツールをインストールしているんだろう」と役割が分かっていない部分もあります
又、anacondaを導入することで様々なライブラリやツールが利用可能になりますが、現状宝の持ち腐れ状態なので必要なモノだけ導入し、今後入れたいと思ったモノについても1つずつ導入していくスタイルが私に合っているかなと思いました

そこでPython環境の再構築を視野に入れて調べていたところ、ライブラリ管理仮想環境構築の二役を一手に担っているパッケージ管理ツールPipenvを見つけました

ドキュメントを見ていると導入・管理・運用の全てがシンプルに行えそうだったので、今の環境をリセットしてPipenvへチャレンジしたいと思います。今回はそのために行った手順を掲載します

Pipenvとは

https://pipenv-ja.readthedocs.io/ja/translate-ja/

Pipenvは、手動でパッケージのインストールおよびアンインストールを行うのと同じように Pipfile に対してパッケージの追加および削除を行うのに加え、自動でプロジェクト用の仮想環境を作成し管理します。 またPipenvは、いかなるときも重要な Pipfile.lock を生成し、これを利用しビルドが常に同じ結果になるようにします。

Pipenvは主にアプリケーションのユーザーと開発者に、簡単に作業環境を作れる方法を提供するためのツールです。 ライブラリとアプリケーションの違いや、依存関係を定義するための setup.py と Pipfile の使い方の違いについては、 ? Pipfile vs setup.py を参照してください。

つまりライブラリ管理と仮想環境構築ができるということですかね

現在の環境

  • PC : macOS High Sierra MacBook Pro (13-inch, 2017, Two Thunderbolt 3 ports)
  • python : pyenv, pyenv-virtualenv, anaconda python自体はanaconda内に入っているものを使ってます

現在のpython環境について詳しく

$ pyenv versions
  system
* anaconda3-5.1.0 (set by /Users/ユーザ名/.pyenv/version)
  anaconda3-5.1.0/envs/ml
  anaconda3-5.1.0/envs/test

1. anaconda(python)をバージョン毎に管理

  • pyenvでanaconda(python)をバージョン毎にインストール
  • pyenv global anacondahoge(version)でバージョンを切り替え

2. anaconda(python)の各バージョンから派生し、名前で管理

  • conda create -n hoge(名前) anacondahoge(ver)でanaconda(python)を1.から派生して環境を構築
    上記コマンドによってanacondaに入ってるpythonモジュール等をフルで備えたpython環境が生成できます
    又、ケツをanacondahoge(ver)でなくpythonとすることで純粋なpython単体の環境も生成できます
  • pyenv global anacondahoge(ver)/hoge(名前)で派生した環境に切りかえる

    condaはanacondaに入っているパッケージ管理&仮想環境構築その他諸々を可能にするツールです
    2.において名前付き仮想環境をcondaで作成しているわけですが、切り替えはpyenvで行ってます
    ここで疑問としてpyenv-virtualenvが、何に対してどのように作用しているのか分かってません
    色々調べたのですが結局分からなかったので、いっそ消しちゃおうかと思ったのが今回の環境移行の動機です

    文字だとわけわかめなので図化してみる
    f:id:Atc:20180703145427j:plain

作業手順

  1. 現在のPython環境を消去(macデフォで入ってたPython2は消さない)
  2. Python3をインストール
  3. Pipenvをインストール
  4. Pipenvで仮想環境を構築&ライブラリ管理

01.現在のPython環境を消去

の前に、TimeMachineでバックアップを取っておきました
では、早速削除していきます
どういう順番で削除していくべきか...
まぁ導入したときの逆順を追っていけばいいかな...? ということで

01.の手順

01-1. condaでcreateした名前付きの各環境とpyenvで入れたanacondaを削除
01-2. pyenv-virtualenvを削除
01-3. pyenvを削除
で、イクゾー
デッデッデデデデッ


01-1. condaでcreateした名前付きの各環境を削除 anacondaの操作_参考
シェルで$ conda remove -n 名前 --allとすればよい
まずはtestから消してみる

$ conda remove -n test --all
Remove all packages in environment /Users/ユーザ名/.pyenv/versions/anaconda3-5.1.0/envs/test:

## Package Plan ##
  environment location: /Users/ユーザ名/.pyenv/versions/anaconda3-5.1.0/envs/test

The following packages will be REMOVED:

    ca-certificates: 2018.03.07-0           
    certifi:         2018.4.16-py36_0       
    libcxx:          4.0.1-h579ed51_0       
    libcxxabi:       4.0.1-hebd6815_0       
    libedit:         3.1.20170329-hb402a30_2
    libffi:          3.2.1-h475c297_4       
    ncurses:         6.1-h0a44026_0         
    openssl:         1.0.2o-h26aff7b_0      
    pip:             10.0.1-py36_0          
    python:          3.6.5-hc167b69_1       
    readline:        7.0-hc1231fa_4         
    setuptools:      39.1.0-py36_0          
    sqlite:          3.23.1-hf1716c9_0      
    tk:              8.6.7-h35a86e2_3       
    wheel:           0.31.0-py36_0          
    xz:              5.2.3-h727817e_4       
    zlib:            1.2.11-hf3cbc9b_2      

Proceed ([y]/n)? y

$ conda info -e
# conda environments:
#
base                  *  /Users/ユーザ名/.pyenv/versions/anaconda3-5.1.0
ml                       /Users/ユーザ名/.pyenv/versions/anaconda3-5.1.0/envs/ml

削除する環境の場所とパッケージが出てきた後、yを入力して消すことができた
この調子で仮想環境を全て消す
(testは純粋にpythonのみの環境なのでまだパッケージは少なかったが、anacondaを入れた環境mlはパッケージ数がめちゃくちゃ多かった) mlも削除した後、baseと書かれている環境も同様にconda removeしたら以下の通りエラー

$ conda remove -n base  --all
CondaEnvironmentError: cannot remove current environment. deactivate and run conda remove again

非アクティブ化してcondaを再度実行する...とでてきたのですが、調べてみてもよくわからなかったのと、当該環境はpyenvでインストールしたanacondaだったので、pyenvでアンインストールすればよいかと判断

$ pyenv uninstall anaconda3-5.1.0
pyenv: remove /Users/ユーザ名/.pyenv/versions/anaconda3-5.1.0?   y

$ pyenv versions
* system (set by /Users/ユーザ名/.pyenv/version)

これでmacに元々入っていたpythonだけになりました(たぶん)


01-2. pyenv-virtualenvを削除 参考

$ brew uninstall pyenv-virtualenv
Uninstalling /usr/local/Cellar/pyenv-virtualenv/1.1.1... (20 files, 60.6KB)


01-3. pyenvを削除 参考

$ which pyenv
/usr/local/bin/pyenv

$ brew uninstall pyenv
Uninstalling /usr/local/Cellar/pyenv/1.2.4... (603 files, 2.4MB)

$ which pyenv
表示なし


macに元々入っていたpythonしかないことを確認

$ which python
/usr/bin/python

OK

02. Python3をインストール The Hitchhiker’s Guide to Python

& brew install python
See: https://docs.brew.sh/Homebrew-and-Python
==> Summary
🍺  /usr/local/Cellar/python/3.7.0: 4,788 files, 102.2MB

$ which python3
/usr/local/bin/python3

$ python3 --version
Python 3.7.0

OK
Python3.7がリリースしたので最新が入りました

03. Pipenvのインストール Pipenv公式ドキュメント, 参考

$ brew install pipenv

==> Summary
🍺  /usr/local/Cellar/pipenv/2018.7.1: 1,358 files, 18.8MB

04. Pipenvで仮想環境を構築&ライブラリ管理 参考

私はカレントディレクトリの後にpytho_1という名前のディレクトリがあり、そこにpythonファイルを置いているので、ひとまずそこに移動します
その後pipenvによって仮想環境を作ります

$ cd pytho-1
$ pipenv install #仮想環境を作成
Creating a virtualenv for this project...
Pipfile: /Users/ユーザ/pytho_1/Pipfile
Using /usr/local/Cellar/pipenv/2018.7.1/libexec/bin/python3.7 (3.7.0) to create virtualenv...
⠋Already using interpreter /usr/local/Cellar/pipenv/2018.7.1/libexec/bin/python3.7
Using real prefix '/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7'
New python executable in /Users/ユーザ/.local/share/virtualenvs/pytho_1-kjK1izHD/bin/python3.7
Also creating executable in /Users/ユーザ/.local/share/virtualenvs/pytho_1-kjK1izHD/bin/python
Installing setuptools, pip, wheel...done.
Setting project for pytho_1-kjK1izHD to /Users/ユーザ/pytho_1

Virtualenv location: /Users/ユーザ/.local/share/virtualenvs/pytho_1-kjK1izHD
Creating a Pipfile for this project...
Pipfile.lock not found, creating...
Locking [dev-packages] dependencies...
Locking [packages] dependencies...
Updated Pipfile.lock (a65489)!
Installing dependencies from Pipfile.lock (a65489)...
  🐍   ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 0/0 — 00:00:00
To activate this project's virtualenv, run pipenv shell.
Alternatively, run a command inside the virtualenv with pipenv run.

すると当該ディレクトリに以下のファイルが生成される

Pipfile  Pipfile.lock

中を見てみる
* Pipfile

$ cat pipfile
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]

[dev-packages]

[requires]
python_version = "3.7"

バージョンが書いてあった。よかったpython2じゃなかった
* Pipfile.lock

$ cat pipfile.lock
{
    "_meta": {
        "hash": {
            "sha256": "7e7ef69da7248742e869378f8421880cf8f0017f96d94d086813baa518a65489"
        },
        "pipfile-spec": 6,
        "requires": {
            "python_version": "3.7"
        },
        "sources": [
            {
                "name": "pypi",
                "url": "https://pypi.org/simple",
                "verify_ssl": true
            }
        ]
    },
    "default": {},
    "develop": {}
}

JSON形式になっているpipfile.lockにはパッケージが書き込まれる模様

  • 作成した環境にパッケージをインストールしてみる
$ pipenv install requests

仮想環境でpyを実行する方法

  1. 作成した仮想環境をactivateする
  2. pipenv run pythonで実行

  3. 作成した仮想環境をactivateする

$ python --version
Python 2.7.10
$ pipenv shell #仮想環境に入る
Launching subshell in virtual environment…
bash-3.2$  . /Users/ユーザ/.local/share/virtualenvs/pytho_1-kjK1izHD/bin/activate
(pytho_1-kjK1izHD) bash-3.2$ python --version
Python 3.7.0

$ cd weather_pop
$ python3 weather_pop.py

(pytho_1-kjK1izHD) bash-3.2$ exit #シェルから抜ける


2. pipenv run pythonで実行

$ pipenv run python3 #requestsがインストールされている対話モードが実行
$ pipenv run python3 weather_pop.py #weather_pop.py(requestsを用いる)が実行された

pipenvを導入して、モジュールをインストール、環境を切り替えるまではできるようになりましたが正直わからんことだらけなので勉強しなきゃ...

毎朝自動で降水確率を教えてくれるプログラムを作りました

こんにちは
この度初めてイチからプログラミングをしてみました

毎朝7:30、その日の6時間ごとの降水確率をmacOSの通知機能で教えてくれるプログラムです
うまくいってテンションがかなりHighなのでこの感動を忘れぬようブログに掲載したいと思います

コード

import requests, xml.etree.ElementTree as ET, os

# XMLをwebから取得
def get_xml():
    r = requests.get('https://www.drk7.jp/weather/xml/08.xml')
    r.encoding = r.apparent_encoding
    return r.text

a = get_xml()
# XMLから降水確率を取得
def get_rainfallchance():
    root = ET.fromstring(a) #パース
    area = root.findall(".//area[@id]")  #北部と南部
    south = area[1] #南部エリアのノード
    info = south.findall('.//info[@date]') #南部の7日分
    today = info[0] #南部の今日の分のノード
    period = today.findall('.//period[@hour]') #南部の今日の降水確率(6時間毎)
    return period

#取得した降水確率をテキスト化してリストにappend
rain = get_rainfallchance()
ch = []
for i in rain:
    ch.append(i.text)
    
#通知センターに表示
os.system("osascript -e 'display notification\"{0[0]}%     {0[1]}%     {0[2]}%     {0[3]}%\" with title \"今日の降水確率\"subtitle\"00-06  07~12  13-18  19-24\"'".format(ch))

これを実行すると以下のように通知が出てきます

こんなかんじ

使わせて頂いた気象情報のデータ

今回は、以下のブログ様にて気象庁の気象情報をXML化したものを配布していたので、ありがたく使わせていただきました

気象庁の天気予報情報を XML で配信 - drk7jp

当該XMLは以下のような情報が入っています
(私は茨城県の南部在住のため茨城県XMLを使いました)
f:id:Atc:20180626194353j:plain
茨城の中に南部北部
南部の中に7日分の気象情報
各日毎に、風速気温等が入っています
当該XMLは毎朝6時頃に更新されるらしいので、私が取得すべき情報は当日分の6時間ごとの降水確率となります
つまりコレ↓

<Response [200]>
<?xml version="1.0" encoding="UTF-8"?>
<weatherforecast>
<title>weather forecast xml</title>
<link>http://www.drk7.jp/weather/xml/08.xml</link>

中略

</area>
    <area id="南部">
    <geo>
        <long>140.1714</long>
        <lat>36.1234</lat>
    </geo>
    
    <info date="2018/06/26"> ←操作日
        <weather>くもり</weather>
        <img>http://www.drk7.jp/MT/images/MTWeather/200.gif</img>
        <weather_detail>南西の風 のち 南の風 海上 では 南の風 やや強く くもり 所により 霧</weather_detail>
        <wave>波 1.5メートル のち 2メートル</wave>
        <temperature unit="摂氏">
        <range centigrade="max">31</range>
        <range centigrade="min">19</range>
        </temperature>
        <rainfallchance unit="%">
        <period hour="00-06">0</period> ←コレ
        <period hour="06-12">0</period> ←コレ
        <period hour="12-18">0</period> ←コレ
        <period hour="18-24">10</period> ←コレ
        </rainfallchance>
    </info>

コレを取得して、後はうまい具合お知らせしてくれるようにすればOK

各コードの解説

import requests, xml.etree.ElementTree as ET, os

3つのモジュールを呼び出しています
requestsはwebから情報を取得できるモジュール
osは使用してるos依存の機能をpython越しで使えるモジュール
xml.etree.ElementTreeXMLファイルやテキストを解析して辞書型的なノリで扱えるようにするモジュール

# XMLをwebから取得
def get_xml():
    r = requests.get('https://www.drk7.jp/weather/xml/08.xml')
    r.encoding = r.apparent_encoding
    return r.text

関数get_xml()を定義してます
requestsモジュールで先程のXMLの情報を取得してます
そのままテキスト化したら文字化けしたので、.apparent_encodingエンコーディングしてます
テキスト化されたxmlを返します
requestsについては以下のとおり学びました

atc.hateblo.jp


a = get_xml()
# XMLから降水確率を取得
def get_rainfallchance():
    root = ET.fromstring(a) #パース
    area = root.findall(".//area[@id]")  #北部と南部
    south = area[1] #南部エリアのノード
    info = south.findall('.//info[@date]') #南部の7日分
    today = info[0] #南部の今日の分のノード
    period = today.findall('.//period[@hour]') #南部の今日の降水確率(6時間毎)
    return period

テキスト化したXMLを変数aに入れ、新たに定義した関数の中でaをパースして辞書型的なノリで使えるようにしてます
ここらへんのコードゴッチャゴチャですが、初めてなのでしゃーないしゃーない
その後、公式ドキュメント20.5.2.2. サポートされている XPath 構文にある方法で、今日の降水確率にジリジリ迫ってきています
そして南部の今日の降水確率が格納された変数periodを返してます

#取得した降水確率をテキスト化してリストにappend
rain = get_rainfallchance()
ch = []
for i in rain:
    ch.append(i.text)

取得した今日の降水確率をテキスト化してリストに入れています

#通知センターに表示
os.system("osascript -e 'display notification\"{0[0]}%     {0[1]}%     {0[2]}%     {0[3]}%\" with title \"今日の降水確率\"subtitle\"00-06  07~12  13-18  19-24\"'".format(ch))

ここが最高にテキトーで草生えるコードです、すき間だらけでダサみが深い
os.system()の引数にAppleScriptdisplay notificationコマンドを入れてmacOSの通知センターに通知を出してます
タイトルに今日の降水確率の文字列、サブタイトルに時間帯、メインに降水確率を出力しているのですが、いい具合に時間帯の下に降水確率が来るように調整していたらすきっ歯になりました
プログラミング初心者って感じがして愛おしい😍
なお、os.system()については以下のとおり学んでます

atc.hateblo.jp

後は定期的に実行する

作成したpythonファイルを定期的に実行させたいと思い調べたところ、cronという言葉がでてきました

cronとは? Wikipedia

デーモン(Deamon)と呼ばれるUnix系OSにおいてバックグラウンドで動作するプロセスのうち、コマンドを定期実行するもの。crontabというファイルに定期実行するものの一覧を記録している。

何ができる?

コマンドやファイルなどを定期的に実行させることができる
使い方については以下を参考にしました

qiita.com

実践

  • 編集開始
$ crontab -e

まだ設定していないので何もないっすね
どうやらviエディタとかいう画面らしいっすね
f:id:Atc:20180626185210p:plain

  • 設定方法は以下の通りするらしい
(分)(時)(日)(月)(曜日) 実行するファイルの絶対パス

毎朝7:30なので以下の通り設定
cronは実行される度/var/mail/にメールが溜まっていくらしいので、メールが来ないようにも設定

MAILTO=''
30 7 * * *  python3 /Users/○○○/python/weather_pop/weather_pop.py
  • ちょっと詰まったところ
    ターミナルで上記コマンドを打ち終えたところ、どうやって設定を保存するのかわからなくなってしまった...
    viエディタはaで入力モード、escでコマンドモードに移る
    コマンドモードで:wとすることで保存
    :qとすることでviエディタが終了となる
    以下を参考にしました

https://oxynotes.com/?p=3844

cron 設定 macでいくら調べても保存方法が出てこないので頭を抱えていたのですが、常識なんですね...

おわりに

毎朝降水確率を教えてくれるようになりました
これで安心して布団を干せます. はじめてイチから作ってみましたが、学ぶ度自分の目標に近づく感覚が楽しかったです
次は何作ろ


追記 crontabの設定 2018/06/27

本日7時30分wktkしていたのですが実行されなかったのでMAILTOの設定を消してもう一度7:35に実行させ、ログを取得したところ/bin/sh: python3: command not foundとでていました
パスが通っていないところにpython3があるっぽいので、crontabにフルパスでpython3の在り処を指定してあげたら実行されました ↓参考

ja.stackoverflow.com

30 7 * * * /Users/○○○/.pyenv/shims/python3 /Users/○○○/python/weather_pop/weather_pop.py

なお、python3の在り処は下記コマンドで取得可

$ which python3

pythonからmacOSの通知センターを操る方法

pythonで出力する文字列をmacの通知で表示したいと思ってます
python mac 通知で検索した結果いくつか選択肢が出てきたので整理してみます
(逐次参考にさせて頂いたサイトを載せて行きます)

選択肢

  1. terminal-notifier github.com macOSの通知を操作できるコマンドラインツール

  2. Alerter dev.classmethod.jp macOSの通知を扱えるコマンドラインツール

  3. AppleScriptのdisplay notificationコマンドとosモジュール qiita.com

選んだのは、3.

コマンドラインツールをインストールすれば簡単に実現できそうですが、AppleScliptが色々できて面白そうなのでこれを機に調べてみたくなりました

さて、AppleScliptを用いて「pythonからmacOSの通知を操る方法」は、一通り調べた結果以下のような手順を踏めばできそうです

1. pyhonでos.system()の引数にosascriptコマンドを入力して実行
2. サブシェルでosascriptコマンドが実行される
3. AppleScliptのdisplay notificationが実行される
4. macOSの通知に文字列が表示される

ひとつずつ調べていこう

1. os.system(COMMAND)とは?

16.1. os --- 雑多なオペレーティングシステムインタフェース — Python 3.6.6rc1 ドキュメント

os.systemはpythonに標準で入っているosモジュールで使える
公式ドキュメントに沿ってosモジュールをお勉強しようと思ったのですが、わからない単語の嵐で全然理解できなかったので、今回はos.system()についてのみ調べます。

以下公式ドキュメントから引用

サブシェル内でコマンド (文字列) を実行します。この関数は標準 C 関数 system() を使って実装されており、system() と同じ制限があります。sys.stdin などに対する変更を行っても、実行されるコマンドの環境には反映されません。command が何らかの出力を生成した場合、インタープリターの標準出力ストリームに送られます。


全然わからんんんんんんn宇宙後かょ
分からない単語を列挙して1つずつ理解しよう

* サブシェル
sub shell ( csh )
コマンド:鐃緒申鐃瞬ワ申鐃緒申鐃緒申: UNIX/Linuxの部屋

シェル内でコマンドを()で囲って実行することで起動するシェル。これによって起動したプログラムを子プロセスと呼ぶ(起動した側は親プロセス)。子プロセスで別ディレクトリに移動しても、子プロセスが終了すれば親プロセス=つまり元のディレクトリに戻ってくる


* 標準C関数system()
シェルコマンドを実行できるC言語の関数


* sys.stdin
29.1. sys — システムパラメータと関数 — Python 3.6.5 ドキュメント,
algorithm.joho.info

pythonでキーボードからの標準入力ができる


* 標準出力ストリーム
nononochi.hatenablog.jp
標準ストリーム:Unix系OSでプロセスと端末間の接続としてはじめから接続されている入出力のチャネル(チャネル=データの通路)
標準入力(stdin) デフォはキーボード
標準出力(stdout) デフォはディスプレイ
標準エラー出力(stderr) デフォはディスプレイ
と3つの入出力がある

つまり?

pythonのos.system('COMMAND')は、引数に入力された文字列を子プロセスとして実行する。この関数はC言語のsystem()関数を使って実装されており当該関数と同様の制約がある。引数に入力した文字列により何かしらの出力が発生したら、それはインタプリタに出力される

→ということは、os.system()を使えばpythonbashのコマンドであるlsやcdが使えるし、cdでディレクトリを移動してもpythonのプログラムが終了すれば元のディレクトリに戻るよ、って事ですか?なるほどわからん

2. osascriptコマンドとは?

コマンド/osascript - MacWiki

ターミナルでAppleScliptを実行するためのコマンド
osascript <ファイル>コマンドラインからスクリプトファイルを呼びだして実行できたり、osascript -e 'コード'でコードを入力することでも実行できる。


3. AppleScliptのdisplay notificationとは?

alvinalexander.com

通知センターに通知できるAppleScliptのコマンド

まとめ

os.system('COMMAND')の引数にosascript -e 'display notification "出力する文字列"'と入力することでpythonから通知センターの通知を操れるのだ

試しにAppleScliptで遊んでみる

pythonからdisplay notificationする前にちょっとAppleScliptで遊んでみたいと思います。
このサイトを参考にしました

鳶嶋工房 / AppleScript / 入門 / AppleScriptってなに?

当該サイトではスクリプトエディタからファイルを新規作成してますが、めんどくさかったのでターミナルからosascriptコマンドで実行してみます

環境

Mac book pro (13-inch, 2017, Two Thunderbolt 3 ports)
macOS High Sierra ver10.13.4

  • ビープ音
$ osascript -e 'beep 2'  #2回鳴る
$ osascript -e 'beep 4'  #4回鳴る
  • ダイアログ
$ osascript -e 'display dialog "Hello World"'
button returned:OK

f:id:Atc:20180625184648p:plain
画像でけぇ...

  • 通知
osascript -e 'display notification "こんにちは世界"'

f:id:Atc:20180625185149p:plain


pythonから実行してみる

>>> import os
>>> a = 'Hello'
>>> os.system("osascript -e 'display notification\" {}\"'".format(a))
0

↓↓
f:id:Atc:20180625185836p:plain
やったぜ〜〜!!
ちなみに出力結果の「0」はコマンドが成功した際に返されるそうです

pythonからwebにアクセスして情報を取得するために_requests

はじめに

WEBから降水確率を取得して毎朝デスクトップ上に通知させたいと思ってます
YahooAPIを取得してGithubリポジトリを登録するまで気合いMAXで行いましたが、 YahooAPIでは60分後までの降水確率しか取得できないと判明...

天気予報は洗濯物のために使ってる私は1日分欲しいので色々調べた結果、
気象庁が公開している気象情報をXMLにしてくれている方がいました。
これなら6時間毎の降水確率がわかりそう

気象庁の天気予報情報を XML で配信 - drk7jp

ということでこちらのページにアクセスして情報を取得したいと思います。
まずpython web xml 取得で検索して色々調べた結果urllibとかrequestsとかいうモジュールを使えば良さそう
これらのモジュールは何ができるのか、何を得られるのかを調べてみます

WEBへのアクセスについての知識

インターネットへのアクセスは、
1. 私たちが使ってるWEBブラウザからWEBページが存在するWEBサーバにリクエストを送る
2. WEBサーバはリクエストに対してレスポンスを返す
3. そのレスポンスを元にWEBブラウザはページを開くことができる
という流れで行われているらしい

WEBでリクエストとレスポンスを行う為に、HTTPという通信プロトコル(約束事)が決められてる。そのためHTTPリクエスト、HTTPレスポンスとも呼ぶ

HTTPリクエス

HTTPリクエストは「リクエストライン」「リクエストヘッダ」「リクエストメッセージボディ」から構成される

  • ライン
    HTTPリクエストの1行目に書かれている
    [メソッド] [URL] [HTTPバージョン]で構成される
    メソッドにはPOST(値がボディにつく)やGET(値がURLにつく)があるらしい
    requestモジュールかなにかのリファレンスでPOSTかGETか指定できるメソッドがあったので実際使う場面が出てきたら覚えたい

  • ヘッダ
    HTTPリクエストの際のお願い事とかが書いてある

  • メッセージボディ
    HTTPリクエストの際のメモ書

HTTPレスポンス

HTTPレスポンスは「ステータスライン」「レスポンスヘッダ」「レスポンスボディ」から構成される

  • ライン
    HTTPレスポンスの1行目に書かれている [HTTPバージョン] [ステータスコード=結果] [ステータスコードの詳細]で構成される
    ステータコードはwikipediaに乗っている → wiki
    とりあえず200なら成功してるってことみたい

  • ヘッダ
    ファイルの情報を示す情報()とかが書いてあるところ

  • ボディ
    HTMLの中身が入ってる



    さて、webにアクセスする際の知識を得たところで、pythonに戻る
    webにアクセスできるurllibrequestはHTTP通信を行うためのモジュール
    HTTPレスポンスを得ることができるのかな?

urllibはpythonに最初から入っているがurllibの公式リファレンスによるとrequestのほうが使い勝手がいいみたい
requestを使ってみることにした

requestsモジュールの概要

Requests: 人間のためのHTTP — requests-docs-ja 1.0.4 documentation

ここからは公式リファレンスのクイックスタートガイドに沿ってrequestsを試してみる

requestsはurllibよりも人間にわかりやすいらしい
まずはインストールしてから呼び出す

$ pip install requests
import requests

URLを指定してURLレスポンスを取得

r = requests.get('https://www.drk7.jp/weather/xml/08.xml')
print(r)

取得したレスポンスをそのまま出力したらステータスコードがでた↓

<Response [200]>

取得したHTTPレスポンスで何ができるのか

  • レスポンスの内容 requestsはサーバから得た内容を勝手に復元(デコード)してくれる。 HTTPリクエストを作成したら、requestsはリクエストヘッダに入ってる情報に基づいて円コーディングする。エンコディングしたデータは変数.textで得ることができる
r.text

結果↓

<?xml version="1.0" encoding="UTF-8"?>
<weatherforecast>
<title>weather forecast xml</title>
<link>http://www.drk7.jp/weather/xml/08.xml</link>
<description>æ°è±¡åºã®å¤©æ°äºå ±æå ±ã XML ã§éä¿¡ã1æ¥1å AM 6:00 ããæ´æ°ã</description>
<pubDate>Thu, 21 Jun 2018 18:00:02 +0900</pubDate> 
<author>æ°è±¡åº</author>
<managingEditor>drk7.jp</managingEditor>
<pref id="è¨åç">

    <area id="åé¨">
    <geo>
        <long>140.4021</long>
.........

文字化けしてる!
ググって以下記事を拝見

kanji.hatenablog.jp

以下のとおり記入することで解決(詳しい原因は調べてない)

r.encoding = r.apparent_encoding
print(r.text)

↓↓

<?xml version="1.0" encoding="UTF-8"?>
<weatherforecast>
<title>weather forecast xml</title>
<link>http://www.drk7.jp/weather/xml/08.xml</link>
<description>気象庁の天気予報情報を XML で配信。11回 AM 6:00 ごろ更新。</description>
<pubDate>Thu, 21 Jun 2018 18:00:02 +0900</pubDate> 
<author>気象庁</author>
<managingEditor>drk7.jp</managingEditor>
<pref id="茨城県">

    <area id="北部">
    <geo>
        <long>140.4021</long>
        <lat>36.3980</lat>

OK~!


エンコーディング変数.eoncodingで調べることができる

r.encoding

↓↓

ISO-8859-1  #encodingにapparent_encodingを指定する前
utf-8 #指定した後


レスポンスのステータスコードを知ることもできる
ステータスコードの意味は先程書いたようにWikipediaにある

r.status_code

↓↓

200


レスポンスヘッダーをみる

r.headers

↓↓

{'Content-Type': 'text/xml', 'Accept-Ranges': 'bytes', 'ETag': '"3863184871"', 'Last-Modified': 'Thu, 21 Jun 2018 09:00:11 GMT', 'Content-Length': '8839', 'Date': 'Thu, 21 Jun 2018 10:03:43 GMT', 'Server': 'lighttpd/1.4.33'}

辞書で取得できるためキーを指定して値を取得できるようだが、大文字でも小文字でもOKみたい
特に今は使わなそうなのでハショリます

URLにパラメータを渡す

APIを使うときにパラメータを指定させることで特定の条件の商品などを取得できるみたい
今回は余裕がないのでまたあとで...
参考:http://bty.sakura.ne.jp/wp/archives/1157


とりあえず今回はxmlをテキスト化することができたので以上。
次はxmlの中の値を取得します

pythonのSeleniumはスクレイピングできるやつ

スクレイピングという文字列に憧れを持っているメンズです。
将来はスクレイピングをする人 = スクレイピング人 = すくれいぴんちゅ と呼ばれたくてウズウズしてます。
調べてみたらSeleniumというモジュールが出てきました。
気になったので調べてみたというブログです。
シュタインズゲートに似たような言葉が出てきたような気がしてウズウズしてますが、気のせいでしょうか?

Selenium

pythonスクレイピングをするために必要
クリックする操作等を自動化する
利用するためには専用のブラウザが必要
今回はhomebrewから'ChromeDriver'をインストール

$ brwe install chromedriver

と思ったら以下のとおりERROR

 Error: No available formula with the name "chromedriver" 
It was migrated from homebrew/core to homebrew/cask.
You can access it again by running:
  brew tap homebrew/cask
And then you can install it by running:
  brew cask install chromedriver

caskに移動したからよろしくって感じ?

caskとは?

参考
パッケージ管理のhomebrewは開発環境をシェルでコマンドを打ち込むことで導入&管理が可能
homebrew-caskはdropboxchrome等.dmgファイルもシェル内でコマンドを入力することでインストールができるようになるツール 当該記事
下記を実行することで導入できる

# 導入
$ brew install brew-cask
# chromedriverの導入
$ brew cask install chromedriver

でもあまりツールは増やしたくないので普通にブラウザからインストールすることにした
...と思って調べてたら他のブラウザについても出てきた
最初に参考にしたサイトでChromeを使ってたのでわかりませんでしたが、ちゃんと各ブラウザごとにあるんですねすごい。 私はFirefoxユーザなのでfirefox版webdriverを以下からDL

github
DLたgeckodriverはPATHが通っているフォルダに移動させて実行権限を付与するらしいので下記にコピー(理由はよくわかってない💩)

/usr/local/bin/

ここで疑問

/usr/local/binってどこ?

参考teratail,参考ブログ

  • usr/bin/ 元々OS等に標準で入っているcdやls等ツールの格納先
  • usr/local/bin 自分で後から追加して組み込んだツール等の格納先
    usr/local/binにアクセスしてみるとpyenv-virtualenvとか自分で入れたツールが入ってる
    ターミナルでのアクセス方法がわからなかったので、shift+command+Gでfinderの検索窓を開き、usr/local/binと入力。ダウンロードしたgeckodriverドラッグアンドドロップした。

なんかよくわからんが権限のあるところに置けばgeckodriverが使えるってことかな?

seleniumをインストール

$ pip install selenium
Collecting selenium
  Downloading https://files.pythonhosted.org/packages/57/bc/17164fd471ccdf0df3a992c710c0c3c47743462ff41ab72a02c6ede96e90/selenium-3.12.0-py2.py3-none-any.whl (946kB)
    100% |████████████████████████████████| 952kB 246kB/s 
Installing collected packages: selenium
Successfully installed selenium-3.12.0
You are using pip version 9.0.1, however version 10.0.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

seleniumを試してみる!

from selenium import webdriver

webdriverのChrome又はfirefoxインスタンスを作成することで各ブラウザの操作ができる

driver = webdriver.Chrome() #Chrome
driver1 = webdriver.firefox() #firefox

(ググったら「引数に何も指定しない場合、各ブラウザのwebdriverがPATHの通ったディレクトリに配置されていないと実行に失敗するので注意。」と書いてあった。よく意味がわかってない💩)

ブラウザの開閉

  • ブラウザを開く
webdriver.Firefox()
  • ページを開く
driver.get('URL')
  • WEBページを閉じる
driver.close()
  • ブラウザを閉じる
driver.quit()

要素にアクセス

HTMLの要素を指定して値を取得することが可能 * idの取得

driver.find_element_by_id('ID')
  • classで取得
driver.find_element_by_class_name('CLASS_NAME')
  • nameで取得
driver.find_element_by_name('NAME')
  • link textで取得
driver.find_elements_by_link_text('LINK_TEXT')
  • ネストされた要素はpathを指定して取得
driver.find_elements_by_xpath(".//a")

アクション

取得した要素に対して、アクションを起こしてwebページを操作する

  • ボタンをクリック
driver.find_element_by_id('btn').click()
  • フォームに文字入力
driver.find_element_by_name('From').send_keys('text')

コンソール上でのWEBの操作にはHTMLの知識が必要そうだった。 ウェブスクレイピングして自動で情報をかき集めてみたいので、HTMLも勉強してみよう

seleniumの活用方法1.qiita
seleniumの活用方法2.qiita

Githubの使い方を覚える

yahooから気象情報を取得して毎朝表示してくれるものを作りたいと思ってます

作成する前に

githubのissuesに作成過程を残すと良いと、とある方のブログで見たので使いたい
そもそもgithubを使ったことがない
使い方を学ぶぞ!

これよりルー大柴の化身が執筆
死ぬほど参考にさせて頂いだQiita

手順

1.gitをインストール
1'.githubアカウントを作ってリモートリポジトリ(貯蔵庫)を作成
2.ファイルを作る(.htmlとか.pyとか)
3.ローカルリポジトリを作る
4.ローカルリポジトリにインデックス(ファイルの中身の変更履歴が記録されてる)をアッド(追加)する
5.インデックスにアッドしたデータをローカルリポジトリにコミット(登録)
6.githubのリモートリポジトリにインデックスを作成
7.githubのリモートリポジトリにローカルでコミットされたデータをプッシュ

1. gitをインストール

$ brew install git

完了

1'. githubアカウントを作ってリモートリポジトリを作成

アカウントの作成は省略
リポジトリについては、github画面右上のをクリック後、new repositoryをクリック
リポジトリ名はyahoo_weatherとした。

2.ファイルを作成

yahooから気象情報を取得して毎朝表示してくれるものを作りたいと思ってます
カレントディレクトリ⇢python⇢weatherとディレクトリを作成
weatherに以下を追加してyahoo_weather.pyを作成

# Yahooから天気情報を取得
# その日の気温と降水確率を表示する

3. ローカルリポジトリの作成

ローカルリポジトリとは、自分のPCのローカルにある、githubに送るデータを送信しておくところ。

$ git init

これによりカレントディレクトリにローカルリポジトリが作成される
weatherに移動してからローカルリポジトリを作成した
(initはinitialize=初期化の意味)

4.ローカルリポジトリのインデックスにアッドする

git add yahoo_weather.py

5. ローカルリポジトリにコミット

$ git commit -m "ぱいそんファイルを作成!"

以下の通り表示された

$ git commit -m "first"
[master (root-commit) a9c3d0c] ぱいそんファイルを作成!
 1 file changed, 2 insertions(+)
 create mode 100644 python/weather/yahoo_weather

6. githubのリモートリポジトリにインデックスを作成

$ git remote add origin https://github.com/私のユーザID/yahoo_weather.git

7. githubのリモートリポジトリにローカルでコミットされたデータをプッシュ

$ git push origin master

以下の通り表示

Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (5/5), 425 bytes | 425.00 KiB/s, done.
Total 5 (delta 0), reused 0 (delta 0)
To https://github.com/私のユーザID/yahoo_weather.git
 * [new branch]      master -> master

githubを見たらちゃんと追加されてた!やった〜〜と思いきや...githubにフルネームがガッツリ乗っててヤバみが深い。
ぐぐったら同じく本名大暴露している人が。
どうやらgitをインストールしたらまずはconfigで設定を変えた方がいいみたい

Git - 最初のGitの構成

解決策として、
8. gitの設定で名前を変える
9. 公開したリポジトリを削除する
を実行することにした

8. gitの設定

$ git config --global user.name 名前
$ git config --global user.email メアド

9. 公開したリポジトリを削除

yahoo_weatherのリポジトリのページにあるsettingをクリック
ページ下部のDanger ZoneDelete this repositoryをクリック
削除したいリポジトリ名を入力してI understandで削除完了

最後にもう一度 1'から実行してみる
githubリポジトリにインデックスを作成する際に以下のエラーメッセージが出てくる。

fatal: remote origin already exists.

ぐぐったらoriginを削除して再度登録すればよいとのこと、以下を実行

$ git remote rm origin  #originを削除
$ git remote add origin https://github.com/私のユーザID/yahoo_weather.git  #再登録

エラーは出なくなった。

ブランチという別バージョンのリポジトリを作成するワザもあるようだが、 機会があったらあとでまたやろう。

issues忘れてた!!