Help us understand the problem. What is going on with this article?

[Python3 入門 20日目]9章 ウェブを解きほぐす(9.1〜9.4)

9.1 ウェブクライアント

  • ウェブはクライアントサーバーシステムになっている。クライアント→サーバーに要求。サーバー→クライアントには、TCP/IP接続を開設し、HTTPを介してURLその他の情報を送り、応答を受け取る。

    • 応答にはステータス、応答データと形式がある。
    • 最もよく知られたウェブクライアントはウェブブラウザである。様々な方法でHTTP要求を送ることができる。
  • 個々のHTTP接続は他の接続に依存せず、独立している。(ステートレス)→HTTPの代わりに状態を管理する方法としてクッキーが使われる。

    • サーバーがクライアント固有情報をクッキーに収めてクライアントに送る。→クライアントはサーバーにクッキーを送り返すと、サーバーはクッキーからクライアントを一意に識別する。

9.1.1 Pythonの標準ウェブライブラリ

Python2ではウェブクライアントとサーバーモジュールはあまり纏まっていなかった。→Python3ではパッケージにまとめた。

  • httpはクライアントサーバーHTTPの詳細を管理する。

    • clientはクライアントサイドの処理を行う。
    • serverはPythonによるウェブサーバー開発を助ける。
    • cookiesとcookiejarは複数のサイトアクセスまたがって必要な情報を保存するクッキーを管理する。
  • urllibはhttpの上で実行される。

    • requestはクライアントの要求を処理する。
    • responseはサーバーの応答を処理する。
    • parseはURLを部品に切り分ける。
>>> import urllib.request as ur
>>> url="https://raw.githubusercontent.com/koki0702/introducing-python/master/dummy_api/fortune_cookie_random1.txt"
>>> conn=ur.urlopen(url)
>>> print(conn)
<http.client.HTTPResponse object at 0x10733c8d0>
>>> data=conn.read()
>>> print(data)
b'You will be surprised by a loud noise.\\r\\n\\n[codehappy] http://iheartquotes.com/fortune/show/20447\n'
#HTTPステータスコードの表示。
#200は全てのコードがうまくいったことを示す。
>>> print(conn.status)
200

#データ形式表示
#HTTP応答ヘッダーのContent-Typeで指定されている。
>>> print(conn.getheader("Content-Type"))
text/plain; charset=utf-8
>>> for key,value in conn.getheaders():
...     print(key,value)
... 
Content-Type text/plain; charset=utf-8
Content-Security-Policy default-src 'none'; style-src 'unsafe-inline'; sandbox
Strict-Transport-Security max-age=31536000
X-Content-Type-Options nosniff
X-Frame-Options deny
X-XSS-Protection 1; mode=block
ETag W/"9f5651c47de1d25d4c531d22c98b96ea61d10d7e4f5c6eb6cbeecd9e01cdfbf8"
Cache-Control max-age=300
X-Geo-Block-List 
Via 1.1 varnish-v4
X-GitHub-Request-Id D326:4317:329188:36E387:5E2EBEE7
Content-Length 99
Accept-Ranges bytes
Date Mon, 27 Jan 2020 10:43:52 GMT
Via 1.1 varnish
Connection close
X-Served-By cache-tyo19932-TYO
X-Cache MISS
X-Cache-Hits 0
X-Timer S1580121832.776951,VS0,VE269
Vary Authorization,Accept-Encoding, Accept-Encoding
Access-Control-Allow-Origin *
X-Fastly-Request-ID 034e4e0799a3de2ed0ae382d63bcd716a0574002
Expires Mon, 27 Jan 2020 10:48:52 GMT
Source-Age 0
>>> 

9.1.2 標準ライブラリを超えて

  • サードパーティモジュールのrequestsを使った方が便利。
>>> import requests
>>> url="https://raw.githubusercontent.com/koki0702/introducing-python/master/dummy_api/fortune_cookie_random1.txt"
>>> resp=requests.get(url)
>>> resp
<Response [200]>
>>> print(resp.text)
I know that there are people who do not love their fellow man, and I people like that!
    -- Tom Lehrer, Satirist and Professor
    [codehappy] http://iheartquotes.com/fortune/show/21465


9.2 ウェブサーバー

9.2.1 Pythonによる最も単純なウェブサーバー

$ python -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
127.0.0.1 - - [27/Jan/2020 20:09:04] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [27/Jan/2020 20:09:04] code 404, message File not found
127.0.0.1 - - [27/Jan/2020 20:09:04] "GET /favicon.ico HTTP/1.1" 404 -
127.0.0.1 - - [27/Jan/2020 20:12:55] "GET /wheel HTTP/1.1" 200 -
q
C
^C      

