Analyser des données de format fixe incorporées dans HTML en python
-
03-07-2019 - |
Question
J'utilise l'API Appengine de Google
from google.appengine.api import urlfetch
pour récupérer une page Web. Le résultat de
result = urlfetch.fetch("http://www.example.com/index.html")
est une chaîne du contenu HTML (dans result.content). Le problème est que les données que je veux analyser ne sont pas vraiment au format HTML. Je ne pense donc pas que l’utilisation d’un analyseur HTML python fonctionne pour moi. Je dois analyser tout le texte brut dans le corps du document HTML. Le seul problème est que urlfetch renvoie une seule chaîne de l'intégralité du document HTML, supprimant ainsi toutes les lignes et les espaces.
MODIFIER: D'accord, j'ai essayé de chercher une URL différente et apparemment, urlfetch ne supprime pas les nouvelles lignes, c'est la page Web originale que j'essayais d'analyser qui servait le fichier HTML de cette façon ... END EDIT
Si le document ressemble à ceci:
<html><head></head><body>
AAA 123 888 2008-10-30 ABC
BBB 987 332 2009-01-02 JSE
...
A4A 288 AAA
</body></html>
result.content sera ceci, après que urlfetch le récupère:
'<html><head></head><body>AAA 123 888 2008-10-30 ABCBBB 987 2009-01-02 JSE...A4A 288 AAA</body></html>'
L'utilisation d'un analyseur HTML ne m'aide pas avec les données entre les balises body. J'allais donc utiliser des expressions régulières pour analyser mes données, mais comme vous pouvez le voir, la dernière partie d'une ligne est combinée à la première partie de la ligne suivante, et je ne sais pas comment la scinder. J'ai essayé
result.content.split('\n')
et
result.content.split('\r')
mais la liste résultante ne comportait qu'un seul élément. Je ne vois aucune option dans la fonction urlfetch de Google pour ne pas supprimer les nouvelles lignes.
Avez-vous des idées pour analyser ces données? Peut-être dois-je aller le chercher différemment?
Merci d'avance!
La solution
Je comprends que le format du document est celui que vous avez posté. Dans ce cas, je conviens qu'un analyseur tel que Beautiful Soup peut ne pas être une bonne solution.
Je suppose que vous obtenez déjà les données intéressantes (entre les balises BODY) avec une expression régulière comme
import re
data = re.findall('<body>([^\<]*)</body>', result)[0]
alors, cela devrait être aussi simple que:
start = 0
end = 5
while (end<len(data)):
print data[start:end]
start = end+1
end = end+5
print data[start:]
(note: je n'ai pas comparé ce code avec des cas de limites, et je m'attends à ce qu'il échoue. Il n'est qu'ici pour montrer l'idée générique)
Autres conseils
La seule suggestion à laquelle je puisse penser est de l’analyser comme si elle avait des colonnes de largeur fixe. Les nouvelles lignes ne sont pas prises en compte pour HTML.
Si vous avez le contrôle des données source, placez-les dans un fichier texte plutôt que HTML.
Une fois que vous avez le corps du texte en une seule et longue chaîne, vous pouvez le séparer comme suit. Cela suppose que chaque enregistrement comporte 26 caractères.
body= "AAA 123 888 2008-10-30 ABCBBB 987 2009-01-02 JSE...A4A 288 AAA"
for i in range(0,len(body),26):
line= body[i:i+26]
# parse the line
EDIT: La compréhension en lecture est une chose souhaitable. J'ai manqué le point sur le fait que les lignes fonctionnent ensemble sans séparateur entre elles, ce qui serait un peu le but de tout cela, n'est-ce pas? Donc, tant pis, ma réponse, ce n'est pas vraiment pertinent.
Si vous savez que chaque ligne est composée de 5 colonnes séparées par des espaces, alors (une fois que vous aurez supprimé le code HTML), vous pourrez faire quelque chose comme (non testé):
def generate_lines(datastring):
while datastring:
splitresult = datastring.split(' ', 5)
if len(splitresult) >= 5:
datastring = splitresult[5]
else:
datastring = None
yield splitresult[:5]
for line in generate_lines(data):
process_data_line(line)
Bien sûr, vous pouvez modifier le caractère de division et le nombre de colonnes selon vos besoins (éventuellement en les transmettant à la fonction de générateur en tant que paramètres supplémentaires), et ajouter le traitement des erreurs, le cas échéant.
Autres suggestions pour fractionner la chaîne s
en blocs de 26 caractères:
En tant que liste:
>>> [s[x:x+26] for x in range(0, len(s), 26)]
['AAA 123 888 2008-10-30 ABC',
'BBB 987 2009-01-02 JSE',
'A4A 288 AAA']
En tant que générateur:
>>> for line in (s[x:x+26] for x in range(0, len(s), 26)): print line
AAA 123 888 2008-10-30 ABC
BBB 987 2009-01-02 JSE
A4A 288 AAA
Remplacez range ()
par xrange ()
dans Python 2.x si s
est très long.