Tyto stránky již nejsou udržovány. Obsah je postupně přesouván/aktualizován na adrese chytrosti.marrek.cz.
1/1
Tento text je mírně zastaralý. Takto se web dnes už nedělá. (viz článek: Jak napíšu webovou aplikaci v Pythonu?) Ale někdy se tento způsob tvorby webu může přece jen hodit. Můžete si tak vyzkoušet staré dobré CGI, které funguje téměř všude.
Obsah:
CGI je protokol pro propojení externích aplikací s webovým serverem. To serveru umožňuje delegovat požadavek od klienta na externí aplikaci, která dle požadavku vrátí výstup.
Prostě a jednoduše: Klient (prohlížeč) zašle web-serveru požadavek. Server spustí nějaký program nebo skript a jeho výstup zašle zpět klientovy.
Aby vše fungovalo je nutné nainstalovat a nastavit webový server. Nejčastěji používaným je Apache.
aptitude install apache2-mpm-prefork
Konfigurace se provádí v souboru /etc/apache2/sites-enabled/000-default
.
aptitude install mc
mcedit /etc/apache2/sites-enabled/000-default
Pokud chcete využít pohodlí práce v GUI můžete udělat něco jako:
aptitude install gedit
a poté se přihlásíte přes ssh
s parametrem -X
ssh -X jmeno@muj.stroj.tld gedit /etc/apache2/sites-enabled/000-default
Grafická aplikace se vám poté zobrazí na lokálním display.
Provedeme konfiguraci podle návodu.
<Directory /var/www/>
Options -Indexes FollowSymLinks MultiViews +ExecCGI
AddHandler cgi-script .cgi
AllowOverride None
Order allow,deny
allow from all
</Directory>
`--> stáhnout
Důležité je +ExecCGI
a AddHandler cgi-script .cgi
.
Všechny soubory s příponou .cgi
se budou spouštět a jejich výstup se odešle do webového prohlížeče.
Po změně konfigurace je třeba znovu přesvědčit webový server aby si konfiguraci znovu načetl.
service apache2 reload
Přejdeme do adresáře /var/www:
cd /var/www
vytvoříme soubor index.cgi
.
mcedit index.cgi
Základem každého skriptu je http hlavička. Minimální hlavička může vypadat např. takto:
Content-Type: text/html;
<prázdný řádek>
Prázdný řádek na konci je důležitý. Více o http můžete najít v článku List of HTTP header fields nebo Hypertext Transfer Protocol. Pokud se chcete podívat jak vypadá některá konkrétní http hlavička konkrétní srkánky poslouží vám dobře program [[!wkcz Telnet]] nebo web-sniffer.
Náš zdrojový kód potom může vypadat asi takto
#!/usr/bin/python
# -*- coding: utf8 -*-
import datetime
print "Content-Type: text/html; Charset=utf-8;\n"
print "<h1>Funguje mi to!!! Huráááá!!!</h1>"
td=datetime.datetime.today()
print "<p>Aktuální datum: {}. {}. {}</p>".format(td.day, td.month, td.year)
print "<p>Aktuální čas: {}:{}:{}</p>".format(td.hour, td.minute, td.second)
`--> stáhnout
Nakonec je ještě potřeba nastavit souboru práva pro spouštění.
chmod a+x index.cgi
Data lze stránce předat pomocí html formulářů.
Jde tedy o html značky
<form>
a <input>
.
<form>
<p>
<input type="text" name="vstup" value="hodnota" />
<input type="checkbox" name="anone" /> Ano nebo ne
</p><p>
<input type="submit" name="tlac" value="Ano"/>
<input type="submit" name="tlac" value="Ne"/>
</p>
</form>
`--> stáhnout
GET a POST jsou dvě základní metody protokolu http. Metoda GET ukládá data to URL. Proto je následující heslo po stisknutí entru viditelné v adresním řádku prohlížeče.
<form method="get">
<input type="password" name="vstup" value="" />
</form>
`--> stáhnout
Z toho důvodu data, která chceme skrýt nebo data, která jsou příliš objemná zasíláme pomocí metody POST.
<form method="post">
<input type="password" name="vstup" value="" />
</form>
`--> stáhnout
Python disponuje modulem CGI,
pomocí kterého lze jednoduše přebírat parametry zaslané skriptu, bez
ohledu na to, zda byla použita metoda GET nebo POST. Základem je objekt
FieldStorage
.
import cgi
print "Content-Type: text/html; Charset=utf-8;\n"
print "<h1>Funguje mi to!!! Huráááá!!!</h1>"
print """
<h1>Formulář, co ho odesílám</h1>
<form method="get">
<p>
<input type="text" name="vstup" value="hodnota" />
<input type="checkbox" name="anone" /> Ano nebo ne
</p><p>
<input type="submit" name="odeslat" />
</p>
</form>
<h1>Data, která jsem dostal</h1>
"""
# načtu data odeslané pomocí formuláře
data = cgi.FieldStorage()
try:
print "<p>vstup:",data["vstup"].value ,"</p>"
except:
print """<p style="color:red;">Nedostal jsem data.</p>"""
# tento způsob je trošku bezpečnější...
print "<p>vstup:",data.getvalue('vstup') ,"</p>"
`--> stáhnout
Ladění je trochu problematické, protože skript nespouštím já ale webový server. Když udělám ve skriptu syntaktickou chybu, v prohlížeči se dozvím jen, že jsem udělal chybu, ale už nevím kde.
Chybu bych měl odhalit poté, co spustím skript v příkazové řádce:
user@troj:~$ /var/www/index.cgi
File "./index.cgi", line 7
print "<h1>Můj data</h1>"
^
IndentationError: unexpected indent
Tento způsob nefunguje vždy dobře a někdy je potřeba se podívat do logu webového serveru. Zde dobře poslouží příkaz tail.
user@troj:/var/www$ tail /var/log/apache2/error.log
[Tue Feb 12 09:57:25 2013] [error]
[Tue Feb 12 09:57:25 2013] [error]
[Tue Feb 12 09:57:25 2013] [error] print "<h1>M\xc5\xafj data</h1>"
[Tue Feb 12 09:57:25 2013] [error]
[Tue Feb 12 09:57:25 2013] [error] ^
[Tue Feb 12 09:57:25 2013] [error] IndentationError
[Tue Feb 12 09:57:25 2013] [error] :
[Tue Feb 12 09:57:25 2013] [error] unexpected indent
[Tue Feb 12 09:57:25 2013] [error]
[Tue Feb 12 09:57:25 2013] [error] Premature end of script headers: index.cgi
Protože je toto všechno velmi složité přináší Python modul cgitb
.
Stačí vložit na začátek skriptu něco jako:
1 #!/usr/bin/python
2 # -*- coding: utf8 -*-
3 import cgitb
4 cgitb.enable()
`--> stáhnout
a naprostou většinu chyb by nemělo být problém vidět přímo ve webovém prohlížeči.
Vše shrneme do malého příkladu webové ankety:
Základem bude funkce anketa
def anketa(dotaz,moznosti,uloziste):
`--> stáhnout
dotaz
je text otázky.
možnosti
je seznam řetězců různých možností odpovědi.
uloziste
identifikátor určuje příslušnost dat ke konkrétní anketě.
Když ji potom budeme volat budeme psát něco jako :
anketa('Kokik hodin denně se učím?',
['jednu','dvě','24','neučím se'],
'uceni')
`--> stáhnout
Základem bude výpis možností a bargraf. Chci aby funkce tiskla asi následující HTML.
<p>Kolik hodin denně se učím?</p>
<ul>
<li><a href="?uloziste=uceni&volba=0">jednu</a><br/>
<span style="display:block; width:100px; background-color:#aa99ee;">100</span>
</li>
<li><a href="?uloziste=uceni&volba=1">dvě</a><br/>
<span style="display:block; width:50px; background-color:#aa99ee;">50</span>
</li>
<li><a href="?uloziste=uceni&volba=2">24</a><br/>
<span style="display:block; width:200px; background-color:#aa99ee;">200</span>
</li>
</ul>
`--> stáhnout
Kolik hodin denně se učím?
Bargraf je tvořen prvkem <span>
o zadané šířce. Všimněte si také parametru
href=
u odkazů. Takto lze skriptu předat parametry pomocí metody GET.
Toto HTML musíme vytisknou v Pythonu. Pokud se
vám nedaří rozluštit některou část kódu jako je
.format
1,
map
2
nebo enumerate
podívejte se na tahák.
print "<p>{0}</p>".format(dotaz)
print "<ul>"
for i,m in enumerate(moznosti):
print """<li><a href="?uloziste={0}&volba={1}">{2}</a><br/>
<span style="display:block;
width:{3}px;
background-color:#aa99ee;">
{4}</span></li>""".format(uloziste,i,m, 200, 0)
print "</ul>"
`--> stáhnout
Šířka bargrafu 200 a počet kliknutí 0 je zatím vložen přímo, ale bude posléze nahrazen hodnotou přečtenou z úložiště.
Jako úložiště nám bude sloužit textový soubor. (Stejně tak to může být id řádku tabulky v SQL databázi).
V pythonu potřebujeme zjisti jestli existuje soubor zadaného jména.
import os.path
if os.path.exists('jmeno souboru'):
pass
`--> stáhnout
Pokud soubor existuje je třeba jej přečíst 3 a připravit na další zápis tj. zkrátit ho na nulovou délku. 4
Pokud souboru neexistuje, znamená to, že na anketu ještě nikdo neklikl a hodnoty všech bargrafů jsou 0. Soubor je ale třeba založit -- otevřít pro zápis.
Soubor musí být umístěn v adresáři, kam má web-server právo zápisu. Doporučuji pro tento účel zřídit samostatný adresář:
cd /var/www
mkdir write
chmod a+w write
jmeno = 'write/XartYTg_'+uloziste+'.txt'
if os.path.exists(jmeno):
f = open(jmeno,"r+")
data = f.readlines()
data = map(int,data) # převedu řetězce na čísla
f.truncate(0)
f.seek(0)
else:
data = map(lambda x: 0, range(len(moznosti)))
f = open(jmeno,'w')
`--> stáhnout
Divné část jména souboru XartYTg
je bezpečnost prvek, který má zamezit
nepovolaným návštěvníkům našich stránek zkoumat soubory do kterých jim nic
není.
Teď už si do seznamu odpovědí můžu nechat vypsat konkrétní hodnotu data[i]
.
""".......{4}</span></li>""".format(uloziste,i,m, 200, data[i])
`--> stáhnout
Ještě před zapsáním dat je ale třeba zjisti, jestli uživatel neklikl na odkaz pro zvýšení počítadla:
form = cgi.FieldStorage()
formUloziste = form.getvalue('uloziste')
formVoba = form.getvalue('volba')
`--> stáhnout
... pokud ano, navýším počítadlo ...
if formUloziste == uloziste and formVoba:
data[int(formVoba)] += 1
`--> stáhnout
... zapíšu data a zavřu soubor.
data=map(lambda x: str(x)+'\n',data) # převedu čísla na řetězce a přidám konec řádku
f.writelines(data)
f.close()
`--> stáhnout
Zbývá ještě vyřešit proměnnou délku bargrafu. Ta by se dala počítat například takto:
delka = 200*data[i]/max(data)
`--> stáhnout
Nejvyšší bargraf by pak měl šířku 200px
a ostatní úměrně ke své hodnotě. To
ale nejde pokud je anketa na začátku prázdná a všechny hodnoty data[]
jsou
nulové. Nulou totiž dělit nejde. Kód bude jen maličko složitější:
mm = max(data) if max(data) != 0 else 1
#... a pak
delka = 200*data[i]/mm
`--> stáhnout
Taky by to ale chtělo dát do seznamu...
""".......{4}</span></li>""".format(uloziste,i,m, delka, data[i])
`--> stáhnout