PythonCGI – 基本

概要

PythonのコードでCGI(Common Gateway Interface)によってHTMLをする方法の基本。

基本の枠組み

コード例

例えば以下の内容をindex.pyとして準備。

  1. SheBangでPythonの場所を指定
  2. Content-Typeを出力して1行空ける
  3. 出力内容を記述

HTTPのヘッダーで最低限Content-Typeを出力し、ボディーとの間に空行が1行必要。これがないと、500エラーとなり、ログには以下の様に記録される。

コードのモード確認

ファイルがrootの実行権限を持っていること。実行権限がない場合はchmod 755chmod +xで設定する。

コードの配置

cgi実行用のディレクトリーにファイルを配置。Apacheの場合例えば/var/www/cgi-binなど。

ブラウザ表示

URLで[ホスト]/cgi-bin/index.pyを指定すると、ブラウザーにPython CGI Testと表示される。

HTML出力例

ASCII出力

基本の枠組みの出力に、以下の様にタグを加えると要素として表示される。

UTF-8出力

上のコードの一部を日本語にしてみる。

ところがこれをブラウザーで確認すると、h1要素は表示されるがp要素が全く表示されない。ソースを確認してもh1要素の行が表示されるだけ(このコードをコンソールで実行すると、h1要素の行もp要素の行も表示される)。

これを解決するには、ソースコードでio.TextIOWrapperを実行するとよいらしい。

上記の2行を追加すると、無事日本語まで表示された。

HTML出力

コンテンツを以下の様にHTMLの構造にして出力する。title要素も機能して、タブの表示が変更された。

ループの利用例

以下のコードでは、見栄えは良くないがforループでli要素を生成している。

出力に以下が加わる。

  1. 1番目のli要素
  2. 2番目のli要素
  3. 3番目のli要素
  4. 4番目のli要素
  5. 5番目のli要素

Apacheの設定

今回は/etc/httpd/conf/httpd.confのデフォルトのままでcgi-scriptの設定を加えていないが、そのままで適切に実行された。

 

2次元リストの結合

概要

2次元リスト同士の結合について整理。

  • 縦方向の結合
    • appendはループが必要
    • extendは破壊的に結合
    • +演算子は非破壊的に結合
  • 横方向の結合
    • extendでループが必要
    • +演算子はループの方法に注意

縦方向の結合

appendは要注意

1次元のリストの場合と同じく、appendは引数全体を要素として追加してしまう。

追加されるリストの各行を取り出して追加すれば可能。ただし破壊的。

extendはok(破壊的)

extendは引数のリストの内容をそのまま追加してくれる。ただし破壊的。

+演算子もok(非破壊的)

+演算子もextendと同じ挙動だが非破壊的。

横方向の結合

extendで一手間必要

extendで各要素に追加する場合、各要素をループで取出して追加する必要がある。

カウンターではなく要素を直接取り出してもok。

+演算子は要注意

+演算子で結合する場合は、結果のリストを準備しておく必要がある。このとき、結合するリストと同じ要素数(行数)の空のリストを要素に持つリストを準備しておく必要がある。

また、ループはカウンターを使って添字でリスト要素を指定しなければならない。

要素を直接取り出して演算した場合、変数が新たに作成されるがその結果は元のリストには反映されないので注意。

 

リストの追加・結合

概要

リストに対する要素の追加、リストの結合について整理。

  • リストへの1つの要素の追加
    • appendが簡明、ただし破壊的
    • extendも使えるがリストとして追加、破壊的
    • +演算子もリストとして追加、非破壊的
  • リスト同士の結合
    • appendは使えない
    • extendは破壊的にリストを追加
    • +演算子は非破壊的にリストを追加

リストへの要素の追加

appendは破壊的

appendで1つの要素をリストに追加できる。appendは破壊的で元のリストを改変する。戻り値はNone

appendは1つの引数しか持てない(append(4, 5)とはできない)。

extendはリストを追加(破壊的)

extendでも要素を追加できるが、要素そのものではなく、追加したい要素を含むリストとして追加する。extendは破壊的で元のリストを改変する。戻り値はNone

+演算子もリストの演算(非破壊的)

