Programmation Python/L'interface CGI

Un livre de Wikilivres.


La lecture préalable de
Apache est conseillée.


Description[modifier | modifier le wikicode]

L'interface CGI (pour Common Gateway Interface) est un composant de la plupart des logiciels serveurs de pages web. Il s'agit d'une passerelle qui leur permet de communiquer avec d'autres logiciels tournant sur le même ordinateur. Avec CGI, vous pouvez écrire des scripts dans différents langages (Perl, C, Tcl, PHP, Python ...).

Plutôt que de limiter le web à des documents écrits à l'avance, CGI permet de générer des pages web sur le champ, en fonction des données que fournit l'internaute par l'intermédiaire de son logiciel de navigation ou de données stockées sur le serveur que le script peut également modifier, par exemple en utilisant une base de données. Vous pouvez utiliser les scripts CGI pour créer une large palette d'applications : des services d'inscription en ligne, des outils de recherche dans des bases de données, des instruments de sondage d'opinions, des jeux, etc.

L'apprentissage de la programmation CGI peut faire l'objet de manuels entiers. Dans cet ouvrage d'initiation, nous vous expliquerons seulement quelques principes de base, afin de vous faire comprendre, par comparaison, l'énorme avantage que présentent les modules serveurs d'applications spécialisés tels que Karrigell, CherryPy ou Zope, pour le programmeur désireux de développer un site web interactif.

Installation[modifier | modifier le wikicode]

Pour plus de détails voir : Apache/CGI.

Par défaut, lire un fichier .py en HTTP renvoie son contenu. Pour que le serveur compile et exécute le code source, il faut que la configuration suivante figure dans sa configuration, ou bien que les scripts soient placés dans un répertoire contenant un fichier nommé .htaccess, avec les lignes[1] :

AddHandler cgi-script .py
Options +ExecCGI

Logo

Sur les serveurs Unix les fichiers ne sont pas exécutables par défaut, il faut donc le préciser pour chacun avec la commande : chmod +x *.py.

Exemples[modifier | modifier le wikicode]

Le module cgitb sert aux éventuels débogages :

#!C:\Program Files (x86)\Python\python.exe
# -*- coding: UTF-8 -*-
print "Content-type: text/html; charset=utf-8\n\n"
print "<html><head><title>Répertoire local</title></head><body>"
import cgitb
cgitb.enable()
import os
print "Le fichier CGI se trouve dans :"
print os.path.dirname(__file__)
print "</body></html>"

L'utilisation d'un formulaire nécessite un import cgi[2]. Pour une base de données MySQL c'est import MySQLdb[3]. Soit le fichier suivant CGI_MySQL.py utilisant les deux :

#!C:\Program Files (x86)\Python\python.exe
# -*- coding: UTF-8 -*-
print "Content-type: text/html; charset=utf-8\n\n"
print "<html><head><title>CGI BDD</title></head><body>"
print "<h1>Extraction MySQL</h1>"
print "<ul>"
import cgitb
cgitb.enable()
import cgi, MySQLdb
formulaire = cgi.FieldStorage()
if formulaire.getvalue('nom') == None:
	print "<h2>Rechercher un nom</h2>"
	print '''
	<form action="CGI_MySQL.py" method="post">
	<input type="text" name="nom" />
	<input type="submit"></form>
		'''
else:
	print "<h2>Résultat</h2>"
	print "Liste des correspondances pour " + formulaire.getvalue('nom') + " :"
	connection = MySQLdb.connect(user='login1', passwd='mdp1', db='base1')
	cursor = connection.cursor()
	cursor.execute("SELECT page_title FROM page WHERE nom ='"+formulaire.getvalue('nom')+"'")
	for row in cursor.fetchall():
		print "<li>%s</li>" % row[0]
	connection.close()
print "</ul>"
print "</body></html>"

Une interaction CGI rudimentaire[modifier | modifier le wikicode]

