用爬虫抓下来的html源码,一般都有很多 换行符空格,现在想实现一个效果,对这些空字符都去掉,也就是对源码做一个压缩

第一时间可能是想到用正则去掉:

new_txt = re.sub(r”>\s+<”, “><”, text)

这样做的坏处是,假如标签的纯文本中存在模式类似 “>\s+<” 的文本,原本的结构就被破坏了,因此最好不要用正则直接去修改 html源码,很容易造成误差

逛了一圈 stockflow,我找到一个方案,就是使用 lxml.etree遍历每个标签节点,对其中的 tail和text 做一个 strip操作,我封装成了一个函数:

1
2
3
4
5
6
7
8
9
10
from lxml import etree

def remove_tag_space(html_text):
tree = etree.HTML(html_text)
for tag in tree.iter():
if tag.tail:
tag.tail = tag.tail.strip()
if tag.text:
tag.text = tag.text.strip()
return etree.tostring(tree, encoding="unicode")

效果如下:

有一点需要注意的是,这方法对于单标签如 <br>、<hr>、<img>、<input>、<param>、<meta>、<link>等,还是会把原文本首尾的空格去掉,如果想避免这种情况的话,我的建议是做一个标签白名单,如果是单标签就跳过

2022.06.12 更新

突然发现 w3lib.html 里面其实已经实现好了轮子:

replace_escape_chars(text, which_ones=(‘\n’, ‘\t’, ‘\r’), replace_by=u’’, encoding=None))

1
2
3
4
5
6
7
In [13]: from w3lib import html

In [15]: text
Out[15]: '<div id="main-view" style="visibility: hidden">\n<div class="backgrounds"></div>\n<div class="apps hide-apps">\n<div class="region full"></div>\n<div class="region top-bar"></div>\n<div class="top-row">\n<div class="region top-left" data-test="top-left"></div>\n<div class="region top-center"></div>\n<div class="region top-right"></div>'

In [16]: html.replace_escape_chars(text)
Out[16]: '<div id="main-view" style="visibility: hidden"><div class="backgrounds"></div><div class="apps hide-apps"><div class="region full"></div><div class="region top-bar"></div><div class="top-row"><div class="region top-left" data-test="top-left"></div><div class="region top-center"></div><div class="region top-right"></div>'

这个模块里面还有很多好用的函数

  • replace_entities(text, keep=(), remove_illegal=True, encoding=’utf-8’)

    暂不确定用法,看源码像是转换 &nbsp &gt 这些符号的

  • replace_tags(text, token=’’, encoding=None)

    注意区别于 remove_tags

    1
    2
    3
    4
    5
    In [17]: html.replace_tags(u'This text contains <a>some tag</a>')
    Out[17]: 'This text contains some tag'

    In [18]: html.replace_tags(u'This text contains <a>some tag</a>', token='***')
    Out[18]: 'This text contains ***some tag***'
  • remove_comments(text, encoding=None)

    1
    2
    In [19]: html.remove_comments(b"test <!--textcoment--> whatever")
    Out[19]: 'test whatever'
  • remove_tags(text, which_ones=(), keep=(), encoding=None)

    不会标签去除里面的文本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    >>> doc = '<div><p><b>This is a link:</b> <a href="http://www.example.com">example</a></p></div>'
    >>> w3lib.html.remove_tags(doc)
    u'This is a link: example'

    # 保留什么标签
    >>> w3lib.html.remove_tags(doc, keep=('div',))
    u'<div>This is a link: example</div>'

    # 去除什么标签
    >>> w3lib.html.remove_tags(doc, which_ones=('a','b'))
    u'<div><p>This is a link: example</p></div>'
  • remove_tags_with_content(text, which_ones=(), encoding=None)