#html #parsing #lxml
#HTML #синтаксический анализ #lxml
Вопрос:
Рассмотрим следующий HTML:
<div>
Some foo text foo
<p> text inside paragraph foo and also foo and <b> nested foo</b> and foo </p>
foo is also here and can occur many times foo foo
<p> here <a>foo</a> already appears inside a link so it is not changed</p>
foo, yeah!
</div>
Мне нужно обернуть все вхождения ‘foo’ в интерактивную ссылку ( <a>
элемент), за исключением вхождений, которые уже находятся внутри <a>
, поэтому ожидаемый результат:
<div>
Some <a>foo</a> text <a>foo</a>
<p> text inside paragraph <a>foo</a> and also <a>foo</a> and <b> nested <a>foo</a></b> and <a>foo</a> </p>
<a>foo</a> is also here and can occur many times <a>foo</a> <a>foo</a>
<p> here <a> foo </a> appears inside a link so it is not changed</p>
<a>foo</a>, yeah!
</div>
Есть ли простой способ сделать это с помощью lxml? Изначально замена необработанной подстроки имела для меня больше смысла, но есть требование, согласно которому некоторые вхождения не должны изменяться, если они находятся внутри определенных элементов HTML.
Ответ №1:
Хорошо, BeautifulSoup кажется намного лучше, чем raw lxml для этой цели
Этот код работает довольно хорошо:
from bs4 import BeautifulSoup
x = """<div>
Some foo text foo
<p> text inside paragraph foo and also foo and <b> nested foo</b> and foo </p>
foo is also here and can occur many times foo foo
<p> here <a>foo</a> already appears inside a link so it is not changed</p>
foo, yeah!
</div>"""
s = BeautifulSoup(x, 'html.parser')
print(s)
for text_node in list(s.strings):
if not text_node.parent.name=='a':
text_node.replace_with(BeautifulSoup(text_node.string.replace('foo', '<a>foo</a>'), 'html.parser'))
print(s)
Редактировать: важно использовать html.parser. Передача «lxml» при создании заменяющего HTML-фрагмента работает некорректно (оборачивает HTML-фрагмент в html-тег)
Ответ №2:
Это должно привести вас туда, куда, я думаю, вы направляетесь:
x_list = x.split(' ')
for word in range (len(x_list)):
if 'foo' in x_list[word]:
if x_list[word] != '<a>foo</a>':
x_list[word]='<a>foo</a>'
new_x = ' '.join(x_list)
print(new_x.strip('n'))
Вывод:
<div>
Some <a>foo</a> text <a>foo</a> <p> text inside paragraph <a>foo</a> and also <a>foo</a> and <b> nested <a>foo</a> and <a>foo</a> </p>
<a>foo</a> is also here and can occur many times <a>foo</a> <a>foo</a> <p> here <a>foo</a> already appears inside a link so it is not changed</p>
<a>foo</a> yeah!
Комментарии:
1. Это сработало бы, но мой вопрос был недостаточно точным. Я не могу предположить, что строка foo всегда будет напрямую обернута в <a> , кроме того, даже с вашим кодом это не сработает, если есть некоторые пробелы, например ‘<a> foo </ a>’. Поэтому я предположил, что строка документа должна быть проанализирована в формате HTML, чтобы обнаружить такого рода вхождения.
2. Ваш второй пункт (о пробелах) может быть легко рассмотрен, но прежде чем я это сделаю, не могли бы вы уточнить свой первый пункт — что вы подразумеваете под «не могу предположить, что строка foo всегда будет напрямую обернута в <a>»? Можете ли вы привести пример подобной строки, которую нужно оставить нетронутой?