He hecho algunas investigaciones sobre esto en el pasado y terminé implementando this approach [pdf] en Python. La versión final que implementé también realizó algunas tareas de limpieza antes de aplicar el algoritmo, como eliminar elementos de cabecera/script/iframe, elementos ocultos, etc., pero este era el núcleo de la misma.
Aquí hay una función con una implementación (muy) ingenua del discriminador de "lista de enlaces", que intenta eliminar elementos con una fuerte relación de enlace a texto (es decir, barras de navegación, menús, anuncios, etc.):
def link_list_discriminator(html, min_links=2, ratio=0.5):
"""Remove blocks with a high link to text ratio.
These are typically navigation elements.
Based on an algorithm described in:
http://www.psl.cs.columbia.edu/crunch/WWWJ.pdf
:param html: ElementTree object.
:param min_links: Minimum number of links inside an element
before considering a block for deletion.
:param ratio: Ratio of link text to all text before an element is considered
for deletion.
"""
def collapse(strings):
return u''.join(filter(None, (text.strip() for text in strings)))
# FIXME: This doesn't account for top-level text...
for el in html.xpath('//*'):
anchor_text = el.xpath('.//a//text()')
anchor_count = len(anchor_text)
anchor_text = collapse(anchor_text)
text = collapse(el.xpath('.//text()'))
anchors = float(len(anchor_text))
all = float(len(text))
if anchor_count > min_links and all and anchors/all > ratio:
el.drop_tree()
En el corpus de prueba que usé, en realidad funcionó bastante bien, pero lograr una alta confiabilidad requerirá muchos ajustes.
gracias, respuesta actualizada –