まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

タグ:python

このブログエントリを書くきっかけとなったのはこの記事でした:
2021年5月のWebサーバ利用シェア、「Nginx」が「Apache」を初めて上回る


ウェブサーバー(HTTP サーバー)のシェア争いで、ついに Nginx が Apache を抜いて首位にたった、というものです。この2つの合計だけで全体の 70% 前後あるという「2トップ」ですが、少しずつ差を縮めた結果、5月になってわずかながら Nginx のシェアが Apache を上回ったようでした。

・・・で、その結果は置いといて(苦笑)、自分自身が気になったのは順位では6位に位置づけられている "Node.js" です:



Nginx や Apache は、例えば WordPress や Drupal のような有名な CMS のウェブサーバーとしても使われていたりするし、他にも Ruby on Rails を使う場合のウェブサーバーとして利用されるので、多くのプログラミング言語/コンテンツ管理システムのウェブサーバーとして採用されていることがシェアが高い背景として想像できます。

その観点で見た場合、6位の "Node.js" は異色といえます。なぜならば Node.js はあくまでプログラミング言語であって、これ自体はウェブサーバーではないからです。統計としては「Node.js で作ったウェブサイトのページ」をカウントしているのだと想像できるし、(Node.js がウェブサーバーではない以上)他に分類のしようがないことも理由だと思いますが、いろんな言語で作られたページのウェブサーバーである Nginx や Apache がランクインしている中では「違うカテゴリーの人が混ざってる」感は否めません。

そして最も気になったのは「そもそも、どうやって Node.js で作ったページだとわかったのか?」でした。Nginx や Apache だとわざと 404 エラーを出してそのエラー画面から調べる、という方法もあるのですが、Node.js では(特に明示しない限り)味気ない 404 エラーがでるだけなので調べようがないはずです。どうやって Node.js ウェブページのシェアを数えたのだろう? という興味が湧いて調べてみたのでした。

結論としてはごくシンプルで、404 エラーを出さなくても HTTP レスポンスヘッダの中に答がありました:
2021052501


Node.js でウェブアプリを作る際にほぼ 100% 使われるであろう Express ライブラリを使ってアプリを作って起動し、そこに curl コマンドを HTTP レスポンスヘッダのみ返すようなオプションをつけて実行してリクエストとレスポンスのヘッダ情報も確認しました。するとレスポンスヘッダの中に "X-Powered-By: Express" という項目がありました。なるほど、「この情報が含まれるページは Node.js 製」と判断したんだろうな。。


で、この情報がわかると、同様にウェブサーバーを併用しない開発言語である Python はどうなっているんだろう? と気になりました。ウェブサーバーランキングには入ってないけど、Python で作ったウェブページかどうかも Node.js と同様にして調べることができるのだろうか・・・と。

で、こちらも Flask ライブラリを使ってアプリを起動し、同様に curl コマンドで HTTP レスポンスヘッダを取得するモードで実行してヘッダを確認すると・・・ こちらはレスポンスヘッダの中に "Server: Werkzeug/0.14.1 Python/3.6.7" というわかりやすい項目がありました。Python の場合は「ウェブページを作る際のライブラリはほぼ 100% Flask」とは言い切れない所がありますが、まあでも同様に調べることはできそうですね。
2021052502


(おまけ)ところで、このブログエントリの中でも使っているのですが、「curl コマンドで HTTP レスポンスヘッダのみ調べる」方法を調べました。複数のオプションを組み合わせる形で実現しているのですが、結論としてはこんな感じでした:
$ curl -D - -s -o /dev/null http://localhost:nnnn/

3つのオプションを組みあわせて実現しています:
  • -D - : dump header オプション、標準出力にヘッダを出力する
  • -s : slient オプション、途中経過を出力しない
  • -o /dev/null : output オプション、(ヘッダ以外の)本体を /dev/null へ出力して廃棄


【参照】
https://qiita.com/yousan/items/fcc15e1046939c465ab7

Python の代表的なウェブアプリケーションフレームワークの1つに Flask があります:
2018053101


「マイクロフレームワーク」と称されるほどの計量な設計になっており、個人的にも Python でウェブアプリ開発する時のフレームワークとして使っています。この Flask でルーティング処理を行う場合は以下のように記述します:
  :
from flask import Flask
  :

app = Flask(__name__)

@app.route('/hello')
def hello():
  return "Hello!"

  :
  :

↑この例では GET /hello というリクエストに対して hello() 関数を割り当て、実行結果として "Hello!" という文字列を返すようにしています。


この "/hello" の部分を正規表現にして、例えば GET /(数字).xxx という形でリクエストを受け取って処理を行いたいのですが、どうすればよいか? というのが本エントリのテーマです。

答えはこんな感じになります:
  :
from flask import Flask
from werkzeug.routing import BaseConverter
  :

app = Flask(__name__)

class RegexConverter(BaseConverter):
  def __init__(self, url_map, *items):
    super(RegexConverter, self).__init__(url_map)
    self.regex = items[0]

app.url_map.converters['regex'] = RegexConverter

