Как выбрать контекстные слова/символы, окружающие тег <a>, с помощью BeautifulSoup?

Я обрабатываю HTML из поискового робота с помощью BeautifulSoup. HTML-код проходит через фильтры, которые «упрощают» HTML, удаляя и заменяя теги, чтобы документ содержал только теги <html>, body, <div> и <a> и видимый текст.

В настоящее время у меня есть функция, которая в настоящее время извлекает URL-адреса и текст привязки с этих страниц. В дополнение к этому я хотел бы также извлечь N «контекстных слов», предшествующих и следующих за тегом <a> для каждой ссылки. Так, например, если у меня есть следующий документ:

<html><body>
<div>This is <a href = "www.example.com">a test</a>
<div>There was a big fluffy dog outside the <a href = "www.petfood.com">pet food store</a> with such a sad face.<div>
</div>
</body></html>

Затем, если N = 8, я хочу получить следующие 8 «контекстных слов» для каждой ссылки:

'www.example.com' --> ('This', 'is', 'There', 'was', 'a', 'big', 'fluffy', 'dog')`

'www.petfood.com' --> ('fluffy', 'dog', 'outside', 'the', 'with', 'such', 'a', 'sad')

Первая ссылка (www.example.com) имеет только два слова, предшествующих началу документа, поэтому возвращаются эти два слова, а также 6 следующих за тегом <a>, чтобы в сумме получить N=8. Также обратите внимание, что возвращаемые слова пересекают границу тега <a>, содержащего <div>.

Вторая ссылка (www.petfood.com) имеет N\2 = 4 слова, которые предшествуют ей, и 4 слова, которые следуют за ней, поэтому они возвращаются в качестве контекста. То есть, если возможно, N слов будут разделены между предшествующими и следующими за тегом <a>.

Я знаю, как это сделать, если текст находится внутри того же <div>, что и ссылка, но я не могу понять, как это сделать за пределами <div> вот так. По сути, с целью извлечения «контекстных слов» я хочу обращаться с документом так, как если бы это был всего лишь один блок видимого текста со ссылками, игнорируя содержащие div.

Как я могу извлечь текст вокруг таких тегов <a> с помощью BeautifulSoup? Ради простоты меня даже удовлетворил бы ответ, который просто возвращает N символов видимого текста, предшествующих/следующих за тегом (и я могу просто обрабатывать токенизацию/разделение самостоятельно).

В вашем первом примере (www.example.com) текст ссылки a test включен в вывод. Но во втором примере www.petfood.com текста ссылки в выводе нет. Вы хотите, чтобы текст ссылки был включен или нет?

glhr 10.04.2019 13:22

Спасибо @glhr. Это была опечатка. Текст ссылки не должен был быть включен. Я уже извлекаю якорный текст и хочу только контекстные слова.

J. Taylor 10.04.2019 17:33
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
2
192
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вот функция, которая принимает весь код HTML и N в качестве входных данных и для каждого вхождения элемента <a> создает кортеж с URL-адресом ссылки в качестве первого элемента и списком из N контекстных слов в качестве второго элемента. Он возвращает кортежи в списке.

def getContext(html,n):
    output = []
    soup = BeautifulSoup(html, 'html.parser')
    for i in soup.findAll("a"):
        n_side = int(n/2)

        text = soup.text.replace('\n',' ')

        context_before = text.split(i.text)[0]
        words_before = list(filter(bool,context_before.split(" ")))

        context_after = text.split(i.text)[1]
        words_after = list(filter(bool,context_after.split(" ")))

        if (len(words_after) >= n_side):
            words_before = words_before[-n_side:]
            words_after = words_after[:(n-len(words_before))]
        else:
            words_after = words_after[:n_side]
            words_before = words_before[-(n-len(words_after)):]

        output.append((i["href"], words_before + words_after))
    return output

Функция анализирует HTML с помощью BeautifulSoup и находит все элементы <a>. Для каждого результата извлекается только текст (с использованием soup.text) и удаляются все символы новой строки. Затем весь текст разделяется на две части с помощью текста ссылки. Каждая сторона анализируется в список слов, фильтруется, чтобы избавиться от любых пробелов, и нарезается таким образом, чтобы было извлечено максимум N контекстных слов.

Например:

html = '''<html><body>
<div>This is <a href = "www.example.com">a test</a> 
<div>There was a big fluffy dog outside the <a href = "www.petfood.com">pet food store</a> with such a sad face.<div>
</div>
</body></html>'''

print(*getContext(html,8))

Выходы:

('www.example.com', ['This', 'is', 'There', 'was', 'a', 'big', 'fluffy', 'dog'])
('www.petfood.com', ['fluffy', 'dog', 'outside', 'the', 'with', 'such', 'a', 'sad'])

Демо: https://repl.it/@glhr/55609756-ссылка-контекст

Редактировать: обратите внимание, что ловушка этой реализации заключается в том, что она использует текст ссылки в качестве разделителя, чтобы различать before и after. Это может быть проблемой, если текст ссылки повторяется в HTML-документе где-то перед самой ссылкой, например.

<div>This test is <a href = "www.example.com">test</a>

Простой обходной путь — добавить в текст ссылки специальные символы, чтобы сделать ее уникальной, например:

def getContext(html,n):
    output = []
    soup = BeautifulSoup(html, 'html.parser')
    for i in soup.findAll("a"):
        i.string.replace_with(f"[[[[{i.text}]]]]")
        # rest of code here

превратит <div>This test is <a href = "www.example.com">test</a> в <div>This test is <a href = "www.example.com">[[[[test]]]]</a>.

Вау, это выглядит великолепно. Я не могу проверить это подробно в данный момент, потому что мне нужно отвезти кого-то в аэропорт, но я сделаю это, когда вернусь позже, и отмечу как принятое, если это сработает (как я ожидаю, глядя на это). Спасибо!

J. Taylor 10.04.2019 18:19

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