Веб-сайт на Python
Что такое веб-сайт?
Это набор Интернет-страничек, наподобие этой, хранящихся на постоянно работающем компьютере (сервере). У компьютера есть адрес, например 87.250.250.203, с которым связано имя, например yandex.ru. Браузер (скажем, Opera или Firefox) подключается к серверу и требует у него нужную страничку. Сервер копается в памяти, достаёт нужный документ и возвращает обратно.
При чём тут Python?
А действительно, при чём? Ведь странички-то пишутся на html!
Дело в том, что информация может поступать очень быстро (особенно, если сайт новостной) и меняться очень часто (mail.ru). Статических страничек начинает нехватать и люди придумывают динамические. Динамическая страница – это не страница. Это программа (на любом языке), которая принимает запрос от браузера и сама, на месте, формирует html-код в ответ. Например, вот такая программка выдаст браузеру страницу, где жирным по белому будет написано «ПРЕВЕД!!!»
# -*- coding: cp1251 -*-
print '''Content-Type: text/html\n
<html>
<head>
</head>
<body>'''
print "<H1>ПРЕВЕД!!!</H1>"
print '</body>\n</html>'
Программное обеспечение для работы веб-сервера
Разнообразного программного обеспечения, способного принимать запросы от браузера и выдавать ответ, очень много. Над всеми ними возвышается король Интернета (и повелитель html-страничек) - Апач (Apache). Довольно громоздкая, медленная, но очень удобная и вообще замечательная штуковина. Но нам, для экспериментов, нужно что-то попроще… попроще… Есть! Питон имеет встроенный веб-сервер, как раз для экспериментов с динамическими страничками!
# -*- coding: cp1251 -*-
"""
Этот скрипт запускает простейший сервер с поддержкой cgi-скриптов.
Конечно можно оставить и так, но для серьёзного сайта выберите
что-нибудь посолиднее.
"""
import CGIHTTPServer
import BaseHTTPServer
import os
# Измените эту директорию на свою перед началом работы!
# /используйте прямые слеши/
# не используйте русские буквы.
HOMEDIR = "D:/teach_prolog/"
PORT = 80
os.chdir(HOMEDIR)
class Handler(CGIHTTPServer.CGIHTTPRequestHandler):
cgi_directories = ["/cgi-bin"]
httpd = BaseHTTPServer.HTTPServer(("",
PORT), Handler)
print "\n"
print "
---------------------------------------------------"
print " ------
Welcome to Teach Python system"
print " -----
Directory mounted:", HOMEDIR
print " ----
Server is now ready. Connect to port", PORT
print "
-----------------------------------------------"
print "\n\n"
httpd.serve_forever()
Что немаловажно, сервер поддерживает CGI.
CGI - скрипты
Cgi-скрипт – это ещё одно название программы, которая выдаёт себя за веб-страничку. Сервер запускает такую программу и ждёт от неё ответ. Этот ответ выдаётся браузеру под видом html-паги. Таким образом, CGI скрипты способны выполнять любые запросы клиента на сервере и отправлять клиенту результаты запроса.
Для написания CGI скриптов пригоден любой интерпретируемый или компилируемый язык программирования, в том числе и Питон. Типичная CGI программа состоит из двух частей: из передачи HTTP заголовков и передачи HTML данных. Веб-сервер связывает вывод CGI скрипта со вводом у броузера. Всё, что выводится CGI скриптом передаётся на клиентскую машину. Обратная связь осуществляется передачей данных от сервера клиенту путём передачи параметров.
Вот ещё один пример типичной CGI программы:
вначале выводятся заголовки
print "Content-Type: text/html"
# Определение типа HTML
print # Пустая строка означает окончание передачи заголовков
теперь выводим основной текст в формате HTML
print "<TITLE>CGI script
output</TITLE>"
print "<H1>This is my first
CGI script</H1>"
print "Hello, world!"
При этом учтите, что нельзя передавать заголовки после вывода текста или внутри него. Так что заголовки выводятся только вначале текста, иначе они считаются просто текстом! Наверное, Вам интересно знать об основной области применения CGI – для обработки информации из форм.
Итак передача параметров в CGI скрипт осуществляется двумя методами: прямой передачей параметров в имени URL в формате http://адрес_скрипта?имя_параметра1=значение_параметра1&имя_параметра2=значение2... При этом способе в программе становятся доступными переменные имя_параметра и им присваиваются переданные значения; второй способ состоит в передаче параметров через HTML форму. Во втором случае необходимо применение модуля CGI:
import cgi
Затем, чтобы включить обработку ошибок полезно вставить в начало следующие строчки:
import cgitb; cgitb.enable()
Если же Вы не хотите, чтобы ошибки Вашего скрипта передавались в броузер клиента, а передавались в log файл, то сделайте следующее:
import cgitb; cgitb.enable(display=0, logdir="/tmp")
Итак, во-первых разберёмся с формами. В модуле CGI есть полезный класс: FieldStorage, который содержит в себе переданную в форме информацию. По сути дела этот класс представляет из себя словарь, обладающий теми же свойствами, что и обычный питоновский словарь, например методами has_key и key(), также можно определить его длину функцией len(). По умолчанию FieldStorage не содержит тех значений, которые в форме остались пустыми (например пустое поле ввода). Чтобы FieldStorage включал все переменные формы сделайте так:
form = cgi.FieldStorage(keep_blank_values=true)
Покажем пример работы с формой:
#Передали заголовки ранее
form = cgi.FieldStorage() #Здесь пустых значений нет!
if not (form.has_key("name")
and form.has_key("addr")):
#А есть ли
такие поля?
print "<H1>Error</H1>" #Плохая форма
print "Please fill in the name and addr fields."
return
print "<p>name:",
form["name"].value
print "<p>addr:",
form["addr"].value #Дальше обрабатываем форму
Учтите, если в форме присутствует несколько элементов с одним именем, то form.getvalue() вернёт список значений всех элементов с
этим именем. Для того чтобы понять, элемент это или список удобно использовать
функцию type. Как
обычно, приведу пример:
from types import ListType
value = form.getvalue("username",
"")
if isinstance(value,
ListType):
# Это список
usernames = ",".join(value)
else:
# Или это просто переменная или полная лажа ;-)
usernames = value
Ну что ещё можно сказать? Хотел бы отметить использование File Upload если в форме есть переменная, представляющая собой файл, то при вызове form.getvalue() этот файл будет загружен из сети в память. Переменная же представляет собой обычный файл и его можно читать, используя методы модуля file:
fileitem = form["userfile"]
if fileitem.file:
# Это и вправду файл!
linecount = 0
while 1:
line = fileitem.file.readline()
if not line: break
linecount
= linecount + 1
Работа с данными. MySql
Для работы с большим объёмом данных просто текстовых файлов будет маловато. И люди придумали Базы Данных.
MySql – обна из реализаций системы управления базами данных (СУБД). Совершенно свободная.
Для работы с MySQL необходимо установить драйвер для работы с этой базой данных для питона: скачиваем (http://sourceforge.net/projects/mysql-python/files/), распаковываем и устанавливаем.
Алгоритм работы с базой данных на питоне: подключаемся к базе данных, создаем курсор (объект для работы с базой данных), выполняем требуемые запросы, применяем изменения базы данных, закрываем соединение с БД.
Перейдем к
практике. Есть csv-файл, каждая строка которого имеет
вид «name;email;adres;telefon» (например «Сергей Иванович;admin@yandex.ru;Москва, ул. Заречная 5, кв.
#!/usr/bin/python
import MySQLdb
import string
# распаковка строки, в которой поля записаны с разделителем ";"
def unpack_line(line):
line = string.replace(line,
"'", "")
els = string.split(line, ";")
# выделяем имя, емейл, адрес и телефон
fname = els[0]
fmail = els[1]
fadres = els[2]
ftel = els[3]
return fname, fmail, fadres, ftel
# подключаемся к базе данных (не забываем указать кодировку, а то в базу запишутся иероглифы)
db = MySQLdb.connect(host="localhost", user="root", passwd="пароль", db="contacts", charset='utf8')
# формируем курсор, с помощью которого можно исполнять SQL-запросы
cursor = db.cursor()
# открываем исходный csv-файл
f = open("log", "r")
# представляем его в виде массива строк
lines = f.readlines()
for line in lines:
# если в строе присутствует емейл (определяем по наличию "@")
if string.find(line, "@") > -1:
# извлекаем данные из строки
fname, fmail, fadres, ftel = unpack_line(line)
# подставляем эти данные в SQL-запрос
sql = """INSERT INTO
contacts(name, mail, adres, tel)
VALUES ('%(name)s',
'%(mail)s', '%(adres)s', '%(tel)s')
"""%{"name":fname, "mail":fmail,
"adres":fadres, "tel":ftel}
# исполняем SQL-запрос
cursor.execute(sql)
# применяем изменения к базе данных
db.commit()
# закрываем соединение с базой данных
db.close()
# закрываем файл
f.close()
Точно таким же образом можно удалять и обновлять данные в базе: изменяется только SQL-запрос. Немного иначе обстоит дело с выборкой данных, а точнее с обработкой выбранной из базы информацией. После выполнения запроса на выборку данных с помощью функции курсора fetchall() можно получить результат запроса. Он представляется в виде массива записей, каждая из которых является массивом содержимого полей. Для того чтобы не запутаться с полями в SQL-запросе необходимо указывать выбираемые поля, и в том же порядке извлекать их из каждой записи.
Разберем выполнение и обработку SELECT-запросов напримере: извлечем первых 10 пользователей из только что заполненной базы, у которых емейл расположен на Яндексе.
#!/usr/bin/python
import MySQLdb
import string
# соединяемся с
базой данных
db = MySQLdb.connect(host="localhost", user="root", passwd="пароль", db="contacts", charset='utf8')
# формируем курсор
cursor = db.cursor()
# запрос к
БД
sql = """SELECT mail,
name FROM eadres WHERE mail LIKE '%yandex.ru' LIMIT 10"""
# выполняем запрос
cursor.execute(sql)
# получаем результат выполнения запроса
data = cursor.fetchall()
# перебираем записи
for rec in
data:
# извлекаем данные из записей - в том же порядке, как и в SQL-запросе
mail, name = rec
# выводим информацию
print name, mail
# закрываем соединение с БД
db.close()
Еще раз напомню, что извлекать данные из записи нужно в том же порядке (и количестве), в каком они записаны в SQL-запросе.
Вот, пожалуй, и все.