¿Cómo extraigo mis datos necesarios de archivo HTML?
-
05-09-2019 - |
Pregunta
Este es el código HTML que tengo:
p_tags = '''<p class="foo-body">
<font class="test-proof">Full name</font> Foobar<br />
<font class="test-proof">Born</font> July 7, 1923, foo, bar<br />
<font class="test-proof">Current age</font> 27 years 226 days<br />
<font class="test-proof">Major teams</font> <span style="white-space: nowrap">Japan,</span> <span style="white-space: nowrap">Jakarta,</span> <span style="white-space: nowrap">bazz,</span> <span style="white-space: nowrap">foo,</span> <span style="white-space: nowrap">foobazz</span><br />
<font class="test-proof">Also</font> bar<br />
<font class="test-proof">foo style</font> hand <br />
<font class="test-proof">bar style</font> ball<br />
<font class="test-proof">foo position</font> bak<br />
<br class="bar" />
</p>'''
Este es mi código Python, utilizando sopa Hermosa:
def get_info(p_tags):
"""Returns brief information."""
head_list = []
detail_list = []
# This works fine
for head in p_tags.findAll('font', 'test-proof'):
head_list.append(head.contents[0])
# Some problem with this?
for index in xrange(2, 30, 4):
detail_list.append(p_tags.contents[index])
return dict([(l, detail_list[head_list.index(l)]) for l in head_list])
Me da la head_list
adecuada desde el HTML, pero la detail_list
no está funcionando.
head_list = [u'Full name', u'Born', u'Current age', u'Major teams', u'Also', u'foo style', u'bar style', u'foo position']
Yo quería algo como esto
{ 'Full name': 'Foobar', 'Born': 'July 7, 1923, foo, bar', 'Current age': '78 years 226 days', 'Major teams': 'Japan, Jakarta, bazz, foo, foobazz', 'Also': 'bar', 'foo style': 'hand', 'bar style': 'ball', 'foo position': 'bak' }
Cualquier ayuda sería apreciable. Gracias de antemano.
Solución
Lo siento por el código innecesariamente compleja, que tienen gran necesidad de una gran dosis de cafeína;)
import re
str = """<p class="foo-body">
<font class="test-proof">Full name</font> Foobar<br />
<font class="test-proof">Born</font> July 7, 1923, foo, bar<br />
<font class="test-proof">Current age</font> 27 years 226 days<br />
<font class="test-proof">Major teams</font> <span style="white-space: nowrap">Japan,</span> <span style="white-space: nowrap">Jakarta,</span> <span style="white-space: nowrap">bazz,</span> <span style="white-space: nowrap">foo,</span> <span style="white-space: nowrap">foobazz</span><br />
<font class="test-proof">Also</font> bar<br />
<font class="test-proof">foo style</font> hand <br />
<font class="test-proof">bar style</font> ball<br />
<font class="test-proof">foo position</font> bak<br />
<br class="bar" />
</p>"""
R_EXTRACT_DATA = re.compile("<font\s[^>]*>[\s]*(.*?)[\s]*</font>[\s]*(.*?)[\s]*<br />", re.IGNORECASE)
R_STRIP_TAGS = re.compile("<span\s[^>]*>|</span>", re.IGNORECASE)
def strip_tags(str):
"""Strip un-necessary <span> tags
"""
return R_STRIP_TAGS.sub("", str)
def get_info(str):
"""Extract useful info from the given string
"""
data = R_EXTRACT_DATA.findall(str)
data_dict = {}
for x in [(x[0], strip_tags(x[1])) for x in data]:
data_dict[x[0]] = x[1]
return data_dict
print get_info(str)
Otros consejos
Empecé a responder a esta antes de comprender que estaba usando 'Beautiful Soup', pero aquí hay un analizador que creo que funciona con su ejemplo de cadena escritos utilizando la biblioteca HTMLParser
from HTMLParser import HTMLParser
results = {}
class myParse(HTMLParser):
def __init__(self):
self.state = ""
HTMLParser.__init__(self)
def handle_starttag(self, tag, attrs):
attrs = dict(attrs)
if tag == "font" and attrs.has_key("class") and attrs['class'] == "test-proof":
self.state = "getKey"
def handle_endtag(self, tag):
if self.state == "getKey" and tag == "font":
self.state = "getValue"
def handle_data(self, data):
data = data.strip()
if not data:
return
if self.state == "getKey":
self.resultsKey = data
elif self.state == "getValue":
if results.has_key(self.resultsKey):
results[self.resultsKey] += " " + data
else:
results[self.resultsKey] = data
if __name__ == "__main__":
p_tags = """<p class="foo-body"> <font class="test-proof">Full name</font> Foobar<br /> <font class="test-proof">Born</font> July 7, 1923, foo, bar<br /> <font class="test-proof">Current age</font> 27 years 226 days<br /> <font class="test-proof">Major teams</font> <span style="white-space: nowrap">Japan,</span> <span style="white-space: nowrap">Jakarta,</span> <span style="white-space: nowrap">bazz,</span> <span style="white-space: nowrap">foo,</span> <span style="white-space: nowrap">foobazz</span><br /> <font class="test-proof">Also</font> bar<br /> <font class="test-proof">foo style</font> hand <br /> <font class="test-proof">bar style</font> ball<br /> <font class="test-proof">foo position</font> bak<br /> <br class="bar" /></p>"""
parser = myParse()
parser.feed(p_tags)
print results
da el resultado:
{'foo position': 'bak',
'Major teams': 'Japan, Jakarta, bazz, foo, foobazz',
'Also': 'bar',
'Current age': '27 years 226 days',
'Born': 'July 7, 1923, foo, bar' ,
'foo style': 'hand',
'bar style': 'ball',
'Full name': 'Foobar'}
El problema es que el HTML no está muy bien pensado - que tiene un "modelo de contenido mixto", donde se intercalan sus etiquetas y sus datos. Sus etiquetas son envueltos en <font>
las etiquetas, pero los datos están en nodos NavigableString.
Es necesario iterar sobre el contenido de p_tag
. Habrá dos tipos de nodos:. Tag
nodos (que tienen sus etiquetas <font>
) y nodos NavigableString
que tienen los otros fragmentos de texto
from beautifulsoup import *
label_value_pairs = []
for n in p_tag.contents:
if isinstance(n,Tag) and tag == "font"
label= n.string
elif isinstance(n, NavigableString):
value= n.string
label_value_pairs.append( label, value )
else:
# Generally tag == "br"
pass
print dict( label_value_pairs )
Algo aproximadamente así.
Usted quiere encontrar las cadenas precedidos por> y seguido por <, haciendo caso omiso al final, o llevando los espacios en blanco. Esto se puede hacer muy fácilmente con un bucle mirando cada carácter de la cadena, o expresiones regulares podría ayudar. Algo parecido a> [\ t] * [^ <] + [\ t] * <.
También es posible usar re.split y una regex que representa el contenido de la etiqueta, algo así como <[^>] *> como el divisor, obtendrá algunas entradas vacías de la matriz, pero estos se eliminan fácilmente.