lxml неправильно анализирует Doctype при поиске ссылок

У меня есть синтаксический анализатор BeautifulSoup4 (4.2.1), который собирает все атрибуты href из наших файлов шаблонов, и до сих пор он был просто идеальным. Но с установленным lxml один из наших парней теперь получает;

TypeError: string indices must be integers.

Мне удалось воспроизвести это на моей виртуальной машине Linux Mint, и единственная разница, по-видимому, заключается в lxml, поэтому я предполагаю, что проблема возникает, когда bs4 использует этот парсер html.

Проблемная функция;

def collecttemplateurls(templatedir, urlslist):
    """
    Uses BeautifulSoup to extract all the external URLs from the templates dir.

    @return: list of URLs
    """
    for (dirpath, dirs, files) in os.walk(templatedir):
        for path in (Path(dirpath, f) for f in files):
            if path.endswith(".html"):
                for link in BeautifulSoup(
                        open(path).read(),
                        parse_only=SoupStrainer(target="_blank")
                ):
                    if link["href"].startswith('http://'):
                        urlslist.append(link['href'])

                    elif link["href"].startswith('{{'):
                        for l in re.findall("'(http://(?:.*?))'", link["href"]):
                            urlslist.append(l)

    return urlslist

Итак, для этого парня строка if link["href"].startswith('http://'): дает ошибку типа, потому что BS4 считает, что html Doctype является ссылкой.

Может ли кто-нибудь объяснить, в чем может быть проблема, потому что никто другой не может ее воссоздать?

Я не понимаю, как это могло произойти при таком использовании SoupStrainer. Я предполагаю, что это как-то связано с проблемой настройки системы.

Я не вижу ничего особенного в нашем Doctype;

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-gb">

<head>

person markwalker_    schedule 01.08.2013    source источник
comment
Я не смог воспроизвести вашу проблему. Я использовал BeautifulSoup версии 3.2.0 и BeautifulSoup(html,parseOnlyThese=SoupStrainer(target="_blank"))   -  person Sudipta    schedule 01.08.2013
comment
@Sudipta Как только я установил lxml (3.2.3) на свой компьютер с Windows, это начало происходить. Я не уверен, какой html-парсер bs4 использовал раньше, но я должен предположить, что проблема возникла из-за lxml.   -  person markwalker_    schedule 05.08.2013


Ответы (1)


SoupStrainer не будет отфильтровывать тип документа; он фильтрует, какие элементы остаются в документе, но тип документа сохраняется, поскольку он является частью «контейнера» для отфильтрованных элементов. Вы перебираете все элементы в документе, поэтому первый элемент, с которым вы сталкиваетесь, — это объект DocType.

Используйте .find_all() в «напряженном» документе:

document = BeautifulSoup(open(path).read(), parse_only=SoupStrainer(target="_blank"))
for link in documen.find_all(target="_blank"):

или отфильтровать объект DocType:

from bs4 import DocType

for link in BeautifulSoup(
        open(path).read(),
        parse_only=SoupStrainer(target="_blank")
):
    if isinstance(link, Doctype): continue 
person Martijn Pieters    schedule 05.08.2013
comment
Чудесно, спасибо! Я выбрал метод find_all, так как никогда не использую : continue (не знаю, как это работает с принципами и передовой практикой!) - person markwalker_; 06.08.2013
comment
continue — прекрасное утверждение; это прямо там с return и break. :-) - person Martijn Pieters; 06.08.2013