12. Веб-сервіси на Python
![]() |
Скрипти на Python можуть виконуватися сервером WEB. Саме цей сервер буде приймати запити від клієнтів. З точки зору клієнта, виклик сервісу WEB еквівалентний запиту на URL цього сервісу. Клієнт може бути написаний будь-якою мовою, зокрема на Python. Нам потрібно вміти «спілкуватися» зі службою WEB, тобто розуміти протокол Http, що забезпечує зв’язок між веб-сервером та його клієнтами. Саме цьому присвячені наведені нижче програми.
Скрипти веб-сервісу будуть виконуватися веб-сервером Apache WampServer. Їх потрібно розмістити в спеціальному каталозі: <WampServer>\bin\apache\apachex.y.z\cgi-bin, де <WampServer> — це папка встановлення WampServer, а x.y.z — версія веб-сервера Apache.
![]() |
Просто розмістити скрипти Python у папці <cgi-bin> недостатньо. У першому рядку скрипта має бути вказано шлях до інтерпретатора Python, який слід використовувати. Цей шлях вказується у вигляді коментаря:
Читач повинен адаптувати цей шлях до свого власного середовища.
12.1. Клієнтсько-серверний додаток для дати та часу
Нашим першим веб-сервісом буде сервіс дати та часу: клієнт отримує поточну дату та час.
12.1.1. Сервер
#!D:\Programs\ActivePython\Python2.7.2\python.exe
import time
# заголовки
print "Content-Type: text/plain\n"
# передача часу клієнту
# місцевий час: кількість мілісекунд з 01.01.1970
# «формат відображення дати та часу»
# d: день (2 цифри)
# m: місяць (2 цифри)
# y: рік у 2 цифри
# H: година 0,23
# M: хвилини
# S: секунди
print time.strftime('%d/%m/%y %H:%M:%S',time.localtime())
Примітки:
- рядок 6: скрипт повинен самостійно генерувати деякі заголовки HTTP у відповіді клієнту. Вони додаватимуться до заголовків HTTP, згенерованих самим сервером Apache. Заголовок HTTP у рядку 6 повідомляє клієнту, що йому буде надіслано ресурс у форматі text/plain, тобто неформатований текст. Зверніть увагу на символ «\n» у кінці заголовка, який створить порожній рядок після заголовка. Це обов’язково: саме цей порожній рядок сигналізує клієнту HTTP про кінець заголовків HTTP у відповіді. Далі йде ресурс, запитуваний клієнтом, у даному випадку — неформатований текст;
- рядок 18: ресурс, надісланий клієнту, — це текст, що відображає поточну дату та час.
12.1.2. Два тести
Попередній скрипт можна виконати безпосередньо в інтерпретаторі Python у командному вікні, як ми це робили досі. Це дозволяє усунути можливі синтаксичні або функціональні помилки. Отримуємо такий результат:
Після тестування скрипта таким чином його можна розмістити в папці <cgi-bin> на сервері Apache (див. параграф 12). Запустимо програму WampServer. Це запускає одночасно веб-сервер Apache та СКБД MySQL. Наразі ми будемо використовувати лише веб-сервер. Далі за допомогою браузера відкриємо наступну сторінку URL: http://localhost/cgi-bin/web_02.py:
![]() |
- в [1]: запитуваний URL;
- у [2]: відповідь, відображена браузером;
- в [3]: вихідний код, отриманий веб-браузером. Це саме той код, який надіслав скрипт Python.
За допомогою деяких інструментів (у даному випадку Firebug, плагін для браузера Firefox) можна отримати доступ до HTTP-заголовків, якими обмінюється сервер. У наведеному вище прикладі веб-браузер отримав такі HTTP-заголовки:
На рядках 7–8 можна впізнати HTTP-заголовок, надісланий скриптом Python. Попередні заголовок були згенеровані веб-сервером Apache.
12.1.3. Програмований клієнт
Тепер ми напишемо скрипт, який буде клієнтом попереднього веб-сервісу. Ми використовуємо можливості модуля httplib, який спрощує написання HTTP-клієнтів.
# -*- coding=utf-8 -*-
import httplib,re
# константи
HOST="localhost"
URL="/cgi-bin/web_02.py"
# підключення
connexion=httplib.HTTPConnection(HOST)
# відстеження
connexion.set_debuglevel(1)
# відправлення запиту
connexion.request("GET", URL)
# обробка відповіді
reponse=connexion.getresponse()
# вміст
contenu=reponse.read()
# завершення з'єднання
connexion.close()
print "------\n",contenu,"-----\n"
# отримання елементів часу
elements=re.match(r"^(\d\d)/(\d\d)/(\d\d) (\d\d):(\d\d):(\d\d)\s*$",contenu).groups()
print "Jour=%s,Mois=%s,An=%s,Heures=%s,Minutes=%s,Secondes=%s" % (elements[0],elements[1],elements[2],elements[3],elements[4],elements[5])
Примітки:
- рядок 3: модуль re необхідний для роботи з регулярними виразами, а модуль httplib — для функцій HTTP-клієнтів;
- рядок 9: створюється HTTP-з'єднання з портом 80 модуля HOST, визначеного в рядку 6;
- рядок 11: моніторинг дозволяє переглянути HTTP-заголовки запиту клієнта та відповіді сервера;
- рядок 13: надсилається запит до веб-сервісу URL. Існує два способи її запиту: за допомогою команди HTTP GET або POST. Різниця між ними пояснюється далі. Тут запит буде здійснено за допомогою команди HTTP GET;
- рядок 15: зчитується відповідь сервера. Тут отримується повна відповідь: заголовки Http та ресурс, який запитував клієнт. У своїй відповіді сервер міг попросити клієнта здійснити перенаправлення. У цьому випадку клієнт httplib автоматично здійснює перенаправлення. Отже, отримана відповідь — це та, що походить від перенаправлення;
- рядок 17: відповідь складається з заголовків HTTP та документа, запитаного клієнтом. Щоб отримати лише заголовки HTTP, використовується [reponse].getHeaders(). Щоб отримати документ, використовується [reponse].read();
- рядок 19: після отримання відповіді від веб-сервера з’єднання з ним закривається;
- відомо, що документ, надісланий сервером, — це рядок тексту у форматі 15/06/11 14:56:36. У рядках 22–26 використовується регулярний вираз для вилучення різних елементів цього рядка.
12.1.4. Результати
Примітки:
- рядок 1: заголовки HTTP, надіслані клієнтом на веб-сервер;
- рядки 2–6: заголовки HTTP у відповіді веб-сервера;
- рядок 8: документ, надісланий сервером;
- рядок 11: результат його обробки;
12.2. Отримання сервером параметрів, надісланих клієнтом
У протоколі HTTP клієнт має два способи передачі параметрів веб-серверу:
- він запитує URL служби у формі
GET url?param1=val1¶m2=val2¶m3=val3… HTTP/1.0
де значення vali повинні попередньо пройти кодування, щоб певні зарезервовані символи були замінені на їхні шістнадцяткові значення.
- запитує URL служби у вигляді
потім серед заголовків HTTP, що надсилаються на сервер, розміщує такий заголовок:
Наступні заголовки, що надсилаються клієнтом, закінчуються порожнім рядком. Після цього він може надіслати свої дані у вигляді
, де vali, як і у випадку з методом GET, має бути попередньо закодовано. Кількість символів, що надсилаються на сервер, має дорівнювати N, де N — це значення, заявлене в заголовку:
12.2.1. Веб-сервіс
Наступний веб-сервіс отримує від свого клієнта 3 параметри: nom, prenom, age. Він отримує їх із своєрідного словника під назвою cgi.FieldStorage, який надає модуль cgi. Значення vali параметра parami отримується за допомогою vali=cgi.FieldStorage().getlist("parami"). Отримується масив із:
- 0 елементів, якщо параметр parami відсутній у запиті клієнта;
- 1 елемент, якщо параметр parami присутній 1 раз у запиті клієнта;
- n елементів, якщо параметр parami присутній n разів у запиті клієнта.
Після отримання параметрів скрипт повертає їх клієнту.
#!D:\Programs\ActivePython\Python2.7.2\python.exe
import cgi
# заголовки
print "Content-Type: text/plain\n"
# отримання сервером інформації, надісланої клієнтом
# тут ім'я=P&прізвище=N&вік=A
formulaire=cgi.FieldStorage()
# їх надсилають назад клієнту
print "informations recues du service web [prenom=%s,nom=%s,age=%s]" % (formulaire.getlist("prenom"),formulaire.getlist("nom"),formulaire.getlist("age"))
Тест можна провести за допомогою веб-браузера:
![]() |
У [1] — відповідь веб-сервісу URL. Зверніть увагу на наявність трьох параметрів: nom, prenom, age. У [2] — відповідь веб-сервісу.
12.2.2. Клієнт GET
# -*- coding=utf-8 -*-
import httplib,urllib
# константи
HOST="localhost"
URL="/cgi-bin/web_03.py"
PRENOM="Jean-Paul"
NOM="de la Huche"
AGE=42
# параметри потрібно кодувати перед відправкою на сервер
params = urllib.urlencode({'nom': NOM, 'prenom': PRENOM, 'age': AGE})
# параметри розміщуються в кінці URL
URL+="?"+params
# з'єднання
connexion=httplib.HTTPConnection(HOST)
# відстеження
connexion.set_debuglevel(1)
# відправлення запиту
connexion.request("GET",URL)
# обробка відповіді
reponse=connexion.getresponse()
# вміст
contenu=reponse.read()
print contenu,"\n"
# закриття з'єднання
connexion.close()
Примітки:
- рядки 8–10: значення 3 параметрів, надісланих до веб-сервісу;
- рядок 13: їх потрібно закодувати. Це робиться за допомогою методу urlencode модуля urllib. Цей модуль імпортується в рядку 3. Метод приймає як параметр словник {param1:val1, param2:val2, ...};
- рядок 15: у запиті GET (рядок 21) клієнт повинен розмістити кодовані параметри в кінці запиту URL веб-сервісу;
- наступні рядки вже розглядалися.
12.2.3. Результати
Примітки:
- рядок 2: зверніть увагу на кодування параметрів (прізвище, ім’я, вік);
- рядок 8: відповідь веб-сервісу.
12.2.4. Клієнт POST
Клієнт POST аналогічний клієнту GET, за винятком того, що закодовані параметри більше не входять до складу цільового URL. Вони передаються як третій аргумент запиту POST (рядок 19).
# -*- coding=utf-8 -*-
import httplib,urllib
# константи
HOST="localhost"
URL="/cgi-bin/web_03.py"
PRENOM="Jean-Paul"
NOM="de la Huche"
AGE=42
# параметри потрібно кодувати перед відправкою на сервер
params = urllib.urlencode({'nom': NOM, 'prenom': PRENOM, 'age': AGE})
# з'єднання
connexion=httplib.HTTPConnection(HOST)
# відстеження
connexion.set_debuglevel(1)
# відправлення запиту
connexion.request("POST",URL,params)
# обробка відповіді
reponse=connexion.getresponse()
# вміст
contenu=reponse.read()
print contenu,"\n"
# закриття з'єднання
connexion.close()
12.2.5. Результати
Примітки:
- зверніть увагу на рядок 2, де показано метод, який використовує клієнт POST для надсилання закодованих параметрів:
- заголовок HTTP Content-Length вказує кількість символів, які будуть надіслані до веб-сервісу;
- після цього заголовка Http йде порожній рядок, що вказує на кінець заголовків Http;
- потім надсилаються 39 символів закодованих параметрів.
- рядок 8: відповідь веб-сервісу.
12.3. Отримання змінних середовища з веб-сервісу
12.3.1. Веб-сервіс
Скрипт CGI на Python виконується в системному середовищі, яке має атрибути. Ці атрибути та їхні значення доступні у словнику os.environ.
#!D:\Programs\ActivePython\Python2.7.2\python.exe
import os
# заголовки
print "Content-Type: text/plain\n"
# інформація про середовище
for (cle,valeur) in os.environ.items():
print "%s : %s" % (cle,valeur)
Примітки:
- рядок 3: щоб отримати доступ до «системних» змінних, потрібно імпортувати модуль os.
Якщо безпосередньо виконати наведений вище скрипт (c.a.d у режимі консолі, а не CGI), у консолі з’являться такі результати:
У веб-браузері (у цьому випадку виконується скрипт CGI) ми отримуємо такі результати:
![]() |
Слід зауважити, що залежно від контексту виконання отримане середовище є різним.
12.3.2. Програмований клієнт
# -*- coding=utf-8 -*-
import httplib
# константи
HOST="localhost"
URL="/cgi-bin/web_04.py"
# підключення
connexion=httplib.HTTPConnection(HOST)
# відправлення запиту
connexion.request("GET", URL)
# обробка відповіді
reponse=connexion.getresponse()
# вміст
print reponse.read()
12.3.3. Результати
Слід зауважити, що запрограмований клієнт не отримує точно таку саму відповідь, як веб-браузер. Це пов’язано з тим, що веб-браузер надіслав веб-серверу інформацію, яку той використав для формування відповіді. У цьому випадку запрограмований клієнт не надіслав жодної інформації про себе.