Veuillez donc encoder le document HTML ci-dessous à l'aide d'un éditeur quelconque :

<HTML>
<HEAD><TITLE>Exercice avec Python</TITLE></HEAD>
<BODY>

<DIV ALIGN="center">
<IMG SRC="penguin.gif">
<H2>Page Web interactive</H2>
<P>Cette page est associée à un script Python</P>

<FORM ACTION="http://localhost/cgi-bin/input_query.py" METHOD="post">
<INPUT TYPE="submit" NAME="send" VALUE="Exécuter le script">
</FORM>

</DIV></BODY></HTML>

Vous savez certainement déjà que les balises initiales <HTML>, <HEAD>, <TITLE>, <BODY>, ainsi que les balises finales correspondantes, sont communes à tous les documents HTML. Nous ne détaillerons donc pas leur rôle ici.

La balise <DIV> utilisée à la ligne 5 sert habituellement à diviser un document HTML en sections distinctes. Nous l'utilisons ici pour définir une section dans laquelle tous les éléments seront centrés (horizontalement) sur la page.

À la ligne 6, nous insérons une petite image.

La ligne 7 définit une ligne de texte comme étant un titre de 2e importance.

La ligne 8 est un paragraphe ordinaire.

Les lignes 10 à 12 contiennent le code important (pour ce qui nous occupe ici). Les balises <FORM> et </FORM> définissent en effet un formulaire, c'est-à-dire une portion de page Web susceptible de contenir divers widgets à l'aide desquels l'internaute pourra exercer une certaine activité : champs d'entrée, boutons, cases à cocher, boutons radio, etc.

La balise FORM doit contenir deux indications essentielles : l'action à accomplir lorsque le formulaire sera expédié (il s'agit en fait de fournir ici l'adresse URL du logiciel à invoquer pour traiter les données transmises), et la méthode à utiliser pour transmettre l'information (en ce qui nous concerne, ce sera toujours la méthode post).

Dans notre exemple, le logiciel que nous voulons invoquer est un script Python nommé input_query.py qui est situé dans un répertoire particulier du serveur d'intranet. Sur de nombreux serveurs, ce répertoire s'appelle souvent cgi-bin, par pure convention. Nous supposerons ici que l'administrateur de votre intranet scolaire vous autorise à installer vos scripts Python dans le même répertoire que celui où vous placez vos pages web personnelles.

Vous devrez donc modifier la ligne 10 de notre exemple, en remplaçant le nom de domaine dans l'adresse http://localhost/cgi-bin/input_query.py.

La ligne 11 contient la balise qui définit un widget de type « bouton d'envoi » (balise <INPUT TYPE="submit">). Le texte qui doit apparaître sur le bouton est précisé par l'attribut VALUE ="texte". L'indication NAME est facultative dans le cas présent. Elle mentionne le nom du widget lui-même (au cas où le logiciel destinataire en aurait besoin).

Lorsque vous aurez terminé l'encodage de ce document, sauvegardez-le dans le répertoire que l'on vous a attribué spécifiquement pour y placer vos pages, sous un nom quelconque, mais de préférence avec l'extension .html ou .htm (par exemple : essai.html).

Le script Python input_query.py est détaillé ci-dessous. Comme déjà signalé plus haut, vous pouvez installer ce script dans le même répertoire que votre document HTML initial :

#! /usr/bin/python
# -*- coding: utf-8 -*-
# Affichage d'un formulaire HTML simplifié :
print "Content-Type: text/html\n"
print """
<H3><FONT COLOR="Royal blue">
Page web produite par un script Python
</FONT></H3>

<FORM ACTION="print_result.py" METHOD="post">
<P>Veuillez entrer votre nom dans le champ ci-dessous, s.v.p. :</P>
<P><INPUT NAME="visiteur" SIZE=20 MAXLENGTH=20 TYPE="text"></P>
<P>Veuillez également me fournir une phrase quelconque :</P>
<TEXTAREA NAME="phrase" ROWS=2 COLS=50>Mississippi</TEXTAREA>
<P>J'utiliserai cette phrase pour établir un histogramme.</P>
<INPUT TYPE="submit" NAME="send" VALUE="Action">
</FORM>
"""

