Python
Démarche de travail
La démarche de travail à suivre est quasiment identique à celle du projet en Bash. Les différences résident dans les commandes utilisées pour effectuer chaque étape et bien sûr dans les particularités du langage python. Certaines étapes se sont avérées plus simples ; d'autres un peu plus problématiques.
Aspiration des pages (et stockage local)
Lien vers l'article du blog
Manipuler les fichiers en python est très simple :
- Pour ouvrir un fichier en mode lecture : fichier=open("nomDuFichier.txt", "r")
- Pour lire dans le fichier: contenu=fichier.read() OU contenu=fichier.readlines()
- Pour ouvrir un fichier en mode écriture : fichier=open("nomDuFichier.txt", "w")
(où "r" et "w" indique la mode d'ouverture (read/write))
Ouvrir un flux vers une page internet peut être aussi simple. Après avoir ajouté la bibliothèque urllib (import urllib), un flux peut être ouvert de la façon suivante :
flux=urllib.urlopen("http://www.nomDuSite.com")
contenuFlux=flux.read()
flux.close()
Ensuite, a priori, il suffit d’ouvrir un nouveau fichier pour stocker la page aspirée et écrire le contenu dedans :
pageAspiree=open("pageaspiree1.html", "w")
print>>pageAspiree, contenuFlux
pageAspiree.close()
Cependant, comme avec le programme en Bash (spécifiquement avec les commandes « wget » et « curl »), des problèmes d’aspiration des pages nous ont forcées à vérifier l’aspiration et à traiter les éventuelles erreurs.
Récupérer le code http de la commande urlopen() permet de traiter les erreurs http et ainsi trier les pages non-aspirées. Nous ne pouvons pas continuer à travailler sur ces pages-là si le contenu n’est pas aspiré, donc c’est une étape très importante.
codehttp = flux.getcode()
Un code http indiquant la réussite de l’aspiration est 200. Un code http autre que 200 indique qu’un problème a empêché l’aspiration.
Le problème est que certaines URLs prennent plus de temps à être aspirées – et ceci peut bloquer tout le programme pendant un certain temps. Il est nécessaire de limiter le temps d’attente pour ne pas bloquer l’avancement du programme, ce qui est possible avec URLlib2 :
flux=urllib2.urlopen(ligne , timeout=60)
Ici le temps maximum d'attente est 60 seconds. Au-delà de 60 seconds, nous passons à l'URL suivante.
URLlib2 a une manière plus sophistiquée de traiter les exceptions. Tandis que la réponse de urllib est de produire la page avec un message d'erreur et puis d'utiliser ce message d'erreur, urllib2 traite automatiquement l'exception en renvoyant à l'écran le message d'erreur, ce qui arrête le programme.
Il est nécessaire avec URllib2 d’anticiper ces erreurs pour que le programme continue malgré qu’une page ne soit pas aspirée.
URLerreur="-"
httperreur="-"
print u">>>URL numéro %d, dossier %s >>> "%(i, title),
try:
flux = urllib2.urlopen(ligne, timeout=1)
except socket.timeout as socketErreur:
print "\rURLErreur >>> %s" %(socketErreur)
print>>fichierin, "Page non aspirée"
print>>fichierin, " socket timeout"
i+=1
continue
except urllib2.HTTPError as httperreur:
print "Erreur http >>> %s" %(httperreur)
print>>fichierin, "Page non aspirée"
print>>fichierin, "%s" %(httperreur)
i+=1
continue
except urllib2.URLError as URLerreur:
print "\rURLErreur >>> %s" %(URLerreur.reason)
print>>fichierin, "Page non aspirée"
print>>fichierin, "%s" %(URLerreur.reason)
i+=1
continue
else:
codehttp = flux.getcode()
print u"début de l'aspiration >>>",
try :
contenuhtml = flux.read()
except socket.timeout as socketErreur:
flux.close()
print "URLErreur >>> %s" %(socketErreur)
print>>fichierin, "Page non aspirée"
print>>fichierin, " socket timeout"
else :
flux.close()
print "done ! - http code : %s" %(codehttp)
pageaspiree=open("./PAGES-ASPIREES/"+filename+"/page"+str(i)+".html", "w" )
print>>pageaspiree, contenuhtml
pageaspiree.close()
print>>fichierin, "< a href=\"../PAGES-ASPIREES/"+filename +"/page" + str(i) +".html\">page "+str(i)+"< /a>"
i+=1
Extraction du contenu textuel (dump en texte-brut)
Lien vers l'article du blog -> L'encodage et l'extraction du contenu textuel
Lien vers l'article du blog -> Deuxième essai
Le module nltk fournit une commande pour nettoyer du html afin d'extraire uniquement le contenu textuel :
dumptext = nltk.clean_html(contenuhtml)
Si l'encodage est de l'utf-8, cette étape peut être effectuée immédiatement pour faire un dump du texte brut en utf-8. Travaillant dans un environnement Unicode, il est nécessaire de faire un dump initial avant de convertir ces pages de leur encodage original en utf-8. Dans tous les cas, il est d'abord nécessaire de détecter l'encodage :
Détection d'encodage
Lien vers l'article du blog -> L'encodage et l'extraction du contenu textuel
Lien vers l'article du blog -> Deuxième essai
C'est une des étapes les plus difficiles en raison de la difficulté de détecter de manière automatique un encodage à partir du texte. Nous avons vu dans le programme en Bash que la commande « file -i » permettait de détecter l'encodage d'un fichier. Ce n'est pas fiable à 100%, ce qui n'est pas étonnant, et la commande renvoyait souvent le résultat « unknown 8-bit » quand elle n'arrivait pas à deviner l'encodage. Dans le cas où « file » ne détectait pas d'encodage, il était nécessaire de faire un deuxième niveau de détection d'encodage, en cherchant dans le code source de la page, en extrayant le codage de la balise <meta charset..>.
En python il existe « chardet », qui renvoie l'encodage du fichier donné et le degré de certitude associé à cette détection d'encodage. Elle est plus intéressante que « file » car elle renvoie aussi un degré d'assurance qui n'est pas fournie par « file ». Le résultat est renvoyé sous forme d'un dictionnaire :
A première vue, « chardet » semble bien fonctionner. Elle fournit un encodage (ou au moins une estimation éclairée de l'encodage) dans TOUS les cas, contrairement à « file » qui renvoie souvent la réponse « unknown 8-bit ».
Pourtant, le fait que « chardet » renvoie toujours un résultat provoque encore plus de problèmes, car nous ne pouvons pas nous fier à ce qui est renvoyé, même avec le niveau d'assurance. Notamment les encodages Windows-1253 et Latin-1, utilisés respectivement pour le grec et le français n'appartiennent pas à la liste d'encodages reconnus par « chardet ».
En se tournant vers la bibliothèque BeautifulSoup, nous avons essayé ensuite le module UnicodeDammit, qui prend comme argument le texte et une liste d'encodages possibles, qui aide UnicodeDammit à correctement détecter l'encodage :
newContent=UnicodeDammit(plaintext,[encodage] )
text= newContent.unicode_markupdump
textutf8=text.encode('utf-8')
Sans cette liste d'encodages possibles, UnicodeDammit rencontre aussi des problèmes dans la détection d'encodage. Une solution serait d'inverser les deux étapes de la détection d'encodage afin de fournir à UnicodeDammit un encodage possible.
- Extraire l'encodage écrit dans la balise <meta charset..>
- Renvoyer l'encodage trouvé en argument à UnicodeDammit
Le module « re » permet d'extraire le codage en utilisant une expression régulière :
encodage=re.findall(r'< ?meta.*charset.*?/?>', contenuhtml)
encodage= re.findall(r'charset ?= ?[^/>]*', encodage[0])
encodage=encodage[0].split("=")
encodage= (re.findall(r'[\w-]+[^ \"\']', encodage[1]))
Extraction des contextes
Lien vers l'article du blog
L'extraction des contextes contenants notre motif nécessite l'utilisation des expressions régulières. Nous avons déjà vu que c'est possible avec le module python "re".
Pour extraire les mots associés à l'américanisation, nous avons utilisé le même motif utilisé en Bash :
Cependant, l'extraction de toute une ligne de texte dans laquelle se trouve le motif, en utilisant les métacaractères ^ (début de ligne) et $ (fin de ligne), était problématique. Une ligne de texte contenait tout un paragraphe, ce qui faisait que l'extraction des contextes n'était pas assez pertinente, car presque tout le texte était renvoyé. Il fallait trouver une expression qui renvoyait un certain nombre de mots avant et après le mot recherché :
expr = re.compile(ur"(?:([^\t\n]{,150})((am.ricani[sz].*?\W)|(\W[Aα]μερικανοπο[ίι].*?\W)|(εξαμερ[ίι]καν[ίι]σ.*?\W)|(\W[Aα]μερικανισμός))([^\t\n]{,150}\W))", re.I)
Le flag 're.I' indique que les différences de casses sont ignorées dans l'expression. Nous voulons extraire le contexte immédiat de chaque côté du mot extrait. Donc l'expression va chercher jusqu'à 150 caractères qui précèdent et 150 caractères qui suivent le mot, à condition que la phrase extraite ne contienne que des mots complets.
Les caractères non-ASCII étant encodés sur plusieurs octets, il était d'abord nécessaire de décoder le texte avant d'extraire les contextes :
texte=texte.decode("utf-8")
pour ensuite recoder le texte en utf-8 avant d'écrire dans le fichier:
texte=texte.encode("utf-8")
Une fois les contextes écrits dans un fichier pour chaque URL et dans un fichier global pour chaque langue, nous procédons à la dernière étape : l'ajout du programme Perl « minigrep » et le comptage du nombre d'occurrences du mot en utilisant len() sur le résultat de re.findall().
Le Script
[ Haut de la page ]
Pour télécharger le script cliquez ici !
1 #!/usr/bin/python
2 #!-*-coding:utf8-*-
3 import urllib
4 import urllib2
5 import os
6 import socket
7 import nltk
8 from bs4 import UnicodeDammit
9 import re
10
11 dos=raw_input("Donnez le nom du dossier contenant les fichiers url : ")
12 tableau=raw_input("Donnez le nom du fichier html ou stocker ces liens : ")
13
14 #Commencer a ecrire dans le tableau (ouverture des balises html et body)
15 fichierin =open(tableau, "w")
16 fichierin.write("<html><head><link rel=\"stylesheet\" href=\"style.css\"/><meta charset=\"utf-8\" /><title>Americanisation</title></head><body>")
17
18
19 #creation d'un dossier pour les fichiers globaux s'il n'existe pas deja
20 if not os.path.exists("./FICHIERSGLOBAUX"):
21 os.mkdir("./FICHIERSGLOBAUX")
22
23 j=1 #variable pour compter les tableaux
24
25
26
27 #------Lire le contenu du dossier et traiter chaque fichier URL------#
28
29 for filename in (os.listdir(dos)):
30
31 #ouvrir le fichier url
32 fichierout=open("%s/%s"%(dos,filename), "r")
33
34 #-------Création des noms de sous-dossiers------#
35
36 (filename, extension) = os.path.splitext(filename)
37
38
39 #----------Création de sous-dossiers------------#
40
41 if not os.path.exists("./PAGES-ASPIREES/"+filename):
42 os.mkdir("./PAGES-ASPIREES/"+filename)
43
44 if not os.path.exists("./DUMP-TEXT/"+filename):
45 os.mkdir("./DUMP-TEXT/"+filename)
46
47 if not os.path.exists("./CONTEXTES/"+filename):
48 os.mkdir("./CONTEXTES/"+filename)
49
50 if not os.path.exists("./FICHIERSGLOBAUX/"+filename):
51 os.mkdir("./FICHIERSGLOBAUX/"+filename)
52
53
54 #-----creation du titre du tableau------#
55
56 title = filename.replace ("_", " ")
57
58 #----Commencer a ecrire le tableau, et ses headers----#
59
60 print>>fichierin, "<table><tr><th colspan=\"9\">Tableau numero",j, "(",title,") :</th></tr>"
61 print>>fichierin, "<tr><th class=\"short\">No.</th><th>Lien</th><th>Page aspirée</th><th>Erreur</th><th>DUMP</th><th>DUMP UTF-8</th><th>Contextes</th><th>Contextes Html</th><th class=\"short\">Nbr Occs</th></tr>"
62
63 j+=1
64 i=1 #compteur pour les URLs
65 countglobal=0 #compteur pour le nombre de fichiers dans les fichiers globaux
66
67
68 #--------Lire le contenu du fichier URL et traiter chaque URL --------#
69
70 for ligne in (fichierout.readlines()):
71
72 print>>fichierin, "<tr><td>",i,"</td><td><a href=\"",ligne,"\">URL",i,"</a></td>" # 2 premieres colonnes
73
74
75 #------Aspiration de la page -----#
76
77 URLerreur="-"
78 httperreur="-"
79
80 print u">>>URL numéro %d, dossier %s >>> "%(i, title),
81
82 try:
83 flux = urllib2.urlopen(ligne, timeout=20) #tenter d'ouvrir le flux
84
85
86 #--------Gestion des erreurs ---------#
87
88 except socket.timeout as socketErreur:
89 print "\rURLErreur >>> %s" %(socketErreur)
90 print>>fichierin, "<td class=\"erreur\">Page non aspirée</td>"
91 print>>fichierin, "<td class=\"erreur\"> socket timeout</td>"
92 print>>fichierin, "<td class=\"erreur\"> -</td>"
93 print>>fichierin, "<td class=\"erreur\"> -</td>"
94 print>>fichierin, "<td class=\"erreur\"> -</td>"
95 print>>fichierin, "<td class=\"erreur\"> -</td>"
96 print>>fichierin, "<td class=\"erreur\">-</td></tr>"
97
98 i+=1
99 continue
100
101 except urllib2.HTTPError as httperreur:
102
103 print "Erreur http >>> %s" %(httperreur)
104 print>>fichierin, "<td class=\"erreur\">Page non aspirée</td>"
105 print>>fichierin, "<td class=\"erreur\">%s</td>" %(httperreur)
106 print>>fichierin, "<td class=\"erreur\"> -</td>"
107 print>>fichierin, "<td class=\"erreur\"> -</td>"
108 print>>fichierin, "<td class=\"erreur\"> -</td>"
109 print>>fichierin, "<td class=\"erreur\"> -</td>"
110 print>>fichierin, "<td class=\"erreur\"> -</td></tr>"
111
112 i+=1
113 continue
114
115 except ValueError as valueError:
116
117 print "Erreur http >>> %s" %(valueError)
118 print>>fichierin, "<td class=\"erreur\">Page non aspirée</td>"
119 print>>fichierin, "<td class=\"erreur\">%s</td>" %(valueError)
120 print>>fichierin, "<td class=\"erreur\"> -</td>"
121 print>>fichierin, "<td class=\"erreur\"> -</td>"
122 print>>fichierin, "<td class=\"erreur\"> -</td>"
123 print>>fichierin, "<td class=\"erreur\"> -</td>"
124 print>>fichierin, "<td class=\"erreur\"> -</td></tr>"
125
126 i+=1
127 continue
128
129 except urllib2.URLError as URLerreur:
130 print "\rURLErreur >>> %s" %(URLerreur.reason)
131 print>>fichierin, "<td class=\"erreur\">Page non aspirée</td>"
132 print>>fichierin, "<td class=\"erreur\">%s</td>" %(URLerreur.reason)
133 print>>fichierin, "<td class=\"erreur\"> -</td>"
134 print>>fichierin, "<td class=\"erreur\"> -</td>"
135 print>>fichierin, "<td class=\"erreur\"> -</td>"
136 print>>fichierin, "<td class=\"erreur\"> -</td>"
137 print>>fichierin, "<td class=\"erreur\"> -</td></tr>"
138
139 i+=1
140 continue
141
142
143 #------Si le flux est ouvert-------#
144
145 else:
146 codehttp = flux.getcode()
147 print u"début de l'aspiration >>>",
148 try :
149 contenuhtml = flux.read() #lire le contenu
150
151
152 #--------Gestion des erreurs-------#
153
154 except socket.timeout as socketErreur:
155 flux.close()
156 print "\rURLErreur >>> %s" %(socketErreur)
157 print>>fichierin, "<td class=\"erreur\">Page non aspirée</td>"
158 print>>fichierin, "<td class=\"erreur\"> socket timeout</td>"
159 print>>fichierin, "<td class=\"erreur\"> -</td>"
160 print>>fichierin, "<td class=\"erreur\"> -</td>"
161 print>>fichierin, "<td class=\"erreur\"> -</td>"
162 print>>fichierin, "<td class=\"erreur\"> -</td>"
163 print>>fichierin, "<td class=\"erreur\"> -</td></tr>"
164
165
166 #--------S'il n'y a pas d'erreurs-------#
167
168 else:
169
170 flux.close() #fermeture de flux
171
172 print "done ! - http code : %s" %(codehttp)
173
174
175 pageaspiree=open("./PAGES-ASPIREES/"+filename+"/page"+str(i)+".html", "w" ) #ecrire le contenu de la page aspiree
176 print>>pageaspiree, contenuhtml
177 pageaspiree.close()
178
179 #3eme et 4eme colonnes
180 print>>fichierin, "<td><a href=\"../PAGES-ASPIREES/"+filename+"/page"+str(i)+".html\">page "+str(i)+"</a></td>"
181 print>>fichierin, "<td>%d</td>" %(codehttp)
182
183
184 #----------Detection d'encodage --------#
185
186 encodage=""
187
188 #-----Detection d'encodage dans le code source -------#
189
190 if re.findall(r'< ?meta.*charset.*?/?>', contenuhtml):
191 encodage=re.findall(r'< ?meta.*charset.*?/?>', contenuhtml)
192 encodage= re.findall(r'charset ?= ?[^/>]*', encodage[0])
193 encodage=encodage[0].split("=")
194 encodage= (re.findall(r'[\w-]+[^ \"\']', encodage[1]))[-1]
195
196
197 #---------Si l'encodage et de l'utf-8---------#
198
199 if encodage=="utf-8" or encodage=="UTF-8":
200
201 dumptextutf8 = nltk.clean_html(contenuhtml)
202
203 dumptext=open("./DUMP-TEXT/"+filename+"/page"+str(i)+"utf8.txt", "w" )
204 print>>dumptext, dumptextutf8
205 dumptext.close()
206
207 #5eme et 6eme colonnes
208 print>>fichierin, "<td> --> </td>"
209 print>>fichierin, "<td><a href=\"../DUMP-TEXT/"+filename+"/page"+str(i)+"utf8.txt\">page "+str(i)+"</a></td>"
210
211 else:
212
213
214 #--------Dump plain text (intial)-------#
215
216
217 plaintext = nltk.clean_html(contenuhtml)
218
219 dumptext=open("./DUMP-TEXT/"+filename+"/page"+str(i)+".txt", "w" )
220 print>>dumptext, plaintext
221 dumptext.close()
222
223
224
225 #-------Conversion en utf-8-------#
226
227 newContent=UnicodeDammit(plaintext,[encodage, "windows-1256", "iso-8859-7"] )
228 text= newContent.unicode_markup
229 dumptextutf8=text.encode('utf-8')
230
231 dumptext=open("./DUMP-TEXT/"+filename+"/page"+str(i)+"utf8.txt", "w")
232 print>>dumptext, dumptextutf8
233 dumptext.close()
234
235 #5eme colonnes
236 print>>fichierin,"<td><a href=\"../DUMP-TEXT/"+filename+"/page"+str(i)+".txt\">"
237
238 if encodage=="":
239 print>>fichierin,"BS4: "+newContent.original_encoding+"</a></td>"#6eme colonne
240 else:
241 print>>fichierin,encodage+"</a></td>"
242 print>>fichierin, "<td><a href=\"../DUMP-TEXT/"+filename+"/page"+str(i)+"utf8.txt\">page "+str(i)+"</a></td>"#7eme colonne
243
244
245 #-------Extraction des contextes---------#
246
247 fichierdmp=open("./DUMP-TEXT/"+filename+"/page"+str(i)+"utf8.txt", "r")
248 conx=fichierdmp.read()
249 fichierdmp.close()
250
251
252 conx=conx.decode("utf-8",errors="ignore") #decoder le texte
253 conx=conx.replace("#","") #supprimer les croisillons
254
255 expr = re.compile(ur"(?:([^\t\n]{,150})(([Aa]m.ricani[sz].*?\W)|(\W[Aα]μερικανοπο[ίι].*?\W)|(εξαμερ[ίι]καν[ίι]σ.*?\W)|(\W[Aα]μερικανισμός))([^\t\n]{,150}\W))", re.I) #motif pour les contextes
256
257 res=re.findall(expr,conx)#chercher les contextes selon le motif ci-dessus
258
259 fichiercontextes=open("./CONTEXTES/"+filename+"/page"+str(i)+".txt", "w")
260
261 #ecrire les contextes dans le fichier contextes.txt
262 for found in res:
263 print>>fichiercontextes, found[0].encode("utf-8")+found[1].encode("utf-8")+found[6].encode("utf-8")+"#"
264
265 fichiercontextes.close()
266
267 print>>fichierin, "<td><a href=\"../CONTEXTES/"+filename+"/page"+str(i)+".txt\">page "+str(i)+"</a></td>"
268
269 #--------Contextes html--------#
270
271
272 os.system("perl ./PROGRAMMES/minigrepMultilingue2/minigrepmultilingue.pl UTF-8 ./DUMP-TEXT/"+filename+"/page"+str(i)+"utf8.txt ./PROGRAMMES/minigrepMultilingue2/motif.txt")
273 os.rename("resultat-extraction.html", "./CONTEXTES/"+filename+"/page"+str(i)+"utf8.html")
274
275 print>>fichierin, "<td><a href=\"../CONTEXTES/"+filename+"/page"+str(i)+"utf8.html\">page "+str(i)+"</a></td>"
276
277
278 #-------Nombre d'occurrences------#
279
280 nboccs=0
281
282 nboccs=len(re.findall(ur"(\W[Aα]μερικανισμός\W)|(\W[Aa]m.ricani[sz].*?\W)|(\Wεξαμερ[ίι]καν[ίι]σ.*?\W)|(?:\W[Aα]μερικανοπο[ίι].*?\W)",conx, re.I))
283
284 print>>fichierin, "<td>"+str(nboccs)+"</td></tr>"
285
286
287 #---------Dump Global--------#
288
289 acopier=open("./DUMP-TEXT/"+filename+"/page"+str(i)+"utf8.txt", "r")
290 pourDG=acopier.read()
291 acopier.close()
292
293
294 dumpsglobaux=open("./FICHIERSGLOBAUX/"+filename+"/DumpsGlobauxPYTHON.txt", "a")
295 dumpsglobaux.write(pourDG)
296 dumpsglobaux.close()
297
298 if pourDG!="":
299 countglobal+=1
300
301
302 #-------Contextes Globaux------#
303
304 acopier=open("./CONTEXTES/"+filename+"/page"+str(i)+".txt", "r")
305 pourCG=acopier.read()
306 acopier.close()
307
308
309 contextesglobaux=open("./FICHIERSGLOBAUX/"+filename+"/ContextesGlobauxPYTHON.txt", "a")
310 contextesglobaux.write(str(pourCG))
311 contextesglobaux.close()
312
313
314
315 i+=1
316
317 fichierout.close()
318
319 #------FIN de la boucle : lire le contenu du fichier URL-----#
320
321
322 print>>fichierin, "<tr class=\"final\"><td></td><td></td><td></td><td></td><td></td>"
323 print>>fichierin, "<td class=\"finalglob\"><a href=\"../FICHIERSGLOBAUX/"+filename+"/DumpsGlobauxPYTHON.txt\">Dump Global("+str(countglobal)+")</a></td>"
324 print>>fichierin, "<td class=\"finalglob\"><a href=\"../FICHIERSGLOBAUX/"+filename+"/ContextesGlobauxPYTHON.txt\">Contextes Globaux("+str(countglobal)+")</a></td><td></td><td></td></tr>"
325
326 print>>fichierin,"</table><br><br>"
327
328
329
330 #refermer les balises ouvertes au début du fichierin.html
331 print>>fichierin, "</body></html>"
332
333 fichierin.close()
334
335
336
Tableaux
[ Haut de la page ]
Bilan
[ Haut de la page ]
Les problèmes:
-
L'aspiration : l'aspiration d'un site est à priori très facile avec python et suit la même méthode que la lecture d'un fichier. Cependant, il est important de gérer les erreurs, surtout avec un nombre de pages important. Afin de gérer le problème de certains sites qui prennent trop de temps à être aspirés et qui bloquent l'avancement du programme, il fallait utiliser urllib2, qui permet un temps limite. Urllib2 gère les exceptions un peu différemment et par défaut les renvoie vers STDOUT. Il fallait alors prévoir chaque sorte d'erreur possible afin que le programme puisse continuer.
La détection d'encodage : Comme avec Bash, la détection d'encodage était problématique. Chardet était un outil intéressant puisqu'il donnait un niveau d'assurance de sa détection d'encodage. Mais ceci provoquait encore plus de problèmes, puisqu'il renvoyait TOUJOURS un résultat. De plus, certains encodages très répandus ne pouvaient simplement pas être reconnus par chardet, renvoyant un grand nombre de fichiers mal encodés. UnicodeDammit par BeautifulSoup fournissait plus d'options, mais était dépendant d'une liste d'encodages possibles afin de détecter suffisamment bien l'encodage. Les deux niveaux d'encodages du script Bash était alors unifiés dans un seul niveau en python : le charset déclaré dans le code source fournissant un encodage possible à BeautifulSoup.
L'extraction des contextes : Le module « re » en Python fournit beaucoup de possibilités. Les parenthèses permettent de sélectionner certains groupes dans le motif, renvoyés en forme de tuple. Il était nécessaire de gérer le format du résultat, en sélectionnant les bons éléments du tuple pour reconstituer la phrase.
Les avantages par rapport à Bash :
-
L'aspiration des pages en Bash nécessitait une bonne connexion internet. La plupart du temps, la moitié des pages n'ont pas été aspirées, ce qui nuisait considérablement à l'extraction de contextes pertinents. Pourtant, avec la même connexion, l'aspiration en python n'était jamais affectée de la même manière.
-
Python offre plus d'options pour la gestion des erreurs, même si pour ce projet la gestion des erreurs d'aspiration ne nécessitait pas un traitement si précis.
Les inconvénients par rapport à Bash :
-
La détection d'encodage était plus problématique avec python, puisque chardet ne supporte pas autant d'encodages que « file ». Pour notre tâche avec un grand nombre de pages, la mauvaise détection d'encodage avait des conséquences importantes pour le traitement du texte, rendant un certain nombre de textes inutilisables.
-
La commande nltk.clean_html était très utile pour extraire le texte brut, mais contrairement à lynx -dump de Bash, elle laissait vide de grandes parties du fichier, là où il y avait précédemment des balises. Ceci rend les fichiers moins lisibles que les fichiers en Bash.
-
Le programme « minigrep » a produit des résultats différents pour les programmes en Bash et en Python. Tandis qu'en Bash les contextes sont limités à une ligne, en python, la ligne de texte est beaucoup plus longue, comportant tout un paragraphe. Il faudrait trouver un moyen de limiter la longueur d'une ligne à l'intérieur du programme « minigrep » ou la longueur de la ligne lue en python. Heureusement, ce n'est pas un problème pour l'extraction des contextes en format texte, ce qui était utilisé pour la phase 2 du projet.