python -m http.server 9999
Serving HTTP on 0.0.0.0 port 9999 (http://0.0.0.0:9999/) ...
^C


9.2 ウェブサーバー

  • ウェブフレームワークは、ウェブサイトを作るための機能を提供するものなので、単純なウェブサーバー以上のことをする。主な機能を以下に示す。
    • ルーティング:URLを解析してサーバー関数を呼び出す。
    • テンプレート:サーバーサイドのデータをHTMLページに流し込む。
#ポート番号は他の値も指定できる。
python -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
#127.0.0.1 :クライアントのアドレス
#一つ目の - :リモートユーザー名
#二つ目の - :ログインユーザー名
#"GET / HTTP/1.1":ウェブサーバーに送られたコマンド
#GETはHTTPメソッド、/は要求されたリソース(ルートディレクトリ)、HTTP/1.1はHTTPのバージョン。
#200:HTTPステータスコード
127.0.0.1 - - [27/Jan/2020 20:09:04] "GET / HTTP/1.1" 200 -

9.2.2 WSGI

Pythoウェブアプリケーションとウェブサーバーの間の普遍的なAPIであるWSGIが定義されてから飛躍的に前進した。

9.2.3 フレームワーク

ウェブフレームワークは以下の機能の一部または全部を提供する。

  • ルーティング
  • テンプレート
  • 認証と権限付与:ユーザー名、パスワード、パーミッションを処理する。
  • セッション:ユーザーが来ている間に、一時的なデータストレージを維持管理する。

9.2.4 Bottle

  • BottleはPythonファイルのみで作られているので非常に試しやすく、デプロイも簡単。
bottleインストール
pip install bottle
Collecting bottle
  Downloading bottle-0.12.18-py3-none-any.whl (89 kB)
     |████████████████████████████████| 89 kB 692 kB/s 
Installing collected packages: bottle
Successfully installed bottle-0.12.18

bottle1.py
from bottle import route, run

#URLと直後の関数を対応付けるために@routeデコレータを使う。
#この場合、/(ホームページ)をhome関数が処理する。
@route("/")
def home():
    return "It is not fancy, but it is my home page"

#run()関数はbottleに組み込まれているPythonによるテストウェブサーバーを実行する。
run(host="localhost",port=9999)

実行文
python3 bottle1.py 
Bottle v0.12.18 server starting up (using WSGIRefServer())...
Listening on http://localhost:9999/
Hit Ctrl-C to quit.

実行結果
#http://localhost:9999にアクセス。
It is not fancy, but it is my home page

bottle2.py
#ホームページかが要求された時にbottleがこのファイルの内容を返すようにする。
from bottle import route, run, static_file

@route("/")
#rootが示す"."はカレントディレクトリ。
def home():
    return static_file("index.html",root=".")

run(host="localhost",port=9999)

実行文
python bottle2.py
Bottle v0.12.18 server starting up (using WSGIRefServer())...
Listening on http://localhost:9999/
Hit Ctrl-C to quit.

実行結果
#http://localhost:9999/にアクセス。
My new and improved home page!!!

bottle3.py
from bottle import route, run, static_file

@route("/")
def home():
    return static_file("index.html",root=".")

#URLを介して関数に引数を渡す。
#echo()の引数thingにはURLの中の/echo/の後ろの文字列引数を代入する。
@route("/echo/<thing>")
def echo(thing):
    return "Say hello to my little friend: %s" % thing

# debug=True
# reloader=True
run(host="localhost",port=9999)

実行文
python bottle3.py
Bottle v0.12.18 server starting up (using WSGIRefServer())...
Listening on http://localhost:9999/
Hit Ctrl-C to quit.

127.0.0.1 - - [27/Jan/2020 20:59:21] "GET /echo/Mothra HTTP/1.1" 200 37

実行結果
#http://localhost:9999/echo/Mothraにアクセス。
Say hello to my little friend: Mothra

  • debug=Trueを指定すると、HTTPエラーが返された時にデバッグページが作られる。
  • reloader=Trueを指定すると、Pythonコードに変更を加えた時にブラウザにページが再ロードされる。

9.2.5 Flask

  • Bottleよりも気が利いている。
Flaskインストール
pip install flask
...
Successfully installed Jinja2-2.10.3 MarkupSafe-1.1.1 Werkzeug-0.16.1 click-7.0 flask-1.1.1 itsdangerous-1.1.0

  • Flaskの静的ファイルのデフォルトホームディレクトリはstaticであり、そのディレクトリのファイルに対するURLも/staticで始まる。そこで、ホームディレクトリを"."にして、/というURLがindex.htmlファイルにマッピングされるようにしている。
  • run()関数の中でdebug=Trueを設定すると、自動再ロードが有効になる。bottleはデバッグと再ロードで別々の引数を使っている。また、例外発生時に、Flaskはどこで問題た起きたか詳細情報を特別な書式で表したページを返してくる。
flask1.py
from flask import Flask

app=Flask(__name__, static_folder=".",static_url_path="")

@app.route("/")
def home():
    return app.send_static_file("index.html")

@app.route("/echo/<thing>")
def echo(thing):
    return thing

# reloader=True
app.run(port=9999, debug=True)

実行結果
#http://127.0.0.1:9999/にアクセス。
My new and improved home page!!!


#http://127.0.0.1:9999/echo/Godzillaをブラウザに入力。
Godzilla

  • Flaskにはjinja2をいうテンプレがある。
flask2.html
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>Flask2 Example</title>
  </head>
  <body>
    Say hello to my little friend:{{ thing }}
  </body>
</html>>

flask2.py
from flask import Flask, render_template

app=Flask(__name__)

#thing=thingはURLから読み込んだ<thing>をthingという変数に代入し、これを"flask2.html"に渡している。
@app.route("/echo/<thing>")
def echo(thing):
    return render_template("flask2.html",thing=thing)


app.run(port=9999,debug=True)

実行結果
#http://127.0.0.1:9999/echo/Gameraをブラウザに入力

< Say hello to my little friend:Gamera >

9.2.5.1 URLパスの一部という形での引数渡し

flask3.html
<html>
<head>
<title>Flask3 Example</title>
</head>
<body>
Say hello to my little friend: {{ thing }}.
Alas, in just destroyed {{ place }}!
</body>
</html>
flask3a.py
from flask import Flask, render_template

app=Flask(__name__)

@app.route("/echo/<thing>/<place>")
def echo(thing, place):
    return render_template("flask3.html",thing=thing, place=place)


app.run(port=9999, debug=True)

実行結果
#http://127.0.0.1:9999/echo/Rodan/McKeesportにアクセス。

Say hello to my little friend: Rodan. Alas, in just destroyed McKeesport!
flask3b.py
from flask import Flask, render_template, request

app=Flask(__name__)

#GET引数として渡す。
@app.route("/echo/")
def echo():
    thing=request.args.get("thing")
    place=request.args.get("place")
    return render_template("flask3.html",thing=thing, place=place)

app.run(port=9999, debug=True)

実行結果
#http://127.0.0.1:9999/echo/?thing=Gorgo&place=Wilmerdingにアクセス。

Say hello to my little friend: Gorgo. Alas, in just destroyed Wilmerding!

flask3c.py
from flask import Flask, render_template, request

app=Flask(__name__)

@app.route("/echo/")
#辞書の**を使えばキーを辞書化してfalask3.htmlへ渡すことができる。
def echo():
    kwargs={}
    kwargs["thing"]=request.args.get("thing")
    kwargs["place"]=request.args.get("place")
    return render_template("flask3.html",**kwargs)

app.run(port=9999, debug=True)

9.2.6 Python以外のウェブサーバー

  • 本番システムでのウェブサーバーは以下の二つ。
    • mod_wsgiモジュール付きのApache
    • uWSGIアプリケーションサーバー付きのnginx

9.2.7 その他のフレームワーク

たくさんありますね。

9.3 ウェブサービスとオートメーション

ウェブはHTML形式以外の様々な形式でアプリケーションとデータを結びつける強力な手段として使われる。

9.3.1 Web APIとREST

  • Web APIでウェブページのデータを提供できる
  • RESTfulサービスはHTTPの動詞を決まった方法で使う。
    • HEAD:リソースについての情報を取得する。
    • GET:サーバーからリソースのデータを取得する。
    • POST:サーバーのデータを更新する。
    • PUT:この動詞は新しいリソースを作る。
    • DELETE:削除

9.3.2 JSON

  • ウェブクライアントとサーバーのデータ交換に適している。

9.3.3 クロールとスクレイピング

いつかの勉強用のために。
「Scrapyチュートリアル」
https://doc-ja-scrapy.readthedocs.io/ja/latest/intro/tutorial.html

9.3.5 BeautifulSoupによるHTMLのスクレイピング

  • ウェブサイトから既にHTMLとデータが取り出してある場合、BeautifulSoupが役に立つ。

9.4 復習課題

9-1 Flaskのデバッグ、サイロードできる開発用ウェブサーバーを使って骨組みだけのウェブサイトを作ろう。サーバーはホスト名localhost、ポート5000を使って起動すること。

flask9-1.py
from flask import Flask

app=Flask(__name__)

app.run(port=5000, debug=True)

9-2 ホームページに対する要求処理するhome()関数を追加しよう。 It is aliveという文字列を返すようにセットアップせよ。

flask9-2.py
from flask import Flask

app=Flask(__name__)

@app.route("/")
def home():
    return "It is alive"

app.run(port=5000, debug=True)

9-3 以下のような内容でhome.htmlをという名前のjinja2テンプレートファイルを作ろう。

home.html
<html>
<head>
  <title>It is alive!</title>
<body>
  I am of course referring to {{thing}},which is {{height}} feet tall and {{color}}.
</body>
</html>

9-4 home.htmlテンプレートを使うようにサーバーのhome()関数を書き換えよう。

home()関数には、thing, height,colorの3個のGET引数を渡すこと。

flask9-4.py
from flask import Flask, render_template, request

app=Flask(__name__)

@app.route("/")
def home():
    thing=request.args.get("thing")
    height=request.args.get("height")
    color=request.args.get("color")
    return render_template("home.html",thing=thing, height=height, color=color)

app.run(port=5000, debug=True)

感想

Web系をさらっと復習しました。
ここら辺は使うとしてももうちょい先になりそうだ。

参考文献

「Bill Lubanovic著 『入門 Python3』(オライリージャパン発行)」

Taka20200105
大学でC言語を勉強しましたが、仕事は全く関係ない物流関係のことやってます。キャリアチェンジしたく、日々精進しております。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away