@app.route('/hello')
def hello():
  return "Hello!"

@app.route('/<regex("[0-9]*"):id>.xxx')
def xxx(id):
    return ( "id = %s" % (id) )

  :
  :

まず正規表現でパスを解釈するためのコンバータークラス: RegexConverter を定義し、
 <regex( XXX ):v>
という形で正規表現を処理できるようにします( XXX 部分の正規表現にマッチしたら、その結果を v という変数に代入する)。

その上で
 @app.route('/<regex("[0-9]*"):id>.xxx')
 def xxx(id):
  :

の定義によって、/(数字).xxx というパスへの GET リクエストを受け取り、この(数字)の部分を id 変数に代入した上で xxx( id ) 関数が呼び出される、という形で実現しています。

なので、例えば /123.xxx に GET リクエストを行うと、id = 123 が代入された状態で xxx( id ) 関数が実行されます。



(参考)



マルチプラットフォーム対応の Python IDE である Rodeo を CentOS/RHEL にインストールする手順を紹介します:
https://www.yhat.com/products/rodeo

2017021300


といっても手順として特別なことはなく、リポジトリを追加して yum でインストールするだけです:
$ sudo wget http://rodeo-rpm.yhat.com/rodeo-rpm.repo -P /etc/yum.repos.d/
$ sudo yum install rodeo

インストール後、アプリケーションメニューの「その他」から起動できます:
2017021301


はい起動しました。簡単ですね~(※CentOS/RHEL 7 の場合)
rodeo_centos6


機械学習や数値解析に便利なライブラリが充実している Python をより便利に使うための Python IDE も充実してきてるんですね。。


なお、上でわざと(※CentOS/RHEL 7 の場合)と強調しているのには意味があります。ご覧のように上記のスクリーンショットは CentOS 6 上で動いている Rodeo の画像なのですが、この環境を作るのは一筋縄ではいかなかった、という背景があります(このスクリーンショットを撮るまでの作業が、まあ大変でした・・・)。別の機会に詳しく書くかもしれませんが、とりあえず Rodeo は CentOS/RHEL 7.x 上で動かすのが無難、と付け加えておきます。


Python から R 言語を呼び出して実行する手順をまとめました。システムは CentOS 6.x を想定しています。


具体的には Python の PypeR ライブラリを使います。なので Python と R 言語と PypeR ライブラリをインストールすることになります。ただそれだけではまだ色々不便なので、numpy や pylab などの PypeR と一緒によく使うライブラリとまとめて環境を用意して使うことにします。

まず以下の手順を実行する上で前提となるライブラリ類と Python3 をまとめて導入しておきます。
# rpm -ivh http://mirror.yandex.ru/fedora/russianfedora/russianfedora/free/el/releases/6/Everything/x86_64/os/puias-release-6-2.R.noarch.rpm
# yum install -y python3.x86_64 python3-devel.x86_64 python3-tools.x86_64 freetype* libpng-devel libjpeg-devel lapack-devel

次に pip をインストールします:
# curl -kL https://bootstrap.pypa.io/get-pip.py | python

導入した pip を使って PypeR などのライブラリをインストールします(numpy や scipy などは結構時間がかかります):
# pip install numpy
# pip install pyper
# pip install pandas
# pip install scipy
# pip install pylab

最後に R 言語環境をインストールします:
# yum install R

これで全ての環境が整いました。では R 言語と導入したライブラリを使って動かす、以下の様な Python スクリプトを用意して実行してみます(なお、この Python スクリプトはここで紹介されているものです):
# -*- coding:utf-8 -*-

import numpy
import pandas
import pylab
import pyper

n = 200
# データの生成
score_x = numpy.random.normal(171.77, 5.54, n)
score_y = numpy.random.normal(62.49, 7.89, n)

score_x.sort()
score_x = numpy.around(score_x + numpy.random.normal(scale=3.0, size=n), 2)
score_y.sort()
score_y = numpy.around(score_y + numpy.random.normal(size=n), 2)

# 散布図を描く
pylab.scatter(score_x, score_y, marker='.', linewidths=0)
pylab.grid(True)
pylab.xlabel('X')
pylab.ylabel('Y')

# Rで回帰分析
df = {'X': score_x, 'Y': score_y}
df = pandas.DataFrame(df)

r = pyper.R(use_pandas='True')

# Rへデータ渡す
r.assign('df', df)

# Rのコマンド実行
print(r("summary(df)"))
r("result <- lm(Y~X, data=df)")
print(r("summary(result)"))

#予測区間と信頼区間を算出するため
new_x = numpy.arange(155, 190, 0.1)
new_df = pandas.DataFrame({'X': new_x})
r.assign('new', new_df)

# 予測区間(R)
r("prediction <- predict(result, new, interval='prediction')")
# 信頼区間(R)
r("confidence <- predict(result, new, interval='confidence')")

# Python側にとってくる
lm_result = r.get('result$fitted.values')
prediction = pandas.DataFrame(r.get('prediction'))
confidence = pandas.DataFrame(r.get('confidence'))

