[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を部品に切り分ける。

```py

>>> 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を使った方が便利。

```py

>>> 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による最も単純なウェブサーバー

```py

$ 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ページに流し込む。

```py:

#ポート番号は他の値も指定できる。
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ファイルのみで作られているので非常に試しやすく、デプロイも簡単。


```py: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

```

```py: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)

```

```py:実行文

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

```

```py:実行結果

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

```

```py: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)

```

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

```

```py:実行結果

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

```

```py: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)

```

```py:実行文

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

```

```py:実行結果

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

```

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

##9.2.5 Flask

- Bottleよりも気が利いている。

```py: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はどこで問題た起きたか詳細情報を特別な書式で表したページを返してくる。

```py: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)

```

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


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

```

- Flaskにはjinja2をいうテンプレがある。

```py: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>>

```

```py: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)

```

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

< Say hello to my little friend:Gamera >

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

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

```py: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)

```

```py:実行結果

#http://127.0.0.1:9999/echo/Rodan/McKeesportにアクセス。

Say hello to my little friend: Rodan. Alas, in just destroyed McKeesport!
```

```py: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)

```

```py:実行結果

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

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

```


```py: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を使って起動すること。

```py:flask9-1.py

from flask import Flask

app=Flask(__name__)

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

```

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

```py: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テンプレートファイルを作ろう。

```py: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引数を渡すこと。

```py: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』(オライリージャパン発行)」