Замена пользовательского тега «HTML» в строке Python - PullRequest
0 голосов
/ 05 марта 2019

Я хочу иметь возможность включать пользовательский тег «HTML» в строку, например: "This is a <photo id="4" /> string".

В этом случае пользовательский тег имеет значение <photo id="4" />.Я также хотел бы изменить этот пользовательский тег, чтобы он записывался по-другому, если это облегчает задачу, например [photo id:4] или что-то в этом роде.

Я хочу иметь возможность передать эту строку в функцию, которая будет извлекать тег <photo id="4" />, и позвольте мне преобразовать его в более сложный шаблон, такой как <div class="photo"><img src="...." alt="..."></div>, который я затем смогу использовать для замены тега в исходной строке.

Я представляю, как он работает примерно так:

>>> content = "This is a <photo id="4" /> string"
# Pass the string to a function that returns all the tags with the given name.
>>> tags = parse_tags('photo', string)
>>> print(tags)
[{'tag': 'photo', 'id': 4, 'raw': '<photo id="4" />'}]
# Now that I know I need to render a photo with ID 4, so I can pass that to some sort of template thing
>>> rendered = render_photo(id=tags[0]['id'])
>>> print(rendered)
<div class="photo"><img src="...." alt="..."></div>
>>> content = content.replace(tags[0]['raw'], rendered)
>>> print(content)
This is a <div class="photo"><img src="...." alt="..."></div> string

Я думаю, что это довольно распространенный шаблон для чего-то вроде размещения фотографии в сообщении в блоге, поэтому мне интересно, есть ли библиотека, которая будет делать что-то похожее на пример parse_tags функция выше.Или мне нужно написать это?

Этот пример тега photo является всего лишь одним примером.Я хотел бы иметь теги с разными именами.В качестве другого примера, может быть, у меня есть база данных людей, и я хочу тег типа <person name="John Doe" />.В этом случае я хочу получить что-то вроде {'tag': 'person', 'name': 'John Doe', 'raw': '<person name="John Doe" />'}.Затем я могу использовать имя, чтобы найти этого человека и вернуть обработанный шаблон его визитной карточки или чего-то еще.

Ответы [ 2 ]

0 голосов
/ 05 марта 2019

Для этого вида «хирургического» анализа (когда вы хотите изолировать определенные теги вместо создания полного иерархического документа), метод pyparsing makeHTMLTags может быть очень полезным.

См. Аннотированный скрипт ниже,показать создание парсера и использовать его для методов parseTag и replaceTag:

import pyparsing as pp

def make_tag_parser(tag):
    # makeHTMLTags returns 2 parsers, one for the opening tag and one for the
    # closing tag - we only need the opening tag; the parser will return parsed
    # fields of the tag itself
    tag_parser = pp.makeHTMLTags(tag)[0]

    # instead of returning parsed bits of the tag, use originalTextFor to
    # return the raw tag as token[0] (specifying asString=False will retain
    # the parsed attributes and tag name as attributes)
    parser = pp.originalTextFor(tag_parser, asString=False)

    # add one more callback to define the 'raw' attribute, copied from t[0]
    def add_raw_attr(t):
        t['raw'] = t[0]
    parser.addParseAction(add_raw_attr)

    return parser

# parseTag to find all the matches and report their attributes
def parseTag(tag, s):
    return make_tag_parser(tag).searchString(s)


content = """This is a <photo id="4" /> string"""

tag_matches = parseTag("photo", content)
for match in tag_matches:
    print(match.dump())
    print("raw: {!r}".format(match.raw))
    print("tag: {!r}".format(match.tag))
    print("id:  {!r}".format(match.id))


# transform tag to perform tag->div transforms
def replaceTag(tag, transform, s):
    parser = make_tag_parser(tag)

    # add one more parse action to do transform
    parser.addParseAction(lambda t: transform.format(**t))
    return parser.transformString(s)

print(replaceTag("photo", 
                   '<div class="{tag}"><img src="<src_path>/img_{id}.jpg." alt="{tag}_{id}"></div>', 
                   content))

Отпечатки:

['<photo id="4" />']
- empty: True
- id: '4'
- raw: '<photo id="4" />'
- startPhoto: ['photo', ['id', '4'], True]
  [0]:
    photo
  [1]:
    ['id', '4']
  [2]:
    True
- tag: 'photo'
raw: '<photo id="4" />'
tag: 'photo'
id:  '4'
This is a <div class="photo"><img src="<src_path>/img_4.jpg." alt="photo_4"></div> string
0 голосов
/ 05 марта 2019

Если вы работаете с HTML5, я бы посоветовал заглянуть в модуль xml (etree).Это позволит вам разобрать весь документ в древовидную структуру и по отдельности манипулировать тегами (а затем превратить панель результатов в HTML-документ).

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

    import re
    text = """<html><body>some text <photo> and tags <photo id="4"> more text <person name="John Doe"> yet more text"""
    tags = ["photo","person","abc"]
    patterns = "|".join([ f"(<{tag} .*?>)|(<{tag}>)" for tag in tags ])
    matches = list(re.finditer(patterns,text))
    for match in reversed(matches):
        tag = text[match.start():match.end()]
        print(match.start(),match.end(),tag)
        # substitute what you need for that tag
        text = text[:match.start()] + "***" + text[match.end():]
    print(text)

Будет напечатано:

    64 88 <person name="John Doe">
    39 53 <photo id="4">
    22 29 <photo>
    <html><body>some text *** and tags *** more text *** yet more text

Выполнение замен в обратном порядке гарантирует, что диапазоны, найденные функцией finditer (), остаются действительными при изменении текста при заменах.

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