html5lib / lxml примеры для пользователей BeautifulSoup? - PullRequest
1 голос
/ 12 сентября 2010

Я пытаюсь отучить себя от BeautifulSoup, который мне нравится, но, кажется, (агрессивно) не поддерживается. Я пытаюсь работать с html5lib и lxml, но я не могу понять, как использовать операторы "find" и "findall".

Просматривая документы по html5lib, я нашел это для тестовой программы:

import cStringIO

f = cStringIO.StringIO()
f.write("""
  <html>
    <body>
      <table>
       <tr>
          <td>one</td>
          <td>1</td>
       </tr>
       <tr>
          <td>two</td>
          <td>2</td
       </tr>
      </table>
    </body>
  </html>
  """)
f.seek(0)

import html5lib
from html5lib import treebuilders
from lxml import etree  # why?

parser = html5lib.HTMLParser(tree=treebuilders.getTreeBuilder("lxml"))
etree_document = parser.parse(f)

root = etree_document.getroot()

root.find(".//tr")

Но это возвращает None. Я заметил, что если я делаю etree.tostring(root), я получаю все свои данные обратно, но все мои теги начинаются с html (например, <html:table>). Но root.find(".//html:tr") выдает KeyError.

Может ли кто-нибудь вернуть меня на правильный путь?

Ответы [ 5 ]

6 голосов
/ 18 мая 2011

Вы можете отключить пространства имен с помощью этой команды: etree_document = html5lib.parse(t, treebuilder="lxml", namespaceHTMLElements=False)

5 голосов
/ 13 сентября 2010

В общем, используйте lxml.html для HTML. Тогда вам не нужно беспокоиться о создании собственного синтаксического анализатора и о пространствах имен.

>>> import lxml.html as l
>>> doc = """
...    <html><body>
...    <table>
...      <tr>
...        <td>one</td>
...        <td>1</td>
...      </tr>
...      <tr>
...        <td>two</td>
...        <td>2</td
...      </tr>
...    </table>
...    </body></html>"""
>>> doc = l.document_fromstring(doc)
>>> doc.finall('.//tr')
[<Element tr at ...>, <Element tr at ...>] #doctest: +ELLIPSIS

FYI, lxml.html также позволяет вам использовать селекторы CSS, что, я считаю, является более простым синтаксисом.

>>> doc.cssselect('tr')
[<Element tr at ...>, <Element tr at ...>] #doctest: +ELLIPSIS
3 голосов
/ 13 сентября 2010

Похоже, что использование "lxml" html5lib TreeBuilder заставляет html5lib строить дерево в пространстве имен XHTML - что имеет смысл, поскольку lxml - это библиотека XML, а XHTML - это то, как каждый представляет HTML как XML. Вы можете использовать синтаксис qname lxml с методом find(), чтобы сделать что-то вроде:

root.find('.//{http://www.w3.org/1999/xhtml}tr')

Или вы можете использовать полные функции XPath lxml, чтобы сделать что-то вроде:

root.xpath('.//html:tr', namespaces={'html': 'http://www.w3.org/1999/xhtml'})

Документация lxml содержит дополнительную информацию о том, как он использует пространства имен XML.

1 голос
/ 13 июля 2015

Я понимаю, что это старый вопрос, но я пришел сюда в поисках информации, которую не нашел ни в одном другом месте. Я пытался что-то почистить с BeautifulSoup, но он задыхался от какого-то короткого HTML. Парсер html по умолчанию явно менее свободен, чем некоторые другие, которые доступны. Одним из часто предпочитаемых синтаксических анализаторов является lxml, который, как я считаю, производит такой же синтаксический анализ, как и ожидалось для браузеров. BeautifulSoup позволяет вам указать lxml в качестве исходного синтаксического анализатора, но его использование требует немного работы.

Во-первых, вам нужен html5lib И вы также должны установить lxml. Хотя html5lib готов использовать lxml (и некоторые другие библиотеки), они не упакованы вместе. [для пользователей Windows, хотя мне не нравится возиться с зависимостями Win в той степени, в которой я обычно получаю библиотеки, делая копии в том же каталоге, что и мой проект, я настоятельно рекомендую использовать для этого pip; довольно безболезненно; Я думаю, вам нужен доступ администратора.]

Тогда вам нужно написать что-то вроде этого:

import urllib2
from bs4 import BeautifulSoup
import html5lib
from html5lib import sanitizer
from html5lib import treebuilders
from lxml import etree

url = 'http://...'

content = urllib2.urlopen(url)
parser = html5lib.HTMLParser(tokenizer=sanitizer.HTMLSanitizer,
                             tree=treebuilders.getTreeBuilder("lxml"),
                             namespaceHTMLElements=False)
htmlData = parser.parse(content)
htmlStr = etree.tostring(htmlData)

soup = BeautifulSoup(htmlStr, "lxml")

Тогда наслаждайся своим прекрасным супом!

Обратите внимание на опцию namespaceHTMLElements = false в анализаторе. Это важно, потому что lxml предназначен для XML, а не только для HTML. По этой причине он помечает все предоставляемые теги как принадлежащие пространству имен HTML. Теги будут выглядеть (например)

<html:li>

и BeautifulSoup не будут работать хорошо.

0 голосов
/ 12 сентября 2010

Попробуйте:

root.find('.//{http://www.w3.org/1999/xhtml}tr')

Вы должны указать пространство имен, а не префикс пространства имен (html:tr).Для получения дополнительной информации см. Документацию lxml, в частности раздел:

...