Regex заменить текстовые узлы в HTML-документе - PullRequest
1 голос
/ 27 октября 2011

У меня есть строка, которая представляет HTML-документ. Я пытаюсь заменить текст в этом документе, исключая разметку и значения атрибута курса, с некоторым заменой HTML. Я думал, что это будет просто, но это невероятно утомительно, когда вы хотите заменить текст на разметку. Например, заменить somekeyword на <a href = "link">somekeyword</a>.

from lxml.html import fragments_fromstring, fromstring, tostring
from re import compile
def markup_aware_sub(pattern, repl, text):
    exp = compile(pattern)
    root = fromstring(text)

    els = [el for el in root.getiterator() if el.text]
    els = [el for el in els if el.text.strip()]
    for el in els:
        text = exp.sub(repl, el.text)
        if text == el.text:
            continue
        parent = el.getparent()
        new_el = fromstring(text)
        new_el.tag = el.tag
        for k, v in el.attrib.items():
            new_el.attrib[k] = v
        parent.replace(el, new_el)
    return tostring(root)

markup_aware_sub('keyword', '<a>blah</a>', '<div><p>Text with keyword here</p></div>')

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

1 Ответ

3 голосов
/ 27 октября 2011

Это может быть решение, которое вы ищете:

from HTMLParser import HTMLParser

class MyParser(HTMLParser):
    def __init__(self,link, keyword):
    HTMLParser.__init__(self)
    self.__html = []
    self.link = link
    self.keyword = keyword

    def handle_data(self, data):
    text = data.strip()
    self.__html.append(text.replace(self.keyword,'<a href="'+self.link+'>'+self.keyword+'</a>'))

    def handle_starttag(self, tag, attrs):
    self.__html.append("<"+tag+">")

    def handle_endtag(self, tag):
    self.__html.append("</"+tag+">")

    def new_html(self):
    return ''.join(self.__html).strip()


parser = MyParser("blah","keyword")
parser.feed("<div><p>Text with keyword here</p></div>")
parser.close()
print parser.new_html()

Это даст вам следующий вывод

<div><p>Text with <a href="blah>keyword</a> here</p></div>

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

from lxml.html import fragments_fromstring, fromstring, tostring
from re import compile
def markup_aware_sub(pattern, repl, text):
    exp = compile(pattern)
    root = fromstring(text)
    els = [el for el in root.getiterator() if el.text]
    els = [el for el in els if el.text.strip()]

    if len(els) == 1:
      el = els[0]
      text = exp.sub(repl, el.text)
      parent = el.getparent()
      new_el = fromstring(text)
      new_el.tag = el.tag
      for k, v in el.attrib.items():
          new_el.attrib[k] = v
      return tostring(new_el)

    for el in els:
      text = exp.sub(repl, el.text)
      if text == el.text:
        continue
      parent = el.getparent()
      new_el = fromstring(text)
      new_el.tag = el.tag
      for k, v in el.attrib.items():
          new_el.attrib[k] = v
      parent.replace(el, new_el)
    return tostring(root)

print markup_aware_sub('keyword', '<a>blah</a>', '<p>Text with keyword here</p>')

Не очень элегантно, но, похоже, работает. Пожалуйста, проверьте это.

...