シュンカの日記

基本的に書きたいことを書いていくスタイル。

Apache上でFlaskを動かすまで(Apache2+wsgi+Flask)

PythonのフレームワークであるFlaskでWebアプリを作成中。
Web上で公開したいため、Apacheと連携させるまでの手順を書く。

ここでは、開発環境(ローカル)でFlaskの動作検証を実施するとともに、その後本番環境(本番Apache)上で動かすまでの手順を書いていく。

環境

※下記は全てインストール済の前提で進めていく

  • OS:Raspbian(Raspberry Pi 3)
  • Pythonバージョン: 3.6.3
  • ウェブサーバー:Apache2

なおPythonアプリの場所は、/var/www/html/flask
でやっていくが、適宜、各々の環境に置き換えてほしい。

参考

qiita.com

開発環境での動作検証

Flaskインストール

pip install flask

テストアプリ作成

[/var/www/html/flask/Hello.py]

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return 'Hello'

if __name__ == '__main__':
    app.debug = True
    app.run(host='0.0.0.0', port=80)

本番環境(公開サーバー)で動かす

apache2のインストール

apache2は既にインストール済の前提だが、なければ下記

 sudo apt-get -y install apache2

mod_wsgiのインストール

mod_wsgi(ウィズギー)は、PythonフレームワークとApache2を連携させるためのインターフェースモジュール。らしい。
wsgi無しでは、PythonをWebサーバーと連携できないので、インストールが必要。※一応cgiを使うという手もある

sudo apt-get -y install libapache2-mod-wsgi

wsgiファイルを作成

/var/www/html/flask にwsgiファイルを作成する。
[/var/www/html/flask/test.wsgi]

import sys, site

#pythonのpathを指定
site.addsitedir('/path/python3.6/site-packages') 

#wsgi, Flaskファイルのディレクトリを指定
sys.path.insert(0, '/var/www/html/flask')
from Hello import app as application

apache2の設定ファイルでVirtualHostを追加

バーチャルホストを使えば、一つのドメインで複数のサイトを作ることができる。
デフォルト(80番)でよければいいが、自分は既に別サイトで使用していたので、ポート8080番でVirtualHostを追加する。

念の為、デフォルトの設定ファイルはいじらず、新しく作る。

sudo cp 000-default.conf 001-default.conf

下記コードを追加する。
[/etc/apache2/sites-available/001-default.conf]

<VirtualHost *:8080>

 WSGIDaemonProcess test user=pi group=pi threads=5
 WSGIScriptAlias / /var/www/html/flask/test.wsgi

<Directory /var/www/html/flask>
 WSGIProcessGroup test
 WSGIApplicationGroup %{GLOBAL}
 WSGIScriptReloading On

 Require all granted
</Directory>
</VirtualHost>

apache2の設定ファイルでポートを追加

上記でポートを追加した場合、設定ファイルの編集が必要。
(従って、ここは本筋ではない)
ports.confを編集して、とりあえず8080番を追加

[/etc/apache2/ports.conf]

   # If you just change the port or add more ports here, you will likely also
   # have to change the VirtualHost statement in
   # /etc/apache2/sites-enabled/000-default.conf
  
   Listen 80
   
 #以下を追加
   Listen 8080
   
   <IfModule ssl_module>
       Listen 443
   </IfModule>
 
  <IfModule mod_gnutls.c>
      Listen 443
  </IfModule>

ルーターの設定でポートを追加する

(上記同様、本筋では無いが)ルーターの設定で、今回新しく追加したポートを公開する必要がある。
今回で言えば8080番を公開する必要がある。
やり方は、ルーターの種類によって異なってくるかと思うので割愛。



以上。ポート8080番で、Flaskアプリケーションが実行できればOK。
f:id:nukano0522:20180430144544p:plain

CakePHPプロジェクト作成時のhtaccessの設定方法

