Заменить несколько строк с элементами в HTML-документе - PullRequest
0 голосов
/ 31 января 2019

У меня есть несколько строк, которые я хочу обернуть тегами HTML внутри документа HTML.Я хочу оставить текст таким же, но заменить строки на элементы HTML, содержащие эту строку.

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

Кроме того, я хочу выполнять эту замену только тогда, когда эти строки полностью содержатся в одном и том же элементе.

Вот мой список замены.

replacement_list = [
    ('foo', '<span title="foo" class="customclass34">foo</span>'),
    ('foo bar', '<span id="id21" class="customclass79">foo bar</span>')
]

Учитывая следующий html:

<html>
<body>
<p>Paragraph contains foo</p>
<p>Paragraph contains foo bar</p>
</body>
</html>

Я бы хотел заменить это:

<html>
<body>
<p>Paragraph contains <span title="foo" class="customclass34">foo</span></p>
<p>Paragraph contains <span id="id79" class="customclass79">foo bar</span</p>
</body>
</html>

До сих пор я пытался использовать красивую библиотеку супа и перебирать свой список замены в порядке уменьшения длины строки, и я могу найти и заменить свои строки другими строками, но я не могу понять, как вставить HTML в эти строки.точки.Или есть ли лучший способ полностью.Попытка выполнить подстановку строки с помощью объекта soup.new_tag завершится неудачно, независимо от того, преобразую ли я ее в строку или нет.

РЕДАКТИРОВАТЬ: понял, что приведенный мной пример даже не соответствует моим собственным правилам, модифицированный пример.

Ответы [ 3 ]

0 голосов
/ 31 января 2019

Я думаю, это очень близко к тому, что вы ищете.Вы можете использовать soup.find_all(string=True), чтобы получить только элементы NavigableString, а затем заменить.

from bs4 import BeautifulSoup
html="""
<html>
<body>
<p>Paragraph contains foo</p>
<p>Paragraph contains foo bar</p>
</body>
</html>
"""
replacement_list = [
    ('foo', '<span title="foo" class="customclass34">foo</span>'),
    ('foo bar', '<span id="id21" class="customclass79">foo bar</span>')
]
soup=BeautifulSoup(html,'html.parser')
for s in soup.find_all(string=True):
    for item in replacement_list[::-1]: #assuming that it is in ascending order of length
        key,val=item
        if key in s:
            new_s=s.replace(key,val)
            s.replace_with(BeautifulSoup(new_s,'html.parser')) #restrict youself to this built-in parser
            break#break on 1st match
print(soup)

#generate a new valid soup that treats span as seperate tag if you want
soup=BeautifulSoup(str(soup),'html.parser')
print(soup.find_all('span'))

Выходы:

<html>
<body>
<p>Paragraph contains <span class="customclass34" title="foo">foo</span></p>
<p>Paragraph contains <span class="customclass79" id="id21">foo bar</span></p>
</body>
</html>

[<span class="customclass34" title="foo">foo</span>, <span class="customclass79" id="id21">foo bar</span>]
0 голосов
/ 06 февраля 2019

Я нашел решение для этого.

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

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

Это решение также нечувствительно к регистру (оно будет оборачивать теги вокруг строки 'fOo'), сохраняя регистр исходного текста.

def html_update(input_html):
    from bs4 import BeautifulSoup
    import re

    soup = BeautifulSoup(input_html)

    replacement_list = [
        ('foo', '<span title="foo" class="customclass34 replace">', '</span>'),
        ('foo bar', '<span id="id21" class="customclass79 replace">', '</span>')
    ]
    # Go through list in order of decreasing length
    replacement_list = sorted(replacement_list, key = lambda k: -len(k[0]))

    for item in replacement_list:
        replace_regex = re.compile(item[0], re.IGNORECASE)
        target = soup.find_all(string=replace_regex)
        for v in target:
            # You can use other conditions here, like (v.parent.name == 'a')
            # to not wrap the tags around strings within links
            if v.parent.has_attr('class') and 'replace' in v.parent['class']:
                # The match must be part of a large string that was already replaced, so do nothing
                continue 

            def replace(match):
                return '{0}{1}{2}'.format(item[1], match.group(0), item[2])

            new_v = replace_regex.sub(replace, v)
            v.replace_with(BeautifulSoup(new_v, 'html.parser'))
    return str(soup)
0 голосов
/ 31 января 2019

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

Предполагая, что ваш файлназывается output.html:

replacement_list = {'foo': '<span title="foo" class="customclass34">foo</span>', 'foo bar':'<span id="id21" class="customclass79">foo bar</span>'}

with open('output.html','w') as dest :
    with open('test.html','r') as src :
        for line in src:   #### reading the src file line by line
            str_possible = []
            for string in replacement_list.keys(): #### looping over all the strings you are looking for
                if string in line: ### checking if this string is in the line
                    str_possible.append(string)
            if len(str_possible) >0:
                str_final = max(str_possible, key=len)  ###taking the appropriate one, which is the longest
                line = line.replace(str_final,replacement_list[str_final])

            dest.write(line)

Я также предлагаю вам проверить использование словарей в Python, который является объектом, который я использую для replacement_list.

Наконец, этот код будетработать, если в строке есть максимум одна строка.Если их два, их нужно немного адаптировать, но это дает вам общее представление.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...