+演算子もextendと同じくリストを追加する。extendと違って非破壊的で、元のリストは改変されず、結果は戻り値で返される。

リストの結合

appendは使えない

appendは引数をリストの要素として追加するので、リストを引数に与えるとそのリストが要素として追加されてしまう。

extendはOk(破壊的)

extendは引数に与えたリストで元のリストを拡張する。ただし破壊的で元のリストが改変される。

+演算子もOk(非破壊的)

+演算子もextendと同じくリストを拡張するが非破壊的。拡張と言うよりも、くっつける/追加するというイメージ。

 

JS/ES – Promise

非同期実行の簡単な例

以下の例では2つのブロックが非同期に実行され、コードが書かれた順番ではなく実行時間の短い順に出力される。

Promiseによる実行順序の保証

基本形

Promiseオブジェクト.then()

この基本形では、以下の手順で処理間を同期させる。

  1. Promiseオブジェクト生成
    • 生成時の引数で渡す無名関数に先行処理を記述
    • 無名関数の引数で処理終了を発行する関数(resolve)を受け取り
    • 先行処理終了のところでresolve関数を実行
  2. Promiseオブジェクトのthenメソッドを記述
    • thenメソッドの引数で渡す無名処理に後続処理を記述
    • この後続処理は、先行処理のresolve関数が実行された後に実行される

処理完了のための関数名は任意だが、staticメソッドと同じ関数名のresolveが使われる。

以下の例では、Promise・・・・・・・・・・・・・無名関数の引数をresolveとして受け取り、resolve関数を300ms待機後の表示の後に実行している。

そしてその実行後に100ms待機の処理が実行される。

Promiseオブジェクトのthenメソッドには引数に無名関数を指定する。Promiseオブジェクトで設定したresolveメソッドが実行された後に引数の無名関数が実行される。

以下の例では、上のPromiseオブジェクトで設定した300ms待機後の表示の後に、thenメソッドで設定した100ms待機・表示が実行される。

new Promise~then

生成したPromiseオブジェクトから直接thenメソッドを実行してもよい。

Promiseオブジェクトを返す

優先実行させたい処理をPromiseオブジェクトとして、そのインスタンスを関数の戻り値とする書き方。関数にthenメソッドが適用できる。

resolveで値を渡す

resolve関数に引数を渡し、これをthenメソッドで受け取ることができる。

thenのチェイン

thenメソッドを連ねてシリアルに実行させる書き方。後続を持つthenメソッドの戻り値をPromiseオブジェクトとするのがミソ。

reject

Promiseの処理で何らかの問題が生じた場合にrejectすることができる。この場合、resolveが実行されないのでthenは処理されず、直近のcatchが呼ばれる。

以下の例ではPromiseオブジェクトで300msのブロックの最後でrejectが実行され、thenメソッドは実行されずにcatchメソッドが実行される。

 

Promise.all~全ての実行を待機

 

MapLibre – GeoJSONのソースを描画する

基本形

  • Mapオブジェクトのonメソッドで、マップのロード後処理として記述する
  • MapオブジェクトのaddSourceメソッドで図形を定義し、addLayerメソッドでレンダリング方法などを記述する
  • addLayerでソースIDを指定する

addSource

  • 第1引数でソースIDを設定し、第2引数に図形のGeoJSONを記述する

addLayer

詳細はMapLibreのLayerのドキュメントを参照。

  • id:一意なレイヤーID
  • type:形状タイプ
    • 'fill', 'line', 'symbol', 'circle', 'heatmap', 'fill-extrusion', 'raster', 'hillshade', 'background'
    • 形状タイプごとの描画仕様は、その内容によってlayoutpaintで指定する
  • source:このレイヤーに描かれるソースのIDを指定
  • layout:形状タイプごとの描画スタイルを設定
    • たとえば'line'の場合、'line-width', 'line-cap', 'line-join'など
  • paint:図形を描く色などを指定
    • たとえば'circle'の場合、'circle-color', 'circle-radius'など

図形の描画例

 

Python3 – WebAPI

概要

PythonでWebAPIによってデータをリクエストして取得する方法をまとめる。ここでは郵便番号配信サービスのzipcloudのAPIを利用する。

