Introducing Python(처음 시작하는 파이썬)
Chapter 9. 웹
* World Wide Web 구현의 3가지 아이디어
1) HTTP (Hypertext Transfer Protocol)
- Request와 Response를 교환하기 위한 Web Server와 Client의 Specification이다.
2) HTML (Hypertext Markup Language)
- 결과에 대한 표현 형식이다.
3) URL (Uniform Resource Locator)
- 교유의 해당 서버와 자원을 나타내는 방법이다.
9.1 Web Clients (웹 클라이언트)
* HTTP
- 웹 데이터를 교환하는데 사용되는 표준 프로토콜이다.
- Stateless(무상태)하다.
(이에 대한 해결책으로 Cookie가 있다. 이는 서버가 클라이언트를 식별할 수 있도록 보내는 특정 정보이다. 클라이언트는 다시 서버에 Cookie를 전송하여 서버가 클라이언트를 식별할 수 있도록 한다.)
- Web Browser에서 생성된 각각의 HTTP Connection은 모두 독립적이다.
- 변하지 않는 원격 컨텐츠는 Web Client에 저장하여 다시 서버로부터 다운로드되는 것을 방지한다. (Caching)
- 특정 데이터는 기억되어야 한다. 예를 들어 쇼핑 사이트의 장바구니에 담긴 물품들은 기억되어야 한다. (Session)
- 각각의 사용자를 식별할 수 있어야 한다. (Authentication)
Python Standard Web Library
* Package \(\texttt{http}\) (URL)
- Module \(\texttt{client}\)는 Client-Side를 관리한다.
- Module \(\texttt{server}\)로 Python Web Server를 작성할 수 있다.
- Module \(\texttt{cookies}\), Module \(\texttt{cookiejar}\)로 사이트 방문자의 데이터를 저장하는 Cookie를 관리할 수 있다.
* Package \(\texttt{urllib}\) (URL)
- Module \(\texttt{request}\)는 클라이언트의 요청을 처리한다.
- Module \(\texttt{response}\)는 서버의 응답을 처리한다.
- Module \(\texttt{parse}\)는 URL을 분석한다.
Example. \(\texttt{urllib}\) Usage
>>> import urllib.request as ur
>>> url = 'http://quotesondesign.com/wp-json/posts'
>>> conn = ur.urlopen(url)
>>> print(conn)
<http.client.HTTPResponse object at 0x1006fad50>
>>> data = conn.read() # read() 메서드로 웹 페이지로부터 데이터를 읽어온다.
>>> print(data)
b'[{"ID":2328,"title":"Pete Episcopo","content":"<p>Everything we do communicates.
<\\/p>\\n","link":"http:\\/\\/quotesondesign.com\\/pete-episcopo-2\\/"}]'
>>> print(conn.status)
200
# 정상적으로 처리되었음을 의미한다.
>>> print(conn.getheader('Content-Type'))
application/json; charset=UTF-8
>>> for key, value in conn.getheaders():
... print(key, value)
...
Server nginx
Date Sat, 24 Aug 2013 22:48:39 GMT
Content-Type text/plain
Transfer-Encoding chunked
Connection close
Etag "8477e32e6d053fcfdd6750f0c9c306d6"
X-Ua-Compatible IE=Edge,chrome=1
X-Runtime 0.076496
Cache-Control max-age=0, private, must-revalidate
# Python Library는 모든 HTTP Response Header를 Parsing하여 Dictionary로 제공한다.
- 이 코드는 TCP/IP Connection을 열고, HTTP Request를 만들어 전송하여 HTTP Response를 받는 일련의 과정을 보여주고 있다.
- conn.status값을 출력하여 HTTP Status Code를 확인할 수 있다.
* HTTP Status Code
HTTP Status Code | Description |
1xx | Information (조건부 응답) - 서버는 요청을 받았지만, 클라이언트에 대한 몇 가지 추가 정보를 필요로한다. |
2xx | Success (성공) - 성공적으로 처리되었음을 의미한다. - 200이외의 모든 Success Code는 추가사항을 전달한다. |
3xx | Redirection (리디렉션) - 리소스가 이전되어 클라이언트에 새로운 URL을 응답해준다. |
4xx | Client Error (클라이언트 에러) - 자주 발생하는 404(Not Found)는 클라이언트 측에 문제가 있음을 나타낸다. |
5xx | Server Error (서버 에러) - 500은 서버 에러를 의미한다. - 502(Bad Gateway)는 웹 서버와 백엔드 애플리케이션 서버가 연결되어 있지 않은 경우 발생한다. |
Third Party Module \(\texttt{requests}\) (URL)
Example. requests 설치 명령어
$ pip install requests
Example. requests Usage
>>> import requests
>>> url = 'http://quotesondesign.com/wp-json/posts'
>>> resp = requests.get(url)
>>> resp
<Response [200]>
>>> print(resp.text)
[{"ID":2328,"title":"Pete Episcopo","content":"<p>Everything we do
communicates.<\/p>\n","link":"http:\/\/quotesondesign.com\/pete-episcopo-2\/"}]
9.2 Web Servers (웹 서버)
* Web Framework
- 웹사이트를 구축할 수 있는 기능을 제공한다.
Web Server Gateway Interface
- CGI(Common Gateway Interface)는 클라이언트를 위해 웹 서버가 외부 프로그램을 실행하고, 그 결과를 리턴하도록 설계되어 있다. 프로그램은 각 클라이언트의 접근을 위해 매번 처음부터 다시 시작되는데, 이러한 접근 방식은 확장성을 저해하고 작은 프로그램을 시작하는데에도 상당한 시간이 걸릴 수 있게 한다.
- CGI에서의 Startup Delay를 피하기 위해 웹 서버에 Interpreter를 두기 시작했다.
- 실례로, Apache에서는 PHP를 mod_php 모듈로, Perl은 mod_perl 모듈로, Python은 mod_python 모듈로 실행한다.
(이러한 동적 언어의 코드는 외부 프로그램이 아닌, 장기적으로 작동하는 Apache Process내에서 실행된다.)
- FastCGI, SCGI와 같이 장기적으로 동작하는 프로그램 내에서 동적언어로 작성된 코드를 실행하여 웹 서버와 통신하는 방식도 있다.
- Python 웹 개발은 Python Web Application과 Web Server간의 범용적 API인 WSGI(Web Server Gateway Interface)를 정의하는데에서 시작되었다.
Framework
* Web Framework에서 제공하는 기본적인 기능
Feature | Description |
Route | - URL을 해석하여 해당 서버의 파일이나 Python 코드를 찾아준다. |
Template | - 서버 사이드의 데이터를 HTML 페이지에 병합한다. |
Authentication, Authorization | - 사용자 이름과 비밀번호, Permission을 처리한다. |
Session | - 웹사이트에 방문하는 동안 사용자의 임시 데이터를 유지한다. |
Framework: \(\texttt{bottle}\) (URL)
- Bottle은 하나의 Python 파일로 구성되어 있고, 추후 쉽게 배포할 수 있다.
- Python Standard Library가 아니다.
* run() 함수의 Optinoal Parameter는 아래와 같다.
- debug=True 는 HTTP 에러가 생길 경우, 디버깅 페이지를 생성시킨다.
- reloader=True 는 파이썬 코드가 변경되면 변경된 코드를 다시 불러온다.
Example. bottle 설치 명령어
$ pip install bottle
Example. 텍스트를 리턴하는 웹 서버
from bottle import route, run
@route('/')
def home():
return "It isn't fancy, but it's my home page"
run(host='localhost', port=9999)
- 위 Python 코드를 실행하고, 웹 브라우저의 주소창에 http://localhost:9999/를 입력하면 텍스트가 리턴된다.
Example. HTML 페이지(index.html)를 리턴하는 웹 서버
from bottle import route, run, static_file
@route('/')
def main():
return static_file('index.html', root='.')
run(host='localhost', port=9999)
Example. URL에 Argument를 전달받아 이를 문자열에 포함시켜 리턴하는 웹 서버
from bottle import route, run, static_file
@route('/')
def home():
return static_file('index.html', root='.')
@route('/echo/<thing>')
def echo(thing):
return "Say hello to my little friend: %s!" % thing
run(host='localhost', port=9999)
- 사용자로부터 입력받는 URL의 /echo/ 뒤에 오는 문자열을 포함하는 문자열을 리턴한다.
Example. requests 라이브러리를 활용하여 위 웹 서버들이 동작하는지 확인하는 Unit Test
import requests
resp = requests.get('http://localhost:9999/echo/Mothra')
if resp.status_code == 200 and \
resp.text == 'Say hello to my little friend: Mothra!':
print('It worked! That almost never happens!')
else:
print('Argh, got this:', resp.text)
Framework: \(\texttt{flask}\)
- \(\texttt{werkzeug}\) WSGI Library와 \(\texttt{jinja2}\) Template Library(URL)를 포함하고 있다.
- 기본 홈 디렉터리는 static이다.
- File에 대한 URL은 /static으로 시작한다.
Example. flask 설치 명령어
$ pip install flask
Example. URL에 Argument를 전달받아 이를 문자열에 포함시켜 리턴하는 웹 서버
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 "Say hello to my little friend: %s" % thing
app.run(port=9999, debug=True)
- run()함수에서 debug=True로 함으로써 파이썬 코드가 변경되면 변경된 코드를 다시 불러오고,
서버 코드에 Exception이 발생한 경우, 이에 대한 정보를 페이지로 출력한다.
(\(\texttt{bottle}\) Framework에서 \(\texttt{debug}\)와 \(\texttt{reloader}\) Parameter가 분리되어 있던 점에 대비적이다.)
※ Web Server를 배포할 때에는 debug=True 옵션을 설정하지 않는것이 좋다.
- 서버에 대한 정보가 노출되기 때문이다.
Example. flask의 Template System인 jinja2를 사용한 예시
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/echo/<thing>')
def echo(thing):
return render_template('flask2.html', thing=thing)
app.run(port=9999, debug=True)
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/echo/<thing>')
def echo(thing):
return render_template('flask2.html', thing=thing)
app.run(port=9999, debug=True)
Example. GET parameter를 이용하는 방법
from flask import Flask, render_template, request
app = Flask(__name__)
@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)
- GET Command가 URL에 사용되는 경우, Argument는 &key1=val1&key2=val2&... 형태로 전달된다.
Example. Dictionary의 ** 연산자를 통해 한 Dictionary로부터 여러 Argument를 Template에 전달하는 예시
from flask import Flask, render_template, request
app = Flask(__name__)
@app.route('/echo/')
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)
- **kwargs는 위의 예시에서 나오는 thing=thing, place=place 처럼 작동한다.
Non-Python Web Server
WSGI Module: \(\texttt{mod_wsgi}\) (URL)
- Apache WEb Server에 최적화된 모듈로,
Apache Process안에서, 혹은 Apache와 통신하기 위해 분리된 Process 안에서 Python 코드를 실행한다.
nginx (URL)
- nginx 웹 서버는 Python Module을 따로 지원하지 않는다.
- 대신, uWSGI와 같은 별도의 WSGI 서버를 사용하여 통신한다.
* uWSGI (URL)
Other Framework
- bottle과 flask와 같은 작은 프레임워크는 DB를 직접적으로 지원하지 않는다.
django (URL)
- CRUD(Create, Read, Update, Delete) 웹 페이지를 자동으로 생성하기 위한 ORM(Obejct Relational Mapping) 코드를 포함하고 있다.
(물론, 취향껏 SQLAlchemy, SQL Query를 사용할 수도 있다.)
web2py (URL)
- django와 비슷한 웹 프레임워크이다.
pyramid (URL)
- pylons 프로젝트에서 성장한 웹 프레임워크로, django와 범위가 비슷하다.
turbogears (URL)
- ORM, 여러 종류의 DB, 여러 종류의 Template Language를 지원한다.
wheezy.web (URL)
- Performance에 중점을 둔 웹 프레임워크이다.
* Python Web Framework Table (URL)
Other Python Web Server
* Python-Based WSGI Server
- uwsgi (URL)
- cherrypy (URL)
- pylons (URL)
* Event-Based Server
- tornado (URL)
- gevent (URL)
- gunicorn (URL)
9.3 Web Services and Automation (웹 서비스와 자동화)
Module: \(\texttt{webbrowser}\)
Example. webbrowser Usage
>>> import webbrowser
>>> url = 'http://www.python.org/'
>>> webbrowser.open(url)
True
# Browser에 Python.org 사이트를 불러온다.
>>> webbrowser.open_new(url)
True
# 사이트를 Browser의 새 창에서 연다.
>>> webbrowser.open_new_tab('http://www.python.org/')
True
# 사이트를 Browser의 새 탭에서 연다.
Web API & REST
- Web Page 대신, Web API를 통해 서버에서 유저에게 데이터를 제공할 수 있다.
* REST (Representational State Transfer)
- RESTful 서비스에서는 HTTP Verb를 사용한다.
- RESTful 클라이언트는 HTTP 요청 헤더를 사용하여 서버로부터 하나 이상의 콘텐츠 타입을 요청할 수 있다.
* HTTP Verb
- HEAD: 실제 데이터가 아닌, 리소스에 대한 정보를 얻어온다.
- GET: 서버에서 리소스의 데이터를 검색한다. 브라우저에서 사용되는 표준 메서드다.
- POST: 서버의 데이터를 갱신한다.
- PUT: 새로운 리소스를 생성한다.
- DELETE: 서버의 데이터를 삭제한다.
Crawler & Scrape
- 자동화된 Web Fetcher를 Crawler 혹은 Spider라 한다.
- 원격 웹 서버에서 콘텐츠를 찾은 후, Scraper에서 원하는 정보를 찾기 위해 Parsing한다.
* Framework: scrapy (URL)
* BeautifulSoup (URL)
- 웹 페이지의 HTML로부터 데이터를 추출할 때 사용한다.
Example. Beautiful Soup 설치 명령어
$ pip install beautifulsoup4
Example. Beautiful Soup Usage
def get_links(url):
import requests
from bs4 import BeautifulSoup as soup
result = requests.get(url)
page = result.text
doc = soup(page)
links = [element.get('href') for element in doc.find_all('a')]
return links
if __name__ == '__main__':
import sys
for url in sys.argv[1:]:
print('Links in', url)
for num, link in enumerate(get_links(url), start=1):
print(num, link)
print()
Reference: Introducing Python(처음 시작하는 파이썬) (Bill Lubanovic 저, O'Reilly, 2015)