6
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Python初心者が最初に作ったWebアプリ

Last updated at Posted at 2020-04-03

Pythonに全く触れたことのない初心者が、とても簡単なWebアプリを作成した時の備忘録です。
Qiita初投稿です。いつもお世話になっております。

■アプリ概要
・ユーザーからの入力を受け付けるWebページを表示
・入力された書籍タイトルを元に、書籍APIの検索リクエスト(Google Books API)
・レスポンスから書籍サムネイルを表示

作成工程、その中でハマった所、解決方法を記載していきます。

—環境—
Mac OS

1.Pythonのインストール

Macはデフォルトで2.x系のPythonがインストールされています。
下記コマンドでインストールされているPythonのバージョンを確認。

Terminal
$ python --version
Python 2.7.16

※ python -V でも同じ結果が得られます

Python 2.x系は2020年1月1日にサポート終了しています。
3.x系とのversionの違いで結構不整合が起きる + 今時2.x系を使用していると小学生にも鼻で笑われるらしいので、、、
最初に3.x系にアップデートします。

↓公式サイトから3.x系のパッケージをダウンロード
https://www.python.org/downloads/

Q1.ハマりポイント
インストールしたのにバージョンが2.x系のまま
python3 --version と確認すると3.x系になっている

Terminal
$ python --version
Python 2.7.16
$ python3 --version
Python 3.7.7

A1.解決方法
1.brewコマンドでpyenvをインストール(読み方が分からず、心の中でぴえんって読んでる)

Terminal
$ brew install pyenv

2.viエディタ等を使い~/.bash_profileに下記4行を追記
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
export PATH="$HOME/.pyenv/shims:$PATH"

※viエディタはTerminalからファイルを編集できるコマンドです。
使い方が結構特殊ですが、Linuxのサーバー構築などでもよく使います。
https://prev.net-newbie.com/linux/commands/vi.html

Terminal
$ vi ~/.bash_profile
# ~/.bash_profileを編集する
$ source ~/.bash_profile

3.pyenvを使いMac全体にpython3.x系を認識させる。

Terminal
$ pyenv global 3.7.0
$ pyenv rehash

これで3.x系が使えるようになりました。

Terminal
$ python --version
Python 3.7.7

2.フォルダ構成

プロジェクトのルートフォルダ直下に index.html と server.py と cgi-bin フォルダを配置。
cgi-bin フォルダ内に index.py を配置します。

project/
  ┝ ─ index.html
  ┝ ─ server.py
  └ ─ cgi-bin/
         └ ─ index.py

3.index.htmlの作成

今回はレイアウトも何も考えず、ただ文字列の入力を受け付けるだけのhtmlを作成します。

index.html
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <form method="POST" action="cgi-bin/index.py">
      <label>取得図書画像タイトル:</label>
      <br>
      <input type="text" name="text">
      <button type="submit">送信</button>
    </form>
  </body>
</html>

ザックリ説明すると、
<form>タグで囲まれている箇所に入力されたデータが、
<submit>契機で、cgi-bin/index.py にPOSTメソッドで送信される。
そんな感じです。

下記のようなページが作成されます。
スクリーンショット 2020-04-03 21.20.59.png

テキストボックスに入力されたデータが、[送信]ボタンを押すと、index.py に送信されます。

4.server.pyの作成

ローカルで検証用のサーバーを立てるために必要になります。

server.py
import http.server
http.server.test(HandlerClass=http.server.CGIHTTPRequestHandler)

Terminalで作成したPythonファイルを起動すると、ローカルサーバーが立ち上がります。
ローカルサーバーが立ち上がった状態で
http://0.0.0.0:8000/
にアクセスすると、先ほどのindex.htmlが表示されるはずです。

Terminal
$ python server.py
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

※ちなみにローカルサーバーを中止するのは command + C です。

5.index.pyの作成

index.htmlから送信されたデータを使い、Google書籍検索APIのリクエストを送ります。

index.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import cgi # CGIモジュールのインポート
import cgitb
import sys
import requests
import json

# 書籍検索APIの雛形
api = "https://www.googleapis.com/books/v1/volumes?q={title}&maxResults=10&startIndex=0"

# デバッグに使うので、本番環境では記述しない
cgitb.enable()

# ユーザーが入力したフォームデータを取得する
form = cgi.FieldStorage()

# HTMLを記述するためのヘッダ
print("Content-Type: text/html; charset=UTF-8") 
print("")

# フォームのデータが入力されていない場合
if "text" not in form:
    print("<h1>Error!</h1>")
    print("<br>")
    print("テキストを入力してください!")
    print("<a href='/'><button type='submit'>戻る</button></a>")
    sys.exit() # index.pyの終了

text = form.getvalue("text") # テキストデータの値を取得する
url = api.format(title=text) # 書籍検索apiの検索単語となる{title}に入力テキストを当てはめる
response = requests.get(url) # リクエストを投げる
data = json.loads(response.text) # レスポンスをjson形式に変換

# レスポンスをhtmlに反映

print(data['items'][0]['volumeInfo']['title'])
print("<br>")
print("<img src=" + data['items'][0]['volumeInfo']['imageLinks']['thumbnail'] + ">")
print("<br>")
print("<a href='/'><button type='submit'>戻る</button></a>")

コメントでだいぶ補足してありますが、ザックリ説明します。
↓こちらがGoogle Books API のURLです。
https://www.googleapis.com/books/v1/volumes?q=いちご100&maxResults=10&startIndex=0
**q=**の後に検索したい書籍の名前を入れると、関連したレスポンスを返してくれます。

