在HTML文档中用正则表达式替换文本节点
我有一个字符串,它代表了一个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
这可能就是你正在寻找的解决方案:
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>')
虽然不是很优雅,但似乎有效。请你看看。