# 回帰直線, 予測区間, 信頼区間を描く
pylab.plot(score_x, lm_result, 'r', linewidth=2)

pylab.plot(new_x, prediction[1], 'g', linewidth=1)
pylab.plot(new_x, prediction[2], 'g', linewidth=1)

pylab.plot(new_x, confidence[1], 'c', linewidth=1)
pylab.plot(new_x, confidence[2], 'c', linewidth=1)

pylab.show()

この内容を例えば python01.py という名前で保存し、python で実行すると以下の青字部分のような出力が確認できます:
# python3 python01.py

try({summary(df)})
       X               Y
 Min.   :153.2   Min.   :41.81
 1st Qu.:167.1   1st Qu.:57.49
 Median :171.3   Median :63.45
 Mean   :171.4   Mean   :63.28
 3rd Qu.:176.3   3rd Qu.:68.39
 Max.   :186.8   Max.   :80.02

try({summary(result)})

Call:
lm(formula = Y ~ X, data = df)

Residuals:
   Min     1Q Median     3Q    Max
-8.389 -2.431 -0.233  2.405 10.974

Coefficients:
              Estimate Std. Error t value Pr(>|t|)
(Intercept) -106.42882    6.72661  -15.82   <2e-16 ***
X              0.99004    0.03921   25.25   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.67 on 198 degrees of freedom
Multiple R-squared:  0.763,     Adjusted R-squared:  0.7618
F-statistic: 637.5 on 1 and 198 DF,  p-value: < 2.2e-16

Python スクリプト内で指定されている PypeR や Pylab などのライブラリが正しく読み込まれて実行することができました。

(参考)
https://gist.github.com/mia-0032/6378324

マルチスレッド&マルチコア&オンメモリで超ハイパフォーマンス NoSQL 型データベース、と言われている Aerospike をインストールしてみました:
2016081000


Aerospike は Docker や Vagrant のイメージが配布されていたり、AWS や GCE の仮想イメージが配布されていたりするので、これらを使うと「インストール」という作業を行わなくてもそのまま使えちゃいます(現にそのイメージを使う解説サイトも目立ちます)。が、今回はオンプレミスや普通の(?)仮想マシンでも使えるように、サーバーソフトの導入手順を紹介します。例によって CentOS 6.x が対象です。

まず Aerospike の動作には Python (2.6+)、Python ライブラリ、そして gcc が必要になります。これらの前提ライブラリをまとめてインストールしておきます:
# yum install gcc python python-devel

改めて、ここから Aerospike の環境を揃えていきます。今回は無料版であるコミュニティエディションのサーバーノード1つと、AMC(Aerospike Management Console 管理コンソール)1つを1台のマシンに導入する手順を紹介します。

まずサーバーノードを導入します。以下のサイトから対象の環境(今回は CentOS 6 なので、互換性のある RedHat 6)を選んで最新版をダウンロードします:
http://www.aerospike.com/download/server/

2016081003


2016/Aug/10 時点では最新バージョンは 3.9.0.3 でした。ダウンロードできるのは tgz ファイルなので、このアーカイブを展開し、インストーラー(asinstall)を実行してインストールします:
# cd /tmp
# tar xzvf ~/aerospike-server-community-3.9.0.3-el6.tgz
# cd aerospike-server-community-3.9.0.3-el6
# ./asinstall

インストールが成功したら以下のコマンドでサーバーとして起動し、またシステム再起動時に自動起動するよう設定しておきます:
# /etc/init.d/aerospike start
# chkconfig aerospike on

これでサーバーノードがこの環境にインストールできました。次にこのサーバーノードを管理するコンソール機能を導入します。

同様にして、以下のサイトから対象の環境を選んで AMC の最新版をダウンロードします。ちなみに AMC は RedHat 6(CentOS 6) 用のものしか用意されておらず、7.x のものは配布されていませんでした:

http://www.aerospike.com/download/amc/

2016081001


こちらの場合は rpm パッケージファイルがダウンロードできるので、RPM コマンドでそのまま導入します:
# rpm -ivh aerospike-amc-community-3.6.10.1-el6.x86_64.rpm

やはり同様に AMC を起動して、次回再起動時に自動起動するよう設定しておきます:
# /etc/init.d/amc start
# chkconfig amc on

これで導入作業は完了です。試しに一度管理コンソールを見てみましょう。ウェブブラウザを起動して、
 http://(導入先のマシン名):8081/
にアクセスしてみましょう:

2016081002


すると最初の1回は上記のような画面になり、AMC で管理するサーバーノードを指定するよう指示されます。今回は AMC と同じマシンにサーバーノードを導入しているので、同じアドレスか localhost を指定します。ポート番号は特に変更していない限り 3000 のままで大丈夫です:

成功すると AMC の管理画面が現れます:
2016081004


とりあえず1ノード環境ですがこれで動いちゃいました。Aerospike はノードを追加すればリニアにスケールするよう設計されているので、足りなくなったらサーバーノードを追加準備して、この AMC に追加すれば使えちゃいます。データの増減が大きいシステムに非常に向いていると言えます。




このページのトップヘ