2011-11-30 20 views
9

Estoy tratando de raspar un discurso de un sitio web usando BeautifulSoup. Sin embargo, estoy teniendo problemas, ya que el discurso está dividido en muchos párrafos diferentes. Soy extremadamente nuevo en la programación y tengo problemas para encontrar la manera de lidiar con esto. El HTML de la página es el siguiente:Raspado con BeautifulSoup y varios párrafos

<span class="displaytext">Thank you very much. Mr. Speaker, Vice President Cheney, 
Members of Congress, distinguished guests, fellow citizens: As we gather tonight, our Nation is  
at war; our economy is in recession; and the civilized world faces unprecedented dangers. 
Yet, the state of our Union has never been stronger. 
<p>We last met in an hour of shock and suffering. In 4 short months, our Nation has comforted the victims, 
begun to rebuild New York and the Pentagon, rallied a great coalition, captured, arrested, and 
rid the world of thousands of terrorists, destroyed Afghanistan's terrorist training camps, 
saved a people from starvation, and freed a country from brutal oppression. 
<p>The American flag flies again over our Embassy in Kabul. Terrorists who once occupied 
Afghanistan now occupy cells at Guantanamo Bay. And terrorist leaders who urged followers to 
sacrifice their lives are running for their own. 

Se continúa así durante un tiempo, con múltiples etiquetas de párrafo. Estoy tratando de extraer todo el texto dentro del lapso.

He intentado un par de maneras diferentes de obtener el texto, pero ninguno ha podido obtener el texto que quiero.

La primera Probé es:

import urllib2,sys 
from BeautifulSoup import BeautifulSoup, NavigableString 

address = 'http://www.presidency.ucsb.edu/ws/index.php?pid=29644&st=&st1=#axzz1fD98kGZW' 
html = urllib2.urlopen(address).read() 

soup = BeautifulSoup(html) 
thespan = soup.find('span', attrs={'class': 'displaytext'}) 
print thespan.string 

que me da:

señor presidente, el vicepresidente Cheney, miembros del Congreso, distinguidos invitados, conciudadanos: Al reunirnos esta noche, nuestra Nación está en guerra; nuestra economía está en recesión; y el mundo civilizado enfrenta peligros sin precedentes. Sin embargo, el estado de nuestra Unión nunca ha sido más fuerte.

Esa es la porción del texto hasta la etiqueta del primer párrafo. Luego intenté:

import urllib2,sys 
from BeautifulSoup import BeautifulSoup, NavigableString 

address = 'http://www.presidency.ucsb.edu/ws/index.php?pid=29644&st=&st1=#axzz1fD98kGZW' 
html = urllib2.urlopen(address).read() 

soup = BeautifulSoup(html) 
thespan = soup.find('span', attrs={'class': 'displaytext'}) 
for section in thespan: 
    paragraph = section.findNext('p') 
    if paragraph and paragraph.string: 
     print '>', paragraph.string 
    else: 
     print '>', section.parent.next.next.strip() 

Esto me dio el texto entre la primera etiqueta de párrafo y la segunda etiqueta de párrafo. Entonces, estoy buscando una manera de obtener el texto completo, en lugar de solo secciones.

Respuesta

8
import urllib2,sys 
from BeautifulSoup import BeautifulSoup 

address = 'http://www.presidency.ucsb.edu/ws/index.php?pid=29644&st=&st1=#axzz1fD98kGZW' 
soup = BeautifulSoup(urllib2.urlopen(address).read()) 

span = soup.find("span", {"class":"displaytext"}) # span.string gives you the first bit 
paras = [x.contents[0] for x in span.findAllNext("p")] # this gives you the rest 
# use .contents[0] instead of .string to deal with last para that's not well formed 

print "%s\n\n%s" % (span.string, "\n\n".join(paras)) 

Como se señaló en los comentarios, lo anterior no funciona tan bien si los <p> etiquetas contienen etiquetas anidadas más. Esto puede ser tratado usando:

paras = ["".join(x.findAll(text=True)) for x in span.findAllNext("p")] 

Sin embargo, que no funciona demasiado bien con la última <p> que no tiene una etiqueta de cierre. Una solución hacky sería tratar eso de manera diferente. Por ejemplo:

import urllib2,sys 
from BeautifulSoup import BeautifulSoup 

address = 'http://www.presidency.ucsb.edu/ws/index.php?pid=29644&st=&st1=#axzz1fD98kGZW' 
soup = BeautifulSoup(urllib2.urlopen(address).read()) 
span = soup.find("span", {"class":"displaytext"}) 
paras = [x for x in span.findAllNext("p")] 

start = span.string 
middle = "\n\n".join(["".join(x.findAll(text=True)) for x in paras[:-1]]) 
last = paras[-1].contents[0] 
print "%s\n\n%s\n\n%s" % (start, middle, last) 
+0

Esto no funciona con la página web vinculada a en la pregunta (es decir, solo imprimirá el primer párrafo, no el discurso completo). – ekhumoro

+0

@ekhumoro corregido –

+0

@ShawnChin ¡Muchas gracias! Eso funcionó perfectamente. – user1074057

2

Así es como se haría con lxml:

import lxml.html as lh 

tree = lh.parse('http://www.presidency.ucsb.edu/ws/index.php?pid=29644&st=&st1=#axzz1fD98kGZW') 

text = tree.xpath("//span[@class='displaytext']")[0].text_content() 

Como alternativa, las respuestas a esta pregunta abarca la forma de lograr lo mismo usando BeautifulSoup: BeautifulSoup - easy way to to obtain HTML-free contents

La función auxiliar de la respuesta aceptada:

def textOf(soup): 
    return u''.join(soup.findAll(text=True)) 
+1

Tal vez vamos a la op sabe por qué lxml es una buena alternativa a BeautifulSoup :) –

+0

Ninguna de estas sugerencias se producirá la salida solicitada en la pregunta. – ekhumoro

+0

@ekhumoro, ¿podría explicar de qué manera mi solución no produce el resultado deseado? El OP quiere '' ... extraer todo el texto dentro del lapso "', y eso es lo que hace el código anterior. – Acorn

0

Usted debe tratar:

soup.span.renderContents() 
+0

'.renderContents()' no hace lo que el OP quiere. No elimina las etiquetas de párrafo. – Acorn

Cuestiones relacionadas