CakePHPのプロジェクトを作成したまではいいが、htaccessの設定に不備があるようで、CSS等のファイル読込がうまくいかなかった。

不備事象

下記のように、「URL rewriting is not properly configured on your server」というエラーメッセージが表示される。CSS等のファイルも読み込まれていないようである。
f:id:nukano0522:20180328161929p:plain

環境、前提

OS:Raspbian
Webサーバー:Apache

また、ここでは、/path/toというディレクトリ配下に、appというCakePHPプロジェクトを作成した前提で話をすすめる。

対応

.htaccessを有効にする

そもそも.htaccessの読込が有効化されていなかったようなので、apacheの設定ファイルを編集する。

以下を追記する。
[apache2.conf]

LoadModule rewrite_module /usr/lib/apache2/modules/mod_rewrite.so

ちなみにmod_rewrite.soの場所は予め調べておく。
f:id:nukano0522:20180331153536p:plain

.htaccessのoverrideの設定を変更

apacheの設定ファイルに下記を追記

[apache2.conf]

<Directory /path/to/app>
    Options FollowSymLinks
    AllowOverride All 
</Directory>

参考:公式サイト
https://book.cakephp.org/3.0/en/installation.html#url-rewriting

.htaccessを編集

.htaccessのファイルを編集する。
こちらも公式サイトを参考。

[/path/to/app/.htaccess]

<IfModule mod_rewrite.c>
    RewriteEngine on
    #RewriteBaseの記述を追加
    RewriteBase   /app
    RewriteRule    ^$    webroot/    [L] 
    RewriteRule    (.*)  webroot/$1    [L] 
</IfModule>

[/path/to/app/webroot/.htaccess]

<IfModule mod_rewrite.c>
    RewriteEngine On
    #RewriteBaseの記述を追加
    RewriteBase /app/webroot
    RewriteCond %{REQUEST_FILENAME} !-f 
    RewriteRule ^ index.php [L] 
</IfModule>

参考:公式サイト
https://book.cakephp.org/3.0/enひょうじされるようになりました。ml#url-rewriting


以上、ちゃんとひょうじされるようになりました。
f:id:nukano0522:20180331155444p:plain

長時間労働で得たもの失ったもの

去年の夏頃から参画していたプロジェクトがようやく一段落したので、久しぶりの更新。

今回のプロジェクトは、今の会社に転職して3つ目のプロジェクトで、それなりに大変で労働時間も多かった。
その中で得たもの失ったものを書いていく。

プロジェクトの超概要

あまり細かいことは書けないが、ざっくりいうとシステム開発の案件である。
もう少し言うと、今流行り(もうコモディティ化している?)のデータ分析の基盤を導入するプロジェクトになる。

細かい話をすると、データウェアハウスの構築、集めたデータを可視化する(レポーティング)プロジェクトとなる。


様々な役割がある中で、自分は2つの領域の担当を任された。

短納期かつ(データ分析基盤としては)大規模な案件であり、(自分も含め)経験の少ない若手中心のメンバー構成であったため、労働時間は大きくなりがちだった。

難易度が高くタスク量が多い中でいかにして残業を減らすか、というのも大切なテーマかとは思うが、ここでは触れないでおく。
たくさん残業をしてしまった反省みたいなものは別途行うものとして、ここではたくさん残業をしてしまったことを通して得られたこと/失ったことを書き留めておく。

ちなみに、残業時間は平常時が40時間/月くらいで、ピーク時は80時間/月であった。
これを多いと捉えるか少ないと捉えるかは人によりけりだと思うが、自分にとっては「長時間」であることを断っておく。

長時間労働で得たもの

やり抜く力/責任感

正直何度か投げ出したい気持ちに駆り立てられた(実際、体調不良でもないのに休んだこともあった)が、結果的にやり遂げることができた。

クライアントのためや同僚のためにがんばった、、、というのもないわけではないが、
途中で投げ出したら結局自分が後悔すると思ったから、というのが正直なところ。

