Como posso analisar html com html5lib e consultar o html analisado com xpath?
Pergunta
Estou tentando usar o html5lib para analisar uma página HTML em algo que posso consultar com o XPath. HTML5LIB tem quase zero documentação e passei muito tempo tentando descobrir esse problema. O objetivo final é retirar a segunda fila de uma mesa:
<html>
<table>
<tr><td>Header</td></tr>
<tr><td>Want This</td></tr>
</table>
</html>
Então vamos tentar:
>>> doc = html5lib.parse('<html><table><tr><td>Header</td></tr><tr><td>Want This</td> </tr></table></html>', treebuilder='lxml')
>>> doc
<lxml.etree._ElementTree object at 0x1a1c290>
Isso parece bom, vamos ver o que mais temos:
>>> root = doc.getroot()
>>> print(lxml.etree.tostring(root))
<html:html xmlns:html="http://www.w3.org/1999/xhtml"><html:head/><html:body><html:table><html:tbody><html:tr><html:td>Header</html:td></html:tr><html:tr><html:td>Want This</html:td></html:tr></html:tbody></html:table></html:body></html:html>
Lol wut?
a sério. Eu estava planejando usar algum XPath para obter os dados que quero, mas isso não parece funcionar. Então o que eu posso fazer? Estou disposto a experimentar diferentes bibliotecas e abordagens.
Solução
A falta de documentação é um bom motivo para evitar uma biblioteca IMO, por mais legal que seja. Você está casado para usar o html5lib? Você já olhou lxml.html?
Aqui está uma maneira de fazer isso com o LXML:
from lxml import html
tree = html.fromstring(text)
[td.text for td in tree.xpath("//td")]
Resultado:
['Header', 'Want This']
Outras dicas
O que você quer usar é o namespaceHTMLElements
argumento, que por algum motivo o inadimplente é verdadeiro.
doc = html5lib.parse('''<html>
<table>
<tr><td>Header</td></tr>
<tr><td>Want This</td></tr>
</table>
</html>
''', treebuilder='lxml', namespaceHTMLElements=False)
print lxml.html.tostring(doc)
Provavelmente ainda é mais fácil usar lxml.html.
Eu sempre recomendo experimentar lxml
biblioteca. É incrivelmente rápido e tem muitos recursos.
Ele também tem suporte ao analisador HTML5LIB se precisar disso: html5Parser
>>> from lxml.html import fromstring, tostring
>>> html = """
... <html>
... <table>
... <tr><td>Header</td></tr>
... <tr><td>Want This</td></tr>
... </table>
... </html>
... """
>>> doc = fromstring(html)
>>> tr = doc.cssselect('table tr')[1]
>>> print tostring(tr)
<tr><td>Want This</td></tr>
Com Belo grupo, você pode fazer isso com
>>> soup = BeautifulSoup.BeautifulSoup('<html><table><tr><td>Header</td></tr><tr><td>Want This</td></tr></table></html>')
>>> soup.findAll('td')[1].string
u'Want This'
>>> soup.findAll('tr')[1].td.string
u'Want This'
(Obviamente, esse é um exemplo realmente grosseiro, mas sim.)
Eu acredito que você pode pesquisar CSS em objetos lxml .. como assim
elements = root.cssselect('div.content')
data = elements[0].text
Como o HTML5LIB (por padrão) cria árvores que contêm informações de namespace (corretas) que você especificar (a direita) também em suas consultas.
Exemplo com uma consulta XPath:
import html5lib
inp='''<html>
<table>
<tr><td>Header</td></tr>
<tr><td>Want This</td></tr>
</table>
</html>'''
xns = '{http://www.w3.org/1999/xhtml}'
d = html5lib.parse(inp)
s = d.findall('.//{}td'.format(xns))[-1].text
print(s)
Resultado:
Want This
O mesmo resultado sem xpath:
s = d.find(xns+'body').find(xns+'table').find(xns+'tbody') \
.findall(xns+'tr')[-1].find(xns+'td').text
Como alternativa, você também pode informar ao HTML5LIB para evitar adicionar qualquer informação para o nome de nome durante a análise:
d = html5lib.parse(inp, namespaceHTMLElements=False)
s = d.findall('.//td')[-1].text
print(s)
Resultado:
Want This
Tente usar o jQuery. E você pode recuperar todos os elementos. Como alternativa, você pode colocar um ID em sua linha e puxá -la para fora.
1) ... ...
$ ("td") [1] .innerhtml será o que você quer
2) ... ...
$ ("#blá"). Texto () será o que você quer