流れは以下の通り。

  1. 準備
    1. urllib.requestライブラリーのインポート
    2. APIエンドポイントURLのセット
    3. パラメーターのセット
    4. パラメーターのエンコード
  2. リクエストとレスポンス
    1. Requestオブジェクトの生成
    2. リクエスト実行とResponseオブジェクトの取得
  3. レスポンスオブジェクトのデコード

API仕様

zipcloudサイトの郵便番号検索APIの内容をまとめると以下の通り。

  • リクエストURL:https://zipcloud.ibsnet.co.jp/api/search
  • リクエストパラメーター
    • zipcode~必須~郵便番号(ハイフンなし7桁の数字)
    • callback~オプション~JSONPとして出力する際のコールバック関数名
    • limit~オプション~同一の郵便番号で複数のデータが存在する場合に返される上限件数(デフォルト20)
    • urlパターンの例:https://zipcloud.ibsnet.co.jp/api/search?zipcode=1330051

コード

最初に実行コードの全体を示す。

コード説明

準備

urllib.requestライブラリーのインポート

HTTPリクエストに必要なurllib.requestライブラリーをインポートする。

APIエンドポイントURLのセット

ここではzipcloudのAPIエンドポイントのURLを変数urlにセットする。

リクエストパラメーターのセット

リクエストパラメーターを辞書でセット。ここでは仕様に従ってzipcode'1130051'を与えている。

パラメーターのエンコード

リクエストに備えて、パラメーター文字列をバイト列にエンコード。ここでは一般的なutf-8を指定しているが、郵便番号は半角数字なのでasciishift_jisでも同じ結果となる。その形式は

urllib.parse.urlencode(パラメーター辞書).encode('文字コード')

リクエストとレスポンス

Requestオブジェクトの生成

ベースのURLとエンコードされたパラメーターを与えてRequestオブジェクトを生成する。

urllib.request.Request(URL文字列, data=エンコード後のパラメーター)

リクエスト実行とResponseオブジェクト取得

リクエストはRequestオブジェクトを引数としてurlopenメソッドを実行。戻り値は結果のResponseオブジェクト。

Responseオブジェクトのデコード

取得したままのResponseオブジェクトをそのまま表示しても、クラスの文字列表現となるだけ。

そこで、文字コードを指定してデコードしてやる。ここではutf-8を指定している。

response.read().decode(文字コード)

結果は文字コードで指定されたエンコード方式でデコードされ、レスポンスの文字列が得られる。

zipcloudのレスポンスはJSON形式でインデント整形された文字列となるが、あくまで文字列であり、要素を指定した値の取出しはできない。

JSON文字列の取り扱い

準備

JSON形式の文字列を辞書として読み込んだり、整形表示をしてみる(PythonでのJSONの取り扱い)。

まず、JSONを扱うにはjsonライブラリーが必要。

辞書への展開

JSON形式の文字列をPythonの辞書として取り込む。

json.loads(JSON形式の文字列)

レスポンス内容の取出し

これはzipcloudの仕様になるが、レスポンスのJSONデータのレスポンス部分は入れ子の内側('response'キーに対応)にあるので、これを取り出す。

同じ郵便番号に複数の住所が存在する場合もあるが、ここでは最初の1つだけ取り出している。

JSON形式での整形出力

辞書の内容を再度整形して文字列化する。

json.dumps(辞書, indent=桁数)

ただし、このままではマルチバイト文字が16進にエスケープされてしまう。

エスケープの抑制

json.dumpsでエスケープを抑制することで、もとの文字コードに基づいて処理される。エスケープを抑制するためには引数でensure_ascii=Falseをセットする。

 

Python3 – cURL

基本形~GET

Python3で指定したURLのサイトからレスポンスを得る手順は以下のとおり。

  1. urllib.request.urlopenで接続のオブジェクトを得る
  2. 接続オブジェクトに対してreadメソッドを実行する

ただし結果はバイト列で得られる。

urlopenの引数にURLを指定

以下はurlopenの引数にURLを指定した手順。

  1. urlopenの戻り値(Responseオブジェクト)を変数responseに保存
  2. responsereadメソッドメソッドでレスポンスの内容を取得

レスポンスの内容はバイト列で返される。

