Я обрабатываю 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 символов видимого текста, предшествующих/следующих за тегом (и я могу просто обрабатывать токенизацию/разделение самостоятельно).
Спасибо @glhr. Это была опечатка. Текст ссылки не должен был быть включен. Я уже извлекаю якорный текст и хочу только контекстные слова.
Вот функция, которая принимает весь код 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>
.
Вау, это выглядит великолепно. Я не могу проверить это подробно в данный момент, потому что мне нужно отвезти кого-то в аэропорт, но я сделаю это, когда вернусь позже, и отмечу как принятое, если это сработает (как я ожидаю, глядя на это). Спасибо!
В вашем первом примере (
www.example.com
) текст ссылкиa test
включен в вывод. Но во втором примереwww.petfood.com
текста ссылки в выводе нет. Вы хотите, чтобы текст ссылки был включен или нет?