Beautiful Soup - Получить весь текст, но сохранить HTML-ссылку?

Мне нужно обработать большой архив чрезвычайно беспорядочного HTML, полный посторонних таблиц, интервалов и встроенных стилей, в уценку.

Я пытаюсь использовать Красивый суп для выполнения этой задачи, и моя цель - в основном вывод функции get_text(), за исключением сохранения тегов привязки с неповрежденным href.

В качестве примера я хотел бы преобразовать:

<td>
    <font><span>Hello</span><span>World</span></font><br>
    <span>Foo Bar <span>Baz</span></span><br>
    <span>Example Link: <a href = "https://google.com" target = "_blank" style = "mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #395c99;font-weight: normal;text-decoration: underline;">Google</a></span>
</td>

В:

Hello World
Foo Bar Baz
Example Link: <a href = "https://google.com">Google</a>

До сих пор мой мыслительный процесс заключался в том, чтобы просто захватить все теги и развернуть их все, если они не являются якорями, но это приводит к тому, что текст повторяется несколько раз, поскольку soup.find_all(True) возвращает рекурсивно вложенные теги как отдельные элементы:

#!/usr/bin/env python

from bs4 import BeautifulSoup

example_html = '<td><font><span>Hello</span><span>World</span></font><br><span>Foo Bar <span>Baz</span></span><br><span>Example Link: <a href = "https://google.com" target = "_blank" style = "mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #395c99;font-weight: normal;text-decoration: underline;">Google</a></span></td>'

soup = BeautifulSoup(example_html, 'lxml')
tags = soup.find_all(True)

for tag in tags:
    if (tag.name == 'a'):
        print("<a href='{}'>{}</a>".format(tag['href'], tag.get_text()))
    else:
        print(tag.get_text())

Что возвращает несколько фрагментов / дубликатов при перемещении парсера вниз по дереву:

HelloWorldFoo Bar BazExample Link: Google
HelloWorldFoo Bar BazExample Link: Google
HelloWorldFoo Bar BazExample Link: Google
HelloWorld
Hello
World

Foo Bar Baz
Baz

Example Link: Google
<a href='https://google.com'>Google</a>

вы хотите удалить стили и другие атрибуты ссылок ?? потому что ваш ввод и вывод относится к этому

Gahan 26.08.2018 15:52
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
4
1
1 241
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Чтобы рассматривать только прямые дочерние элементы, установите рекурсивный = False, тогда вам нужно обработать каждый 'td' и отдельно извлечь текст и ссылку привязки.

#!/usr/bin/env python
from bs4 import BeautifulSoup

example_html = '<td><font><span>Some Example Text</span></font><br><span>Another Example Text</span><br><span>Example Link: <a href = "https://google.com" target = "_blank" style = "mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #395c99;font-weight: normal;text-decoration: underline;">Google</a></span></td>'

soup = BeautifulSoup(example_html, 'lxml')
tags = soup.find_all(recursive=False)
for tag in tags:
    print(tag.text)
    print(tag.find('a'))

Если вы хотите, чтобы текст был напечатан на отдельных строках, вам придется обрабатывать промежутки индивидуально.

for tag in tags:
    spans = tag.find_all('span')
    for span in spans:
        print(span.text)
print(tag.find('a'))
Ответ принят как подходящий

Одним из возможных способов решения этой проблемы было бы введение некоторой специальной обработки для элементов a, когда дело доходит до печати текста элемента.

Вы можете сделать это, переопределив метод _all_strings() и вернув строковое представление элемента-потомка a, и пропустив строку с возможностью навигации внутри элемента a. Что-то в этом роде:

from bs4 import BeautifulSoup, NavigableString, CData, Tag


class MyBeautifulSoup(BeautifulSoup):
    def _all_strings(self, strip=False, types=(NavigableString, CData)):
        for descendant in self.descendants:
            # return "a" string representation if we encounter it
            if isinstance(descendant, Tag) and descendant.name == 'a':
                yield str(descendant)

            # skip an inner text node inside "a"
            if isinstance(descendant, NavigableString) and descendant.parent.name == 'a':
                continue

            # default behavior
            if (
                (types is None and not isinstance(descendant, NavigableString))
                or
                (types is not None and type(descendant) not in types)):
                continue

            if strip:
                descendant = descendant.strip()
                if len(descendant) == 0:
                    continue
            yield descendant

Демо:

In [1]: data = """
   ...: <td>
   ...:     <font><span>Hello</span><span>World</span></font><br>
   ...:     <span>Foo Bar <span>Baz</span></span><br>
   ...:     <span>Example Link: <a href = "https://google.com" target = "_blank" style = "mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #395c99;font-weight: normal;tex
   ...: t-decoration: underline;">Google</a></span>
   ...: </td>
   ...: """

In [2]: soup = MyBeautifulSoup(data, "lxml")

In [3]: print(soup.get_text())

HelloWorld
Foo Bar Baz
Example Link: <a href = "https://google.com" style = "mso-line-height-rule: exactly;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #395c99;font-weight: normal;text-decoration: underline;" target = "_blank">Google</a>

Поразительно, спасибо, это гибкое и элегантное решение, о котором я бы никогда не подумал. Я сделал небольшую корректировку обработки тега a, чтобы выводить так, как я хотел, и это идеально.

waffl 26.08.2018 17:42

Как он мог вернуть только атрибут href? Нравится: Example Link: <https://google.com>

Gabriel 02.07.2019 19:29

Отвечая на мой вопрос, просто измените: if isinstance(descendant, Tag) and descendant.name == 'a': yield str(descendant) Кому: if isinstance(descendant, Tag) and descendant.name == 'a': yield str('<{}> '.format(descendant.get('href', '')))

Gabriel 02.07.2019 21:03

Другие вопросы по теме