¿Por qué BeautifulSoup no puede leer / analizar correctamente este documento RSS (XML)?

YCombinator es lo suficientemente bueno como para proporcionar una fuente RSS y una gran fuente RSS que contiene los elementos principales en HackerNews . Estoy tratando de escribir una secuencia de comandos de Python para acceder al documento de fuente RSS y luego analizar cierta información utilizando BeautifulSoup. Sin embargo, estoy teniendo un comportamiento extraño cuando BeautifulSoup intenta obtener el contenido de cada uno de los elementos.

Aquí hay algunas líneas de muestra de la fuente RSS:

  Hacker Newshttp://news.ycombinator.com/Links for the intellectually curious, ranked by readers.  EFF Patent Project Gets Half-Million-Dollar Boost from Mark Cuban and 'Notch' https://www.eff.org/press/releases/eff-patent-project-gets-half-million-dollar-boost-mark-cuban-and-notch http://news.ycombinator.com/item?id=4944322 <![CDATA[Comments]]>   Two Billion Pixel Photo of Mount Everest (can you find the climbers?) https://s3.amazonaws.com/Gigapans/EBC_Pumori_050112_8bit_FLAT/EBC_Pumori_050112_8bit_FLAT.html http://news.ycombinator.com/item?id=4943361 <![CDATA[Comments]]>  ...   

Aquí está el código que he escrito (en python) para acceder a esta fuente e imprimir el title , el link y los comments de cada elemento:

 import sys import requests from bs4 import BeautifulSoup request = requests.get('http://news.ycombinator.com/rss') soup = BeautifulSoup(request.text) items = soup.find_all('item') for item in items: title = item.find('title').text link = item.find('link').text comments = item.find('comments').text print title + ' - ' + link + ' - ' + comments 

Sin embargo, esta secuencia de comandos da un resultado que se ve así:

 EFF Patent Project Gets Half-Million-Dollar Boost from Mark Cuban and 'Notch' - - http://news.ycombinator.com/item?id=4944322 Two Billion Pixel Photo of Mount Everest (can you find the climbers?) - - http://news.ycombinator.com/item?id=4943361 ... 

Como puede ver, el elemento central, el link , se omite de alguna manera. Es decir, el valor resultante del link es de alguna manera una cadena vacía. Entonces, ¿por qué es eso?

A medida que profundizo en lo que está en la soup , me doy cuenta de que de alguna manera se está ahogando cuando analiza el XML. Esto se puede ver mirando el primer elemento en los items :

 >>> print items[0] EFF Patent Project Gets Half-Million-Dollar Boost from Mark Cuban and 'Notch'https://www.eff.org/press/releases/eff-patent-project-gets-half-million-dollar-boost-mark-cuban-and-notchhttp://news.ycombinator.com/item?id=4944322... 

Notarás que algo raro está sucediendo con solo la etiqueta de link . Solo obtiene la etiqueta de cierre y luego el texto de esa etiqueta después de ella. Este es un comportamiento muy extraño, especialmente en contraste con el title y los comments se analizan sin problemas.

Esto parece ser un problema con BeautifulSoup porque lo que realmente se lee en las solicitudes no tiene ningún problema. No creo que esté limitado a BeautifulSoup, ya que también intenté usar la API xml.etree.ElementTree y surgió el mismo problema (¿se basa BeautifulSoup en esta API?).

¿Alguien sabe por qué sucedería esto o cómo puedo seguir usando BeautifulSoup sin recibir este error?

Nota: finalmente pude obtener lo que quería con xml.dom.minidom, pero esto no parece una biblioteca muy recomendada. Me gustaría continuar usando BeautifulSoup si es posible.

Actualización : estoy en una Mac con OSX 10.8 usando Python 2.7.2 y BS4 4.1.3.

Actualización 2 : tengo lxml y se instaló con pip. Es la versión 3.0.2. En cuanto a libxml, registré en / usr / lib y el que aparece es libxml2.2.dylib. No estoy seguro de cuándo o cómo se instaló.