これを責任感と呼べるかどうかはわからないが、最後までやり抜いたことは事実なので、自分のキャリアの中で大きな糧になったということにしておく。
実際、”責任感”という観点で上位者にもそれなりに評価してもらった。

また、難易度の高い仕事を最後までやれたのは、多少自信にもなった。

しかし、結果的にやり遂げられたが一歩間違えれば投げ出してしまっていたのかもしれないと考えると、”責任感”も結果論でしか無いんじゃないか、とも思う。

何とか最後まで続けられた要因として個人的に思うのは、「やばいな」と思ったときに休んでリフレッシュしたことが良かった(サボりと非難されても何も言い返せない。そういう意味だと自分は責任感なんてものは持ち合わせていないのかもしれない)

連帯感が得られる

夜遅くまで一緒に働いていると、謎の連帯感が生まれる。
やはり苦労を共にすることで信頼感や親密さが生まれるのだろうか。
(だからといって長時間残業を推奨しているわけではないことは、ここでも断っておく。)

こういった経験は、後々また一緒に働くことになったらプラスに作用するかもしれない(しないかもしれない)。

お金

幸い(当然そうあるべきなのだが)、残業した分はきっちりお給料に反映された。

自分は忙しさによるストレスを、暴飲暴食、夜遊びに発散するタイプではないので、働いた分だけお金がたまることになった。
(結局、これからの結婚資金に費やすことにはなりそうだが、残業代でまかなえると思えば得をした気分である)

サラリーマンである以上、また成果主義に完全に移行しきれていない日本の現状においては、残業はお金を貯める一つの方法ではあるので、ある意味では長時間労働も悪いことだとは言い切れないと思っている。

長時間労働で失ったもの

心のゆとり

朝起きるのがツライことが多かった。
出勤してみると意外と大丈夫なのだが、目が覚めたときに「今日も資料作成して会議に出て夜まで働くのか」と想像すると気が滅入った(結果、休んだこともあった)。

これは長時間労働というより仕事内容と自分のメンタルの問題なのかもしれないが。

でも、できれば夜遅くまで仕事はしていたくないマインドなので、長時間労働によって心のゆとりが奪われていたのは間違いないと思う。

せっかく自由な時間が使える休日も、抜け殻のように眠ることが多かった。

仕事以外の人間との交流

半年間、同じ会社の同じプロジェクトのメンバーとずっと一緒にいたような印象がある。

休日もどこか出かけようかというモチベーションがないことが多く、定期的に会っていた友人達ともここ半年間は疎遠になってしまった。
当然、新しい人と知り合うなんてことはまったく無かった(人見知りなので、あまり望んではいないけど)

趣味の時間

平日は仕事以外の時間がなかなか確保しづらく、
(繰り返しになるが)休日は仕事をしないはずなのに、平日の反動からか活動的になれなかった。

週に3~4回ほどランニングをするのが日課になっていたが、ここ半年は週1~2回、ひどいときはまったく走らないなんてこともあった。

運動がストレス解消に一役買っていたところもあるので、
   
  仕事が忙しい→ゆとりがなくなる→運動しない→仕事に身が入らない

の悪循環に陥っていた。

長時間労働は総じて良くないことが分かった

少なくとも自分にとっては、長時間労働はメリットよりデメリットのほうが多いと感じた。

一時的にお金は稼げるかもしれないが、メンタルを壊して働けなくなってしまえば、長期的には損してしまう。

自分のメンタルも強くないことがわかったので、心が壊れない程度に仕事量を調節してもらうとか、周りをもっと頼るとか、働き方をもっと考えなければならないと感じている。

以上。

python+selenium+PhantomJSでスクレイピングしたデータをMySQLに格納するまで

python+selenium+PhantomJSでスクレイピングしたデータをMySQLに格納するまでの手順です。
RUNNETというマラソンのポータルサイトがあるんですが、そこから大会のエントリーリストを取得し、MySQLにデータを格納します。

