2012-03-19 71 views
1

我有一堆不正确格式的中文html文件。它们包含不必要的空格和换行符,它们将在浏览器中显示为额外的空格。我用lxml编写了一个脚本来修改html文件。它在简单的标签上工作正常,但是我被困在嵌套的标签上。例如:使用lxml删除中文HTML文件中的多余空格

<p>祝你<span>19</span>岁 
    生日快乐。</p> 

将显示为浏览器为:

祝你19岁 生日快乐。 

注意额外的空间。这是需要删除的内容。结果HTML应该是这样的:

<p>祝你<span>19</span>岁生日快乐。</p> 

我该怎么做?

请注意,嵌套(如span标签)可以是任意的,但我不需要考虑嵌套元素中的内容,它们应该保留原样。只有外部元素中的文本需要格式化。

这是我的本钱:

# -*- coding: utf-8 -*- 

import lxml.html 
import re 

s1 = u"""<p>祝你19岁 
    生日快乐。</p>""" 
p1 = lxml.html.fragment_fromstring(s1) 
print p1.text   # I get the whole line. 
p1.text = re.sub("\s+", "", p1.text) 
print p1.tostring() # spaces are removed. 

s2 = u"""<p>祝你<span>19</span>岁 
    生日快乐。</p>""" 
p2 = lxml.html.fragment_fromstring(s2) 
print p2.text  # I get "祝你" 
print p2.tail  # I get None 
i = p2.itertext() 
print i.next() # I get "祝你" 
print i.next() # I get "19" from <span> 
print i.next() # I get the tailed text, but how do I assemble them back? 
print p2.text_content() # The whole text, but how do I put <span> back? 
+1

这个任务的哪一部分让你感到困惑?你有什么尝试? – Marcin 2012-03-19 09:42:50

+1

@Marcin代码示例添加。希望现在已经清楚了。 – 2012-03-19 10:32:37

+0

好问题 - 我的头顶没有答案,但我最好猜测的是你必须走树形结构(递归或迭代,如你喜欢的),去除额外的空间。 – Marcin 2012-03-19 10:40:39

回答

1

争议的是,我不知道这是否是可能的,而无需使用HTML/XML解析器,考虑到它看起来是换行的事业来完成。

我建了一个正则表达式查找空白文本中国之间的这种解决方案在这里的帮助:https://stackoverflow.com/a/2718268/267781

,我不知道是否捕获所有的字符之间还是较为任何空白具体的[char]\n\s*[char]是最适合你的问题。

# -*- coding: utf-8 -*- 
import re 

# Whitespace in Chinese HTML 
## Used this solution to create regexp: https://stackoverflow.com/a/2718268/267781 
## \s+ 
fixwhitespace2 = re.compile(u'[\u2e80-\u2e99\u2e9b-\u2ef3\u2f00-\u2fd5\u3005\u3007\u3021-\u3029\u3038-\u303a\u303b\u3400-\u4db5\u4e00-\u9fc3\uf900-\ufa2d\ufa30-\ufa6a\ufa70-\ufad9\U00020000-\U0002a6d6\U0002f800-\U0002fa1d](\s+)[\u2e80-\u2e99\u2e9b-\u2ef3\u2f00-\u2fd5\u3005\u3007\u3021-\u3029\u3038-\u303a\u303b\u3400-\u4db5\u4e00-\u9fc3\uf900-\ufa2d\ufa30-\ufa6a\ufa70-\ufad9\U00020000-\U0002a6d6\U0002f800-\U0002fa1d]',re.M) 
## \n\s* 
fixwhitespace = re.compile(u'[\u2e80-\u2e99\u2e9b-\u2ef3\u2f00-\u2fd5\u3005\u3007\u3021-\u3029\u3038-\u303a\u303b\u3400-\u4db5\u4e00-\u9fc3\uf900-\ufa2d\ufa30-\ufa6a\ufa70-\ufad9\U00020000-\U0002a6d6\U0002f800-\U0002fa1d](\n\s*)[\u2e80-\u2e99\u2e9b-\u2ef3\u2f00-\u2fd5\u3005\u3007\u3021-\u3029\u3038-\u303a\u303b\u3400-\u4db5\u4e00-\u9fc3\uf900-\ufa2d\ufa30-\ufa6a\ufa70-\ufad9\U00020000-\U0002a6d6\U0002f800-\U0002fa1d]',re.M) 

sample = u'<html><body><p>\u795d\u4f6019\u5c81\n \u751f\u65e5\u5feb\u4e50\u3002</p></body></html>' 

fixwhitespace.sub('',sample) 

屈服

<html><body><p>祝你19日快乐。</p></body></html> 

然而,这里是你会如何使用解析器和XPath找换行做到这一点:

# -*- coding: utf-8 -*- 
from lxml import etree 
import re 

fixwhitespace = re.compile(u'[\u2e80-\u2e99\u2e9b-\u2ef3\u2f00-\u2fd5\u3005\u3007\u3021-\u3029\u3038-\u303a\u303b\u3400-\u4db5\u4e00-\u9fc3\uf900-\ufa2d\ufa30-\ufa6a\ufa70-\ufad9\U00020000-\U0002a6d6\U0002f800-\U0002fa1d](\n\s*)[\u2e80-\u2e99\u2e9b-\u2ef3\u2f00-\u2fd5\u3005\u3007\u3021-\u3029\u3038-\u303a\u303b\u3400-\u4db5\u4e00-\u9fc3\uf900-\ufa2d\ufa30-\ufa6a\ufa70-\ufad9\U00020000-\U0002a6d6\U0002f800-\U0002fa1d]',re.M) 
sample = u'<html><body><p>\u795d\u4f6019\u5c81\n \u751f\u65e5\u5feb\u4e50\u3002</p></body></html>' 

doc = etree.HTML(sample) 
for t in doc.xpath("//text()[contains(.,'\n')]"): 
    if t.is_tail: 
    t.getparent().tail = fixwhitespace.sub('',t) 
    elif t.is_text: 
    t.getparent().text = fixwhitespace.sub('',t) 

print etree.tostring(doc) 

产量:

<html><body><p>&#31069;&#20320;19&#26085;&#24555;&#20048;&#12290;</p></body></html> 

我很好奇你的工作数据的最佳匹配是什么。

+0

谢谢!纯正则表达式不适用于某些内容,但xpath +正则表达式工作得很好。 – 2012-03-20 03:29:59

+0

*纯正则表达式不适用于某些内容* - 我想这会验证关于正则表达式和HTML/XML的所有警告和警告!我很高兴其中一种方法在整个数据集中都是有效的。 – MattH 2012-03-20 08:48:32

2
>>> root = etree.fromstring('<p>祝你<span>19</span>岁\n生日快乐。</p>') 
>>> etree.tostring(root) 
b'<p>&#31069;&#20320;<span>19</span>&#23681;\n&#29983;&#26085;&#24555;&#20048;&#12290;</p>' 

>>> for e in root.xpath('/p/*'): 
... if e.tail: 
...  e.tail = e.tail.replace('\n', '') 
... 

>>> etree.tostring(root) 
b'<p>&#31069;&#20320;<span>19</span>&#23681;&#29983;&#26085;&#24555;&#20048;&#12290;</p>' 
+0

谢谢。我应该多看看xpath。我接受了马特的答案,因为它更全面。 – 2012-03-20 03:33:25