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.

¿Fue útil?

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top