Ce script ne fait rien d'autre que d'afficher une nouvelle page web, laquelle contient encore une fois un formulaire, mais celui-ci nettement plus élaboré que le précédent.

La première ligne est absolument nécessaire : elle indique à l'interface CGI qu'il faut lancer l'interpréteur Python pour pouvoir exécuter le script. La seconde ligne spécifie l'encodage du code source.

La ligne 4 est indispensable. Elle permet à l'interpréteur Python d'initialiser un véritable document HTML qui sera transmis au serveur web. Celui-ci pourra à son tour le réexpédier au logiciel navigateur de l'internaute, et celui-ci le verra donc s'afficher dans la fenêtre de navigation.

La suite est du pur code HTML, traité par Python comme une simple chaîne de caractères que l'on affiche à l'aide de l'instruction print. Pour pouvoir y insérer tout ce que nous voulons, y compris les sauts à la ligne, les apostrophes, les guillemets, etc., nous délimitons cette chaîne de caractères à l'aide de « triples guillemets » (Rappelons également ici que les sauts à la ligne sont complètement ignorés en HTML : nous pouvons donc en utiliser autant que nous voulons pour « aérer » notre code et le rendre plus lisible).

Un formulaire HTML pour l'acquisition des données[modifier | modifier le wikicode]

Analysons à présent le code HTML lui-même. Nous y trouvons essentiellement un nouveau formulaire, qui comporte plusieurs paragraphes, parmi lesquels on peut reconnaître quelques widgets. La ligne 10 indique le nom du script CGI auquel les données du formulaire seront transmises : il s'agira bien évidemment d'un autre script Python.

À la ligne 12, on trouve la définition d'un widget de type « champ d'entrée » (Balise INPUT, avec TYPE="text"). L'utilisateur est invité à y encoder son nom. Le paramètre MAXLENGTH définit une longueur maximale pour la chaîne de caractères qui sera entrée ici (20 caractères, en l'occurrence). Le paramètre SIZE définit la taille du champ tel qu'il doit apparaître à l'écran, et le paramètre NAME est le nom que nous choisissons pour la variable destinée à mémoriser la chaîne de caractères attendue.

Un second champ d'entrée un peu différent est défini à la ligne 14 (balise TEXTAREA). Il s'agit d'un réceptacle plus vaste, destiné à accueillir des textes de plusieurs lignes. (Ce champ est automatiquement pourvu d'ascenseurs si le texte à insérer se révèle trop volumineux). Ses paramètres ROWS et COLS sont assez explicites. Entre les balises initiale et finale, on peut insérer un texte par défaut (Mississippi dans notre exemple).

Comme dans l'exemple précédent, la ligne 16 contient la définition du bouton qu'il faudra actionner pour transmettre les données au script CGI destinataire, lequel est décrit ci-après.

Un script CGI pour le traitement des données[modifier | modifier le wikicode]

Le mécanisme utilisé à l'intérieur d'un script CGI pour réceptionner les données transmises par un formulaire HTML est fort simple, comme vous pouvez l'analyser dans l'exemple ci-dessous :

#! /usr/bin/python
# Traitement des données transmises par un formulaire HTML

import cgi                        # Module d'interface avec le serveur web
form = cgi.FieldStorage()         # Réception de la requête utilisateur :
                                  # il s'agit d'une sorte de dictionnaire
if form.has_key("phrase"):        # La clé n'existera pas si le champ
   text = form["phrase"].value    # correspondant est resté vide
else:
   text ="*** le champ phrase était vide ! ***"

if form.has_key("visiteur"):      # La clé n'existera pas si le champ
   nomv = form["visiteur"].value  # correspondant est resté vide
else:
   nomv ="mais vous ne m'avez pas indiqué votre nom"