ちなみにRUNNETからはいろんな大会が見れていいんですが、欲しくない情報もいっぱいあるので、自分の欲しい大会情報だけ抽出して管理できたらな~と思いやってみました。

環境など

OS:Raspbian
Python:3.6.3(pyenv)
スクレイピング対象のWebページ:RUNNET(https://runnet.jp/entry/runtes/user/pc/RaceSearchZZSDetailAction.do)

selenium+PhantomJSをインストール

まずは、seleniumをインストールします

pip install selenium

PhantomJSをインストールします

wget https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2
tar xvf phantomjs-2.1.1-linux-x86_64.tar.bz2
sudo cp phantomjs-2.1.1-linux-x86_64/bin/phantomjs /usr/local/bin/ #PATHの通ったディレクトリにコピーする
sudo apt-get install -y libfontconfig1 fonts-migmix #スクリーンショットを撮るために必要

以下コマンドでバージョン名が確認できればインストールされています。

phantomjs -v

Webページ(RUNNET)からデータを取得する

Webページを開く

Webページ(RUNNET)を開いてみます。

###test.py###
from selenium import webdriver
driver = webdriver.PhantomJS()
driver.get("http://runnet.jp/entry/runtes/user/pc/RaceSearchZZSDetailAction.do")

スクリーンショットを取って処理内容を確認することができます。

###test.py###
driver.save_screenshot('test.png')

test.pngを開くと以下のようになっています。RUNNETの大会検索のページを開くことができています。
f:id:nukano0522:20180108160339p:plain

要素にアクセスし、検索条件を指定する

このままだと余計な大会も検索されてしまうので、検索条件を指定して結果を絞り込みたいです。
ここでは、”フルマラソン”を検索条件に指定して、フルマラソンの大会リストを取得してみます。
開発者ツール(F12)から、フルマラソンの要素を調べます。
f:id:nukano0522:20180108161557p:plain

「id="dummy_distance02"」とあるので、下記のようにidを指定してやると、要素にアクセスできます。

###test.py###
#フルマラソン
elem_distance = driver.find_element_by_id("dummy_distance02")
elem_distance.click()

先ほどと同様に、スクリーンショットを確認してみます。”フルマラソン”にチェックが付いています。
f:id:nukano0522:20180108162102p:plain

開発者ツールから検索ボタンの要素を調べ、検索ボタンをクリックします。

###test.py###
#検索ボタンをクリック
elem_search=driver.find_element_by_id("easy_search")
elem_search.click()

フルマラソンの大会リストを取得する

各大会のクラス名は、下記のように「class="item"」となっています。この中に、大会名、開催場所、開催日といった欲しい情報が入っています。
f:id:nukano0522:20180108172951p:plain

ですので、For文を利用して、各itemに対して大会の情報を取得していきます。

###test.py###
info=[] #各大会の情報を格納するためのリストを定義
for e in driver.find_elements_by_css_selector(".item"):
	try:
		info.append({
			'name':e.find_element_by_css_selector(".title_run").text, #大会名
			'date':e.find_element_by_css_selector(".date").text, #開催日
			'place':e.find_element_by_css_selector(".place").text, #開催場所
		})
	except:
		pass

取得したデータをMySQLに格納する

MySQLでテーブルを作成しておく

いったんMySQL環境から、テーブルを作成します。

create table test_entry_list(name varchar(256), date varchar(100), place varchar(100));

MySQL-pythonをインストール

pythonからMySQLを扱うために、”MySQL-python”というライブラリを使います。
インストールします。

pip install MySQL-python

pythonからMySQLに接続する

pythonに戻ります。
データベース名、ユーザー名、パスワードを設定し、MySQLに接続します。

###test.py###
import MySQLdb
#MySQLにデータ格納
conn = MySQLdb.connect(db='DB名', user='ユーザー名', passwd='パスワード', charset='utf8mb4')
c = conn.cursor()

テーブルにデータを格納する

先ほど作成したテーブル”test_entry_list”に、info内の大会情報を格納します。
For文を利用して、1大会ずつ順番にINSERTしていきます。

###test.py###
#1大会ずつテーブルに格納
for dict in info:
	c.execute('insert into test_entry_list values(%(name)s, %(date)s, %(place)s)', {
	'name':dict['name'], 
	'date':dict['date'], 
	'place':dict['place'],
	}
	)
conn.commit() #コミット
conn.close()

テーブルにデータが格納されているか、確認してみます。
f:id:nukano0522:20180108171201p:plain

うまくいってます。

参考書籍

pythonスクリプト(pyenv+Selenium+PhantomJS)をcrontabから実行できない場合

Python(pyenv+Selenium+PhantomJS)でスクレイピングのスクリプトを定期実行しようと、crontabに登録したのだが、実行されなかったので対処法をメモしておきます。

エラー現象など

■OS、Pythonのバージョン
OS:Raspbian for Raspberry Pi
Python:3.6.3(pyenv)

■現象

  • cronは起動している状態
  • 端末上でPythonスクリプトの直接実行は問題なくできる
  • 他のスクリプト(シェル、PHPなど)はcronからも実行できている
  • Pythonスクリプトだけcronから実行されない
  • ログファイルにはログが吐かれていない

解決方法

結論から言うと、下記で解決しました。解決までに2時間ほどかかってしまいました・・。

  • crontabで、pyenv環境下のPythonを明示的に指定して実行
  • crontabで、PATHを記載

解決方法の詳細

ログファイルには何も吐き出されていないので、そもそもPythonスクリプトが実行されていない模様でした。

調べてみると、crontabから以下のように「Python」を指定すると、pyenv環境下のPythonではなくデフォルトのPythonを呼びだそうとして、うまく実行されないみたいです。

00 03 * * * cd /var/www/path; python hoge.py

というわけで、pyenv環境下のPythonを明示的に呼び出すようにします。
pythonの場所は、コマンド「which python」で確認可能です。

00 03 * * * cd /var/www/path; /home/ユーザー名/.pyenv/shims/python hoge.py

これでcronから実行したのですが、今度は以下のようなエラーログが出てしまいました。

selenium.common.exceptions.WebDriverException: Message: 'phantomjs' executable needs to be in PATH.

「PhantomJSのPATHが通っていない」的なエラー。

こちらはcrontabにPATHを直接記載することで解消しました。
PATHの内容はコマンド「echo $PATH」で確認可能です。

PATH=/home/ユーザー名/.pyenv/shims:/home/ユーザー名/.pyenv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games  
00 03 * * * cd /var/www/path; /home/ユーザー名/.pyenv/shims/python hoge.py

これでcrontabからPythonスクリプトを実行できました。
pyenvとseleniumを使っていることが、今回のエラー要因でもあったみたいなので、
普通にPython使っている分には起こりえないかもしれませんが・・・。

以上でした。

Ubuntuにログインできなくなった場合の対処法

Ubuntuのパッケージをアップデートしている最中に電源を切ってしまったところ、ログインできなくなってしまいました。

具体的には、ログイン画面までは行けるんですが、パスワードを入力しても画面が固まって先に進まないという現象。

Ubuntuのリカバリーモードでパッケージの修復を行ったところ解決したので、手順を書いておきます。

ちなみに、Ubuntuのバージョンは16.04です。

続きを読む

女性管理職は離婚しやすいのは本当か、調べてみた

今の会社ではバリキャリの女性管理職をよく見聞きするんだけど、どうやら離婚率も高いらしい。

「あの人(女性管理職)って、離婚してるらしいよ」って話をよく聞きます。(主に若手女性社員から笑)


実際女性管理職って離婚しやすいの?って疑問に感じたので調べてみた。

続きを読む