文字列でレスポンスを得るには、decodeメソッドでバイト列を文字列にデコードする。

urlopenの引数にRequestオブジェクトを指定

まずURLを指定してurllib.request.Requestオブジェクトを生成し、これをurlopenの引数として接続を開く。

POST

ポストの流れは以下のとおり。

  • urllib.requesturllib.parseをインポートする
  • urlopenの引数にRequestオブジェクトを渡す
  • Requestオブジェクト生成時にdata引数を指定するとPOSTメソッドになる(method='POST'がなくてもよい)
  • dataへ渡す引数は、以下のように処理
    • 元のパラメータ群を辞書で準備
    • そのデータをurllib.parse.urlencodeでエンコード(utf-8のエンコードも加える)

以下はhttpbin.orgを利用して確認した例。httpbin.org/postからのレスポンスがJSON形式なので、jsonライブラリーをインポートしてレスポンスを整形している。

 

Python3 – json

概要

JSONの文字列が与えられたときの扱い。

  1. json.loadsで文字列をPythonの変数に変換(デコード)
  2. json.dumpsでPythonの変数を文字列に変換(エンコード)
    • ensure_ascii=Falseでユニコード文字をエスケープさせない
    • indent=nでJSONの構造に即した改行・インデントを適用

json.loads

json.loadsメソッドは、JSON文字列をPythonの変数にデコードする。Pythonの辞書になっていることがわかる。

JSONのキーと値が辞書のキーと値に対応している。

json.dumps

そのままエンコードした場合

json.dumpsは、辞書に変換されたJSONの内容を文字列にエンコードする。

ただしこのとき、ユニコードはエスケープされ、改行を含まない文字列となる。

引数設定によるJSONの構造表現

以下の2つの引数を指定する。

ensure_ascii=False
ユニコードをエスケープせずそのままの表現とする。
indent=n
JSONの構造に即した改行・インデントを適用する。

これによって、ユニコード文字列はエスケープされず、JSONの構造に即した改行・インデントが適用された文字列が得られる。

 

 

PHP – json

概要

JSONの文字列が与えられたときの扱い。

  1. json_decodeで文字列をPHPの変数に変換
  2. json_encodeでPHPの変数を文字列に変換
    • このとき、JSON_UNESCAPED_UNICODEJSON_PRETTY_PRINTのフラグを立てる

json_decode

json_decode関数は、引数で与えられたJSON文字列をPHPの変数に変換する。

JSONのキーや単体の要素はstringに、{ ... }stdClassのオブジェクトに変換され、キー=>値がオブジェクトのプロパティーとなっている。

json_encode

そのままエンコードした場合

json_encodeは、PHPの変数で表現されたJSONコードを文字列に変換する。

ただしこのとき、ユニコードはエスケープされ、改行を含まない文字列となる。

オプションフラグの設定

以下の2つのフラグをセットする。

JSON_UNESCAPED_UNICODE
ユニコード文字をエスケープせずに扱う。
JSON_PRETTY_PRINT
JSONの構造に即した改行・インデントを適用する。

これによって、ユニコード文字列はエスケープされず、JSONの構造に即した改行・インデントが適用された文字列が得られる。

 

Python3 – bytesとString

概要

Pythonでの文字列表現をバイト列に変換するにはencodeメソッドを用い、バイト列を文字列に変換するにはdecodeメソッドを用いる。encode/decodeの引数には文字コードを指定する。

バイト列をPythonのprint文で出力すると、b'文字列'で表現され、改行文字などはエスケープコード(\nなど)で表示される。

ASCII文字列

encode/decode

ASCII文字列はencode/decodeの引数を'ascii'とする。

ASCIIの場合は引数が'utf-8''shift_jis'としても結果は同じ。

16進表現

bytes.hexメソッドの引数にバイト列を与えると、その16進表現の文字列が得られる。

UTF-8/Shift_JIS

encode/decode

マルチバイト文字の場合、encodedecodeで文字コードを整合させる。

encodedecodeで文字コードが違うと、文字化けするのではなくエラーになる。

ただしerrors引数の設定をデフォルトの'strict’から変更すると、文字化けした文字列などが返される。

16進表現

マルチバイト文字の16進表現はバイト列の表現のとおりになる。