curlを使っても手軽に確認できます。

Terminal
$ curl https://www.googleapis.com/books/v1/volumes?q=いちご100&maxResults=10&startIndex=0

"kind": "books#volumes",
 "totalItems": 2836,
 "items": [
  {
   "kind": "books#volume",
   "id": "vWSUDwAAQBAJ",
   "etag": "b/w9qaxsyy4",
   "selfLink": "https://www.googleapis.com/books/v1/volumes/vWSUDwAAQBAJ",
   "volumeInfo": {
    "title": "いちご100% モノクロ版【期間限定無料】 2",
    "authors": [
     "河下水希"
    ],
    "publisher": "集英社",
    "publishedDate": "2002-10-04",
    "description": "【春マン!! 期間限定無料!!/真中淳平が突如迷い込んだ恋の迷路! いちごパンツが導く超青春ラブコメディ!】※2019年5月8日までの期間限定無料お試し版です。2019年5月9日以降はご利用できなくなります。 西野と東城、二人の間で揺れる真中の気持ち。ホントに好きなのは西野? それとも東城? 勉強に全く身が入らないまま迎えた受験当日、いちご模様迷宮の入り口・幻の美少女が目の前に…!! どうする真中!?",
    "industryIdentifiers": [
     {
      "type": "OTHER",
      "identifier": "PKEY:088733268733043155P5"
     }
    ],
    "readingModes": {
     "text": true,
     "image": false
    },
    "pageCount": 188,
    "printType": "BOOK",
    "categories": [
     "Comics & Graphic Novels"
    ],
    "maturityRating": "NOT_MATURE",
    "allowAnonLogging": false,
    "contentVersion": "1.2.2.0.preview.2",
    "panelizationSummary": {
     "containsEpubBubbles": true,
     "containsImageBubbles": true,
     "epubBubbleVersion": "99b0fa95624a43e9_A",
     "imageBubbleVersion": "99b0fa95624a43e9_A"
    },
    "imageLinks": {
     "smallThumbnail": "http://books.google.com/books/content?id=vWSUDwAAQBAJ&printsec=frontcover&img=1&zoom=5&source=gbs_api",
     "thumbnail": "http://books.google.com/books/content?id=vWSUDwAAQBAJ&printsec=frontcover&img=1&zoom=1&source=gbs_api"
    },
    "language": "ja",
    "previewLink": "http://books.google.co.jp/books?id=vWSUDwAAQBAJ&dq=%E3%81%84%E3%81%A1%E3%81%94100&hl=&cd=1&source=gbs_api",
    "infoLink": "http://books.google.co.jp/books?id=vWSUDwAAQBAJ&dq=%E3%81%84%E3%81%A1%E3%81%94100&hl=&source=gbs_api",
    "canonicalVolumeLink": "https://books.google.com/books/about/%E3%81%84%E3%81%A1%E3%81%94100_%E3%83%A2%E3%83%8E%E3%82%AF%E3%83%AD%E7%89%88_%E6%9C%9F%E9%96%93%E9%99%90.html?hl=&id=vWSUDwAAQBAJ"
   },
   "saleInfo": {
    "country": "JP",
    "saleability": "NOT_FOR_SALE",
    "isEbook": false
   },
   "accessInfo": {
    "country": "JP",
    "viewability": "NO_PAGES",
    "embeddable": false,
    "publicDomain": false,
    "textToSpeechPermission": "ALLOWED",
    "epub": {
     "isAvailable": true
    },
    "pdf": {
     "isAvailable": true
    },
略------------------------------------------------------

responseには、上記のような長い応答が格納され、
dataにはresponseをJSON型に変換して格納します。

今回はその中から、[title]と[thumbnail]を使いたいので、
data['items'][0]['volumeInfo']['title']
data['items'][0]['volumeInfo']['imageLinks']['thumbnail']
のようにアクセスし、必要となるデータを取り出します。

[0]の値を[1][2]と変更することによって、
取り出す検索結果対象を変更することができます。

スクリーンショット 2020-04-03 21.42.24.png

↓↓↓

スクリーンショット 2020-04-03 21.43.06.png

完成○△□

Q2.ハマりポイント
送信ボタンを押下すると、
FileNotFoundError: [Errno 2] No such file or directory: '/Users/hoge/project/cgi-bin/index.py'
とエラーが表示され、検索結果が表示されない

A2.解決方法
/Users/hoge/project/cgi-bin/index.py は存在していた。
index.py の1行目が誤っていた
× #!usr/bin/env python3
○ #!/usr/bin/env python3

Q3.requestsがインポートできない
import requests
の箇所でエラーが起きてしまう

A3.解決方法
こちらの記事が大変参考になりました。
https://qiita.com/Kent_recuca/items/349586e9c034535f2991

Pythonのsys.pathに
requestsがインストールされたパスを追記することで解決

総括

以前SpringBootを用いてWebアプリを作成したことがありましたが、
それに比べフォルダ構成、環境構築が楽ですぐに動かせるという所感です。

機械学習分野に使われていることは知っていましたが、Web環境でも使用されていることに驚きました。
Pythonに初めて触れたので、
コメントであるはずの#の後ろでなぜエラーが起きるのか、
ローカルサーバーを立ち上げると、なぜindex.htmlが表示されるのか、
まだまだ謎だらけですが、これからお勉強して理解を深めていきたいと思います。

6
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?