[Plan du site]
Vous êtes ici ---
> Le Site du Zéro
> Les tutoriels
> Non-Officiels
> Programmation
> Python
> Aperçu de la CGI avec Python
> Lecture du tutoriel
Aperçu de la CGI avec Python
Vous vous apprêtez à lire un tutoriel rédigé par un membre de ce site. Malgré tout le soin que ce membre a pu apporter au tutoriel, nous ne pouvons pas garantir que les informations contenues sur cette page sont exactes à 100%. Merci de garder cela en tête lorsque vous lirez cette page ;o)
Vous aimeriez rendre votre site interactif, par exemple pour mettre en place un compteur de visites, un formulaire de contact, un petit jeu, ...
Pour ça, tout le monde utilise le PHP. Mais, vous faites partie d'une élite éclairée, et vous aimeriez le faire avec Python. C'est faisable !
Voici un aperçu de la
CGI avec ce langage.
Juste une chose avant de commencer, si vous n'avez pas d'hébergeur qui vous permette d'utiliser Python sur vos pages web, regardez donc
cette liste. Si vous hébergez votre site vous même, alors sachez qu'il vous suffit d'installer Apache et Python.
Pour que vos scripts soient exploitables, il faut tout d'abord configurer Apache.
Créons un fichier
.htaccess, que vous placerez à la racine du site (en effet, ce fichier fonctionne récursivement : tous les répertoires situés dans le répertoire contenant votre
.htaccess seront assujettis aux règles que vous définirez dans celui-ci). Le voici :
Code : Apache1
2 | AddHandler cgi-script .py
Options +ExecCGI
|
Ces deux lignes indiquent que les fichiers
.py doivent être interprétés avant d'être envoyés au client. Le comportement par défaut serait d'envoyer le fichier comme s'il s'agissait d'un fichier texte ordinaire.
Une bonne chose à faire est de configurer Apache pour considérer vos fichiers
index.py comme les indexes de vos répertoires. Pour cela, vous pouvez ajouter la ligne suivante à votre
.htaccess :
Code : Apache
Néanmoins, il reste une chose
très importante à faire : rendre votre script exécutable. Je dois reconnaître que je me suis souvent fait avoir par ce genre d'erreur. C'est assez pervers car vous ne savez pas pourquoi votre script échoue : vous vous retrouvez face à une erreur 500 fort peu explicite.
Pour ce faire, sous UNIX, il faut utiliser la commande suivante :
Code : Bash
Vous
devez le faire sur chacun de vos scripts.
Maintenant, nous allons voir comment rédiger nos fichiers Python.
Lorsque vous utilisez python pour votre site web, vous générez du HTML.
Il vous faut le préciser : vous pourriez très bien renvoyer une image, aussi faut il informer le client de la nature du document renvoyé.
Pour ce faire, il faut tout simplement utiliser
print :
Code : Python1
2
3
4
5 | #!/usr/bin/python
print 'Content-type: text/html'
print
# Là commence votre code.
|
La première ligne est importante : il s'agit du
shebang. Il indique quel interpréteur Python utiliser.
La seconde (en réalité, la troisième car j'ai sauté une ligne) indique le type de document envoyé.
La dernière écrit une ligne vide. En effet, le client et le serveur communiquent par le protocole HTTP. Ce dernier est composé d'un en-tête, dans lequel des informations comme le nom et la version du client, ceux du serveur, le poids du fichier, l'état de la requête (vous savez, 200 OK, 404 Not Found, 500 Internal Server Error, ...), etc., et du corps de la requête, qui contient, dans le cas d'une requête du serveur vers le client, le fichier envoyé. Ces deux blocs (en-tête et corps) sont séparés par une ligne vide. Pour plus d'informations, regardez donc l'
article sur Wikipedia.
Le type de fichier (que j'ai spécifié à la deuxième ligne avec
'Content-type: text/html'), fait partie de l'en-tête de la requête. Or, la page que vous souhaitez envoyer se trouve dans le corps. C'est pourquoi vous devez les séparer à l'aide de cette ligne vide.
Votre script est maintenant prêt. Vous pouvez désormais écrire votre code HTML, toujours à l'aide de print :
Code : Python1
2
3
4
5 | #!/usr/bin/python
print 'Content-type: text/html'
print
print '<html><head><title>...'
|
Vous pouvez, plutôt que de vous esquinter à écrire ligne par ligne :
Code : Python1
2
3
4
5
6 | print '<html>'
print '<head>'
print '<title>Mon super site en Python qui powne tout XdXDxDXDXd</title>'
print '</head>'
print '<body>'
# ... Je vous épargne la suite de cette horreur.
|
Utiliser la syntaxe suivante, qui n'est pas toujours connue :
Code : Python 1
2
3
4
5
6
7
8
9
10 | print '''
<html>
<head>
<title>Mon super site en Python qui powne tout XdXDxDXDXd</title>
</head>
<body>
<p>Vous êtes jaloux, hein ?</p>
</body>
</html>
'''
|
Vous constatez qu'à l'intérieur des
''', vous pouvez utilisez l'indentation que vous voulez.
Il est bien entendu important de rendre son site conforme aux normes de la W3C.
Si vous avez eu l'occasion de faire une erreur dans votre code, vous avez sûrement pu admirer une erreur 500. Et, vous l'avez remarqué, c'est très génant pour le dépistage d'erreurs. Le module
cgitb va pouvoir vous aider.
Pour cela, il faut l'importer, puis l'activer :
Code : Python1
2 | import cgitb
cgitb.enable()
|
C'est fait ! Maintenant, les erreurs s'afficheront sur la page. Ces deux lignes vous seront certainement utiles !
Maintenant que vous connaissez le strict minimum, pourquoi ne pas l'employer ? Nous allons réaliser un compteur de visites.
Il serait judicieux de tenter de le faire seul. Néanmoins, je vous donne ma méthode :
Tout d'abord, je tente d'ouvrir un fichier en mode lecture (
'r' pour
read). Je stocke son contenu dans la variable
nbr_visiteurs, en le transformant en un nombre (car il s'agit pour l'instant d'une chaîne). Si cela ne marche pas (pour la bonne raison que mon fichier n'existe pas, ou que son contenu n'est pas un nombre), alors
nbr_visiteurs = 0.
Cela donne :
Code : Python1
2
3
4
5 | try: # Pour essayer le code qui suit. Si ledit code ne fonctionne pas, alors except: est appelé. Autrement, on continue.
fichier = open('compteur','r') # Lecture dans le fichier, appelé 'compteur'.
nbr_visiteurs = int(fichier.read()) # int() transforme son argument (à savoir, le contenu du fichier retourné par fichier.read()) en un nombre.
except Exception:
nbr_visiteurs = 0 # Si le fichier est inexistant (= page jamais visitée) ou incorrect, on repart de 0.
|
Maintenant, nous allons ouvrir le fichier avec l'option 'w' pour write. S'il existe, il sera supprimé et recréé vide. Sinon, il sera tout simplement créé. Dans les deux cas, nous n'aurons plus qu'à écrire dedans la valeur nbr_visiteurs + 1 (car il faut compter le visiteur qui charge la page :-) ), en la transformant en une chaine avec la fonction str().
Code : Python1
2 | fichier = open('compteur','w')
fichier.write(str(nbr_visiteurs+1))
|
Finalement, on indique au visiteur le nombre de visites :
Code : Python1 | print nbr_visiteurs+1,'visites \o/'
|
Ce qui, avec le code minimal, donne :
Code : Python 1
2
3
4
5
6
7
8
9
10
11
12
13 | #!/usr/bin/python
print 'Content-type: text/html'
print
try:
fichier = open('compteur','r')
nbr_visiteurs = int(fichier.read())
except Exception:
nbr_visiteurs = 0
fichier = open('compteur','w')
fichier.write(str(nbr_visiteurs+1))
print nbr_visiteurs+1,'visites \o/'
|
Un des points inévitables en CGI est le traitement de formulaires. En Python, c'est faisable aussi.
Nous travaillons maintenant avec des données envoyées par l'utilisateur. Nous allons notamment les afficher à l'écran. Or, si le visiteur décide de mettre du HTML dans les données qu'il envoie, cela peut présenter une faille.
Prenons un cas concret. Si le visiteur entre le code suivant :
Code : HTML1 | <script type="text/javascript">alert('OLOL CMT G T PWNED');</script>
|
Une alerte s'affichera sur l'écran du visiteur ! Et rien ne l'empêche de se faire passer pour vous "Votre session va expirer. Entrez votre mot de passe à nouveau.", de le récupèrer puis de se le faire envoyer par mail, le tout en JavaScript !
Pour pallier à ce problème, nous allons utiliser une fonction fort pratique :
cgi.escape(). C'est un peu l'équivalent de
htmlentities() en PHP : elle remplace les caractères
< et
> par
< et
>. De ce fait, le code Javascript cité plus haut s'affichera tel quel, au lieu d'être exécuté.
Aussi,
prenez l'habitude de sécuriser toutes les données envoyées par le visiteur à l'aide de cette fonction.
Pour traiter un formulaire, nous devons tout d'abord importer le module CGI. Vous savez comment faire :
Code : Python
À partir de là, ça se corse

On va créer une instance de la classe
cgi.FieldStorage(), qui contiendra le formulaire envoyé. Puis, on pourra en extraire les informations avec
instance.getvalue('nom'). Cela vous semble tordu ? En vérité, c'est très simple.
Code : Python 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | #!/usr/bin/python
import cgi
print 'Content-type: text/html'
print
formulaire = cgi.FieldStorage()
if formulaire.getvalue('nom') == None:
print '''
Veuillez remplir le formulaire :
<form action="formulaire.py" method="post">
<input type="text" name="nom" />
<input type="submit"></form>
'''
else:
print 'Ainsi, vous vous appelez',cgi.escape(formulaire.getvalue('nom')),' ?' # N'oubliez pas de sécuriser le code !
|
Ce code, enregistré dans un fichier
formulaire.py (autrement, il vous faudra le modifier, car le formulaire renvoie vers
formulaire.py), créera une instance de
cgi.FieldStorage() dans la variable formulaire.
Si
formulaire.getvalue('nom') == None, alors le champ
'nom' était vide, voire non envoyé.
On présente donc le formulaire au client. Autrement, on récupère la valeur du champ
'nom' avec
formulaire.getvalue('nom'), et on l'affiche tout en prenant soin de le sécuriser avec
cgi.escape().
Si le champ s'était appelé
'prenom', vous comprenez bien qu'on aurait récupéré sa valeur avec
formulaire.getvalue('prenom').
Vous voulez une bonne nouvelle ? Pour un formulaire envoyé avec GET (c'est à dire que les valeurs des champs se retrouvent dans l'url, dans le style
page.py?var=valeur&var2=valeur2), c'est exactement pareil.
Ainsi, le script suivant enregistré sous
essai.py et appelé avec
essai.py?var=1&var2=2 affichera 'Var = 1 et Var2 = 2'.
Code : Python1
2
3
4
5
6
7
8
9 | #!/usr/bin/python
import cgi
print 'Content-type: text/html'
print
form = cgi.FieldStorage()
print 'Var = ',cgi.escape(form.getvalue('var')),' et Var2 = ',cgi.escape(form.getvalue('var2'))
|
Le livre d'or est une des fonctionnalités qu'on se plaît à implémenter sur son site. Grâce aux formulaires et à la manipulation de fichiers, c'est tout à fait faisable en Python.
Nous allons tout d'abord créer un formulaire tout simple, puis le traiter. Ensuite, nous verrons comment lire le fichier dans lequel seront enregistrés les messages.
Le formulaire ressemblera à ça :
Code : HTML1
2 | <form action="enregistrement.py" method="post"><p>Vous avez une remarque, un commentaire, un conseil ? Signez le livre d'or !</p>
<p><p>Pseudonyme : <input type="text" name="pseudo" /><br />Site web : <input type="text" name="site" /><br />Message :<br /><textarea name="message"></textarea><br /><input type="submit" /></form>
|
À vous de le faire correspondre à vos besoins.
Pour la gestion du formulaire :
Code : Python 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 | #!/usr/bin/python
import cgi
print 'Content-type: text/html'
print
print '<html><head><title>Mon super site</title></head><body>'
formulaire = cgi.FieldStorage()
if formulaire.getvalue('message') != None:
print '<p>Merci d\'avoir participé au livre d\'or. Vous pouvez le visiter <a href="livreor.py">ici</a>.</p>'
message = formulaire.getvalue('message')
site = formulaire.getvalue('site')
pseudo = formulaire.getvalue('pseudo')
try:
fichier = open('livreor','r')
livreor = fichier.read()
except IOError:
livreor = ''
message_poste = '<message><auteur>'+cgi.escape(pseudo)+'</auteur><site>'+cgi.escape(site)+'</site><contenu>'+cgi.escape(message)+'</contenu></message>\n'
fichier = open('livreor','w')
fichier.write(message_poste+livreor)
else:
print '''Erreur : vous n'avez pas rempli le formulaire.'''
print '</body></html>'
|
C'est tout simple :
- On charge le formulaire dans la variable formulaire avec cgi.FieldStorage
- On teste si le formulaire a été rempli, en vérifiant qu'un message est présent. Vous pouvez bien sûr vérifier si tous les champs sont remplis.
- On charge les éléments du formulaire dans des variables.
- On essaye de lire le contenu du fichier livreor. Si cela rate (avec une IOError), c'est que le fichier n'existe pas. Auquel cas, le contenu de livreor sera une chaîne vide.
- On ouvre le fichier livreor en mode écriture. On écrit dedans le contenu du nouveau message.
L'écriture est terminée. Chaque message est écrit dans le format:
Code : XML1 | <message><auteur>Auteur</auteur><site>Site</site><contenu>Contenu</contenu></message>
|
.
Nous n'avons plus qu'à le lire !
Code : Python 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 | #!/usr/bin/python
def trouver(chaine,chaine1,chaine2):
position_chaine1 = chaine.index(chaine1) + len(chaine1)
position_chaine2 = chaine.index(chaine2,position_chaine1)
return chaine[position_chaine1:position_chaine2]
def isoler_message(chaine):
dico = {}
try:
message = trouver(chaine,'<message>','</message>')
dico['auteur'] = trouver(message,'<auteur>','</auteur>')
dico['site'] = trouver(message,'<site>','</site>')
dico['contenu'] = trouver(message,'<contenu>','</contenu>')
return dico
except Exception:
return 0
def supprimer(chaine):
return chaine.replace('<message>'+trouver(chaine,'<message>','</message>')+'</message>','')
print 'Content-type: text/html'
print
print '''
<html>
<head>
<title>Le livre d'or</title>
</head>
<body>
'''
try:
fichier = open('livreor','r')
contenu = fichier.read()
continuer = True
while continuer:
message = isoler_message(contenu)
if not message:
continuer = False
else:
print '<p>De : <b><a href="',message['site'],'">',message['auteur'],'</a></b></p>'
print '<p>',message['contenu'],'</p>'
print '<br /><br />'
chaine = supprimer(chaine)
except Exception:
print 'Il n\'y a aucun message dans le livre d\'or'
print '</body></html>'
|
Ma première fonction,
trouver(), retourne la première chaîne trouvée entre
chaine1 et
chaine2 dans
chaine. Son fonctionnement est simple, lisez le code si vous souhaitez le comprendre.
La seconde,
isoler_message(), utilise
trouver() pour trouver le premier message (la première chaîne entre
<message> et
</message>), puis extrait les différentes informations (l'auteur, le site, et le contenu). Finalement, elle retourne le dictionnaire dans lequel elle a tout enregistré.
La troisième,
supprimer(chaine), supprime le premier message (le texte entre
<message> et
</message>, ainsi que les deux balises), pour que la lecture puisse être recommencée sur le message suivant.
On commence par afficher le début du HTML de la page. Puis, on essaye de lire le fichier
livreor, puis d'isoler le premier message, de l'afficher, et de le supprimer de la liste, avant de recommencer, jusqu'à ce qu'il n'y ait plus de messages.
Si cela ne fonctionne pas, on affiche qu'il n'y a pas de message dans le livre d'or.
C'est tout à fait rudimentaire. À vous d'ajouter ce que vous voulez : datez les messages, faites des pages lors de l'affichage, ...
Vous voyez que, en travaillant des fichiers et en traitant des formulaires, vous pouvez déjà aller loin. Légèrement modifié, ce livre d'or peut vous fournir un système de news.
Une dernière astuce dont j'aimerai vous faire part est l'affichage de la source. En effet, il est agréable de pouvoir avoir le code d'un site web sous les yeux, pour pouvoir s'en inspirer, ou même donner des conseils au webmaster.
Ce genre de chose ne doit PAS être implémenté sur des pages sensibles comme l'administration, à moins que vous ne soyez absolument certain(e) de la sécurité de votre code.
Pour afficher la source, le client n'aura qu'à entrer l'URL de la page ainsi :
http://site.web/index.py?source=1
Il nous faudra donc utiliser
cgi.FieldStorage(), et tester si source=1. Dans ce cas, on lit le fichier, et on l'affiche entre
<pre> et
</pre>, pour que le code HTML et les sauts à la ligne apparaissent.Puis, on s'arrête là avec
sys.exit(0).
Autrement, on envoie la page comme d'habitude.
Voici mon code :
Code : Python 1
2
3
4
5
6
7
8
9
10
11
12
13 | #!/usr/bin/python
import sys # Nécéssaire pour votre sys.exit(0)
import cgi
print 'Content-type: text/html'
print
form = cgi.FieldStorage()
if form.getvalue('source') == '1':
fichier = open('fichier.py','r')
print '<pre>'+fichier.read()+'</pre>
sys.exit(0) # On s'arrête là.
# Suite du code
|
C'est enfantin mais pratique
Ce tutoriel n'est ici que pour solliciter votre enthousiasme. Pour pouvoir mettre en place un site web plus complexe, utilisant une base de données, par exemple, vous devez vous tourner vers la documentation.
J'espère vous avoir intéressé et donné des idées.
Bonne continuation avec Python !
Merci à delroth et plus généralement à tout #python pour l'aide qui m'a été apportée lors de la création de mon propre site, et sans qui ce tutoriel n'aurait pas vu le jour.