Análisis de datos de formato fijo incrustados en HTML en Python
-
03-07-2019 - |
Pregunta
Estoy usando la app de Google App
from google.appengine.api import urlfetch
para obtener una página web. El resultado de
result = urlfetch.fetch("http://www.example.com/index.html")
es una cadena del contenido html (en result.content). El problema es que los datos que quiero analizar no están realmente en formato HTML, por lo que no creo que usar un analizador HTML de Python funcione para mí. Necesito analizar todo el texto sin formato en el cuerpo del documento html. El único problema es que urlfetch devuelve una sola cadena de todo el documento HTML, eliminando todas las nuevas líneas y espacios adicionales.
EDITAR: De acuerdo, intenté obtener una URL diferente y, al parecer, urlfetch no elimina las nuevas líneas, fue la página web original que intenté analizar la que sirvió al archivo HTML de esa manera ... EDITAR FINAL
Si el documento es algo como esto:
<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 será esto, después de que urlfetch lo busque:
'<html><head></head><body>AAA 123 888 2008-10-30 ABCBBB 987 2009-01-02 JSE...A4A 288 AAA</body></html>'
El uso de un analizador HTML no me ayudará con los datos entre las etiquetas del cuerpo, por lo que iba a utilizar expresiones regulares para analizar mis datos, pero como puede ver, la última parte de una línea se combina con la primera parte de La siguiente línea, y no sé cómo dividirla. Lo intenté
result.content.split('\n')
y
result.content.split('\r')
pero la lista resultante era solo 1 elemento. No veo ninguna opción en la función urlfetch de Google para no eliminar nuevas líneas.
¿Alguna idea de cómo puedo analizar estos datos? Tal vez necesito buscarlo de manera diferente?
Gracias de antemano!
Solución
Entiendo que el formato del documento es el que usted ha publicado. En ese caso, estoy de acuerdo en que un analizador como Beautiful Soup puede no ser una buena solución.
Supongo que ya está obteniendo los datos interesantes (entre las etiquetas BODY) con una expresión regular como
import re
data = re.findall('<body>([^\<]*)</body>', result)[0]
entonces, debería ser tan fácil como:
start = 0
end = 5
while (end<len(data)):
print data[start:end]
start = end+1
end = end+5
print data[start:]
(nota: no verifiqué este código en los casos de límites y espero que falle. Solo está aquí para mostrar la idea genérica)
Otros consejos
La única sugerencia que se me ocurre es analizarla como si tuviera columnas de ancho fijo. Las nuevas líneas no se toman en consideración para HTML.
Si tiene el control de los datos de origen, póngalos en un archivo de texto en lugar de HTML.
Una vez que tenga el texto del cuerpo como una sola cadena larga, puede dividirlo de la siguiente manera. Esto supone que cada registro tiene 26 caracteres.
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
EDITAR: La comprensión de lectura es algo deseable. Me perdí la parte de las líneas que se ejecutan juntas sin ningún separador entre ellas, ¿cuál sería el punto central de esto, verdad? Entonces, no importa mi respuesta, no es realmente relevante.
Si sabe que cada línea tiene 5 columnas separadas por espacios, entonces (una vez que haya eliminado el html) podría hacer algo como (sin probar):
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)
Por supuesto, puede cambiar el carácter dividido y el número de columnas según sea necesario (posiblemente incluso pasarlas a la función del generador como parámetros adicionales), y agregar el manejo de errores según corresponda.
Otras sugerencias para dividir la cadena s
en bloques de 26 caracteres:
Como una lista:
>>> [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']
Como generador:
>>> 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
Reemplace range ()
con xrange ()
en Python 2.x si s
es muy largo.