Wow, gran pregunta. Esto me parece un error en BeautifulSoup. La razón por la que no puede acceder al enlace utilizando soup.find_all('item').link es que, cuando se carga por primera vez el html en BeautifulSoup, hace algo extraño al HTML:

 >>> from bs4 import BeautifulSoup as BS >>> BS(html)   Hacker Newshttp://news.ycombinator.com/Links for the intellectually curious, ranked by readers.  EFF Patent Project Gets Half-Million-Dollar Boost from Mark Cuban and 'No tch' https://www.eff.org/press/releases/eff-patent-project-gets-half-million-d ollar-boost-mark-cuban-and-notch http://news.ycombinator.com/item?id=4944322 Comments]]>   Two Billion Pixel Photo of Mount Everest (can you find the climbers?) <link />https://s3.amazonaws.com/Gigapans/EBC_Pumori_050112_8bit_FLAT/EBC_Pumori_ 050112_8bit_FLAT.html <comments>http://news.ycombinator.com/item?id=4943361</comments> <description>Comments]]></description>  ...   

Mire con cuidado: en realidad, cambió la primera etiqueta a y luego eliminó la etiqueta . No estoy seguro de por qué haría esto, pero sin solucionar el problema en la inicialización de la clase BeautifulSoup.BeautifulSoup , no podrá usarlo por ahora.

Actualizar:

Creo que su mejor apuesta (aunque sea hack-y) por ahora es usar lo siguiente para el link :

 >>> soup.find('item').link.next_sibling u'http://news.ycombinator.com/' 

En realidad, el problema parece estar relacionado con el analizador que está utilizando. Por defecto, se usa uno de HTML. Intente usar soup = BeautifulSoup (request.text, ‘xml’) después de instalar el módulo lxml.

Luego utilizará un analizador XML en lugar de uno HTML y debería estar bien.

Consulte http://www.crummy.com/software/BeautifulSoup/bs4/doc/#installing-a-parser para obtener más información

@Yan Hudon tiene razón. He resuelto el problema con soup = BeautifulSoup(request.text, 'xml')

No creo que haya un error en BeautifulSoup aquí.

Instalé una copia limpia de BS4 4.1.3 en el stock de Apple 2.7.2 de OS X 10.8.2, y todo funcionó como se esperaba. No analiza el como y, por lo tanto, no tiene el problema con el item.find('link') .

También intenté usar el stock xml.etree.ElementTree y xml.etree.cElementTree en 2.7.2, y xml.etree.ElementTree en python.org 3.3.0, para analizar la misma cosa, y de nuevo funcionó bien. Aquí está el código:

 import xml.etree.ElementTree as ET rss = ET.fromstring(x) for channel in rss.findall('channel'): for item in channel.findall('item'): title = item.find('title').text link = item.find('link').text comments = item.find('comments').text print(title) print(link) print(comments) 

Luego instalé lxml 3.0.2 (creo que BS usa lxml si está disponible), usando el /usr/lib/libxml2.2.dylib integrado de Apple (que, según xml2-config --version , es 2.7.8), e hizo las mismas pruebas usando su etree, y usando BS, y una vez más, todo funcionó.

Además de arruinar el , la salida de jdotjdot muestra que BS4 está arruinando el de una manera extraña. El original es este:

 Comments]]> 

Su salida es:

 Comments]]> 

Mi salida de ejecutar su mismo código exacto es:

 Comments]]> 

Entonces, parece que hay un problema mucho más grande aquí. Lo extraño es que le está sucediendo a dos personas diferentes, cuando no está sucediendo en una instalación limpia de la última versión de cualquier cosa.

Eso implica que es un error que se ha solucionado y que solo tengo una versión más reciente de lo que tenía el error, o es algo extraño en la forma en que ambos instalaron algo.

BS4 en sí puede ser descartado, ya que al menos Treebranch tiene 4.1.3 al igual que yo. Aunque, sin saber cómo lo instaló, podría ser un problema con la instalación.

Se puede descartar Python y su etree incorporado, ya que al menos Treebranch tiene el mismo stock de Apple 2.7.2 de OS X 10.8 que yo.

Podría muy bien ser un error con lxml o el libxml subyacente, o la forma en que se instalaron. Sé que jdotjdot tiene lxml 2.3.6, por lo que este podría ser un error que se ha corregido entre 2.3.6 y 3.0.2. De hecho, dado que, de acuerdo con el sitio web de lxml y las notas de cambio para cualquier versión posterior a la 2.3.5, no hay 2.3.6, así que, sea lo que sea, puede haber algún tipo de lanzamiento con errores desde una sucursal cancelada o algo así. … No sé su versión de libxml, o cómo se instaló, o en qué plataforma está, por lo que es difícil de adivinar, pero al menos esto es algo que se puede investigar.