print "Content-Type: text/html\n"
print """
<H3>Merci, %s !</H3>
<H4>La phrase que vous m'avez fournie était : </H4>
<H3><FONT Color="red"> %s </FONT></H3>""" % (nomv, text)

histogr ={}
for c in text:
   histogr[c] = histogr.get(c, 0) +1

liste = histogr.items()       # conversion en une liste de tuples
liste.sort()                  # tri de la liste
print "<H4>Fréquence de chaque caractère dans la phrase :</H4>"
for c, f in liste:
   print 'le caractère <B>"%s"</B> apparaît %s fois <BR>' % (c, f)

Les lignes 4 et 5 sont les plus importantes :

Le module cgi importé à la ligne 4 assure la connexion du script Python avec l'interface CGI , laquelle permet de dialoguer avec le serveur web.

À la ligne 5, la fonction FieldStorage() de ce module renvoie un objet qui contient l'ensemble des données transmises par le formulaire HTML. Nous plaçons cet objet, lequel est assez semblable à un dictionnaire classique, dans la variable form.

Par rapport à un véritable dictionnaire, l'objet placé dans form présente la différence essentielle qu'il faudra lui appliquer la méthode value() pour en extraire les données. Les autres méthodes applicables aux dictionnaires, telles la méthode has_key(), par exemple, peuvent être utilisées de la manière habituelle.

Une caractéristique importante de l'objet dictionnaire retourné par FieldStorage() est qu'il ne possédera aucune clé pour les champs laissés vides dans le formulaire HTML correspondant.

Dans notre exemple, le formulaire comporte deux champs d'entrée, auxquels nous avons associé les noms visiteur et phrase. Si ces champs ont effectivement été complétés par l'utilisateur, nous trouverons leurs contenus dans l'objet dictionnaire, aux index « visiteur » et « phrase ». Par contre, si l'un ou l'autre de ces champs n'a pas été complété, l'index correspondant n'existera tout simplement pas. Avant toute forme de traitement de valeurs, il est donc indispensable de s'assurer de la présence de chacun des index attendus, et c'est ce que nous faisons aux lignes 7 à 15.

Exercices

  1. Pour vérifier ce qui précède, vous pouvez par exemple désactiver (en les transformant en commentaires) les lignes 7, 9, 10, 12, 14 & 15 du script. Si vous testez le fonctionnement de l'ensemble, vous constaterez que tout se passe bien si l'utilisateur complète effectivement les champs qui lui sont proposés. Si l'un des champs est laissé vide, par contre, une erreur se produit.

Solution

  1. Réfléchissez !
 le script étant lancé par l'intermédiaire d'une page web, les messages d'erreur de Python ne seront pas affichés dans cette page, mais plutôt enregistrés dans le journal des événements du serveur web. Veuillez consulter l'administrateur de ce serveur pour savoir comment vous pouvez accéder à ce journal. De toute manière, attendez-vous à ce que la recherche des erreurs dans un script CGI soit plus ardue que dans une application ordinaire.

Le reste du script est assez classique.

  • Aux lignes 17 à 21, nous ne faisons qu'afficher les données transmises par le formulaire. Veuillez noter que les variables nomv et text doivent exister au préalable, ce qui rend indispensables les lignes 9, 10, 14 et 15.
  • Aux lignes 23, 24 et 25, nous nous servons d'un dictionnaire pour construire un histogramme simple.
  • À la ligne 27, nous convertissons le dictionnaire résultant en une liste de tuples, pour pouvoir trier celle-ci dans l'ordre alphabétique à la ligne 28.
  • La boucle for des lignes 30 et 31 se passe de commentaires.

Références[modifier | modifier le wikicode]

  1. (en) « HOWTO Use Python in the web »
  2. http://fr.openclassrooms.com/informatique/cours/apercu-de-la-cgi-avec-python
  3. https://pypi.python.org/pypi/MySQL-python/1.2.5