1. 程式人生 > >自己動手實現html去標籤和文字提取

自己動手實現html去標籤和文字提取

原文出處:https://blog.csdn.net/AsuraDong/article/details/72877382

隨意觀看


工具準備

  • python3.6
  • 正則表示式(別的語言思路一樣,容易借鑑)

python正則表示式:flags的應用

這裡主要介紹一下re.compile(pattern[, flags])裡面的flags用法

識別符號 作用
re.I 忽略大小寫
re.L 表示特殊字符集 \w, \W, \b, \B, \s, \S 依賴於當前環境
re.M 多行模式
re.S ' . '並且包括換行符在內的任意字元(注意:' . '不包括換行符)
re.U 表示特殊字符集 \w, \W, \b, \B, \d, \D, \s, \S 依賴於 Unicode 字元屬性資料庫

特別強調re.Ire.S的用法

  1. 眾所周知,html標籤是大小寫不敏感的
    ,所以我們需要re.I
  2. 其次html中<style>..</style><sript>..</scipt>等一些標籤裡面是包含換行符的。而我們為了保留原來文字的特點包括換行符,所以需要re.S.可以匹配換行符

清洗全形和半形字元

實現字元的清洗工作,否則,jieba詞庫會將全形數字全部分開。而且,清洗後的半形文字更適合觀看

小姿勢

中文文字永遠是全形,只有英文字母、數字鍵、符號鍵才有全形半形的概念,一個字母或數字佔一個漢字的位置叫全形,佔半個漢字的位置叫半形

全形半形轉換說明

  1. 全形字元unicode編碼從65281~65374 (十六進位制 0xFF01 ~ 0xFF5E)
  2. 半形字元unicode編碼從33~126 (十六進位制 0x21~ 0x7E)
  3. 空格比較特殊,全形為 12288(0x3000),半形為 32(0x20)

程式碼實現

def Q2B(_char):#全形轉半形
    if 65281<=ord(_char)<=65374:
        _char = chr(ord(_char)-65248)
    elif ord(_char)==12288:
        _char = chr(32)
    return _char
 
def isQ(Char):
    return True if (65281<=ord(Char)<=65374 or ord(Char)==12288) else False
 
def B2Q(_char):#半形轉全形
    if 33<=ord(_char)<=126:
        _char = chr(ord(_char)+65248)
    elif ord(_char)==32:
        _char = chr(12288)
    return _char
 
def isB(Char):
    return True if (33<=ord(Char)<=126 or ord(Char)==32) else False

網頁字元實體

標準的html程式碼中的文字內容是不會出現'<'/' '等這些字元的。現在很多工具都會將網頁文字內容處理成標準形式再發布。我們這裡討論的就是標準的html程式碼及文字內容

html字元實體查詢地址

為了方便討論,我們這裡取了幾個常用的作為示範,並且構造以下dict

html_char = {}
html_char['&quot;'] = html_char['&#34;']='"'
html_char['&apos;'] = html_char['&#39;'] = "'"
html_char['&amp;'] = html_char['&#38;'] = '&'
html_char['&lt;'] = html_char['&#60;'] = '<'
html_char['&gt;'] = html_char['&#62;'] = '>'
html_char['&nbsp;'] = html_char['&#160;']= ' '

Code實現

難點重點就在這裡,做了很多準備工作,幸好python比較方便,其他語言的玩家可以借鑑一下思路

正則Code實現去標籤[^1]

    ...
    #CDATA 部分由 "<![CDATA[" 開始,由 "]]>" 結束:
    cdata_rule = re.compile(r'<![CDATA[.*]]>',re.I | re.S)
 
    #去除指令碼(隨時會出現)
    script_rule = re.compile(r'<script.*?</script>',re.I | re.S)
 
    #取出<head>..</head>和中間的內容,style也在裡面,不需要再寫了
    head_rule = re.compile(r'<head.*?/head>',re.I | re.S)
 
    #為了以防一些文字不是全部擷取html程式碼,還是寫一下以防萬一
    style_rule = re.compile(r'<style.*?/style>',re.I | re.S)
 
    #處理註釋
    comment_rule = re.compile(r'<!.*?>',re.I | re.S)
    
    #處理換行
    br_rule = re.compile(r'<br\s*?/{0,1}>',re.I)
 
    #html標籤
    html_rule = re.compile(r'<.*?/{0,1}>',re.I)
    ...

正則Code實現去字元實體

    ...
    global html_char
    letter_char = re.compile(r'&[a-z]+;',re.I)
    for char in letter_char.findall(raw):
        raw = re.sub(char,html_char[char],raw)
 
    number_char = re.compile(r'&#\d+;',re.I)
    for char in number_char.findall(raw):
        raw = re.sub(char,html_char[char],raw)
    ...

全部程式碼(含測試文字)

import re
 
html_char = {}
html_char['&quot;'] = html_char['&#34;']='"'
html_char['&apos;'] = html_char['&#39;'] = "'"
html_char['&amp;'] = html_char['&#38;'] = '&'
html_char['&lt;'] = html_char['&#60;'] = '<'
html_char['&gt;'] = html_char['&#62;'] = '>'
html_char['&nbsp;'] = html_char['&#160;']= ' '
 
def Q2B(_char):#全形轉半形
    if 65281<=ord(_char)<=65374:
        _char = chr(ord(_char)-65248)
    elif ord(_char)==12288:
        _char = chr(32)
    return _char
 
def isQ(Char):
    return True if (65281<=ord(Char)<=65374 or ord(Char)==12288) else False
 
def B2Q(_char):#半形轉全形
    if 33<=ord(_char)<=126:
        _char = chr(ord(_char)+65248)
    elif ord(_char)==32:
        _char = chr(12288)
    return _char
 
def isB(Char):
    return True if (33<=ord(Char)<=126 or ord(Char)==32) else False
 
#定義一個裝飾器,可有可無
def log(clean_html):
    def info(*args, **kw):
        print("The text after processing:")
        return clean_html(*args, **kw)
    return info
 
@log
def clean_html(html_str,special_char=None,to_char=None):
 
    #這裡留個介面,處理特殊字串
    if special_char:
        special_rule = re.compile('|'.join(set(special_char)))
        if not to_char:
            to_char = ''
 
    #CDATA 部分由 "<![CDATA[" 開始,由 "]]>" 結束:
    cdata_rule = re.compile(r'<![CDATA[.*]]>',re.I | re.S)
 
    #去除指令碼(隨時會出現)
    script_rule = re.compile(r'<script.*?</script>',re.I | re.S)
 
    #取出<head>..</head>和中間的內容,style也在裡面,不需要再寫了
    head_rule = re.compile(r'<head.*?/head>',re.I | re.S)
 
    #為了以防一些文字不是全部擷取html程式碼,還是寫一下以防萬一
    style_rule = re.compile(r'<style.*?/style>',re.I | re.S)
 
    #處理註釋
    comment_rule = re.compile(r'<!.*?>',re.I | re.S)
    
    #處理換行
    br_rule = re.compile(r'<br\s*?/{0,1}>',re.I)
 
    #html標籤
    html_rule = re.compile(r'<.*?/{0,1}>',re.I)
 
    if special_char:
        raw = special_rule.sub(to_char,html_str)
    else:
        raw = html_str
 
    raw = cdata_rule.sub('',raw)
    raw = script_rule.sub('',raw)
    raw = head_rule.sub('',raw)
    raw = style_rule.sub('',raw)
    raw = comment_rule.sub('',raw)
    raw = br_rule.sub('\n',raw)
    raw = html_rule.sub('',raw)
 
    global html_char
    letter_char = re.compile(r'&[a-z]+;',re.I)
    for char in letter_char.findall(raw):
        raw = re.sub(char,html_char[char],raw)
 
    number_char = re.compile(r'&#\d+;',re.I)
    for char in number_char.findall(raw):
        raw = re.sub(char,html_char[char],raw)
 
    raw_list = list(raw)
    for i in range(len(raw_list)):
        if isQ(raw_list[i]):
            raw_list[i] = Q2B(raw_list[i])
    raw = ''.join(raw_list)
    
    return raw
 
def test():
    test_html = """
        
    <div id="sidebar">
 
    <div id="tools">
    <h5 id="tools_example"><a href="/example/xmle_examples.asp">&nbsp;XML 例項,特殊字元:15(處理之後應該沒有了)</a></h5>
    <h5 id="tools_quiz"><a href="/xml/xml_quiz.asp">&#60;XML 測驗&gt;</a></h5>
    <h3>&#39;vevev&#39;</h3>
    </div>
 
    <div id="ad">
    <script type="text/javascript"><!--
    google_ad_client = "ca-pub-3381531532877742";
    /* sidebar-160x600 */
    google_ad_slot = "3772569310";
    google_ad_width = 160;
    google_ad_height = 600;
    //-->
    </script>
    <script type="text/javascript"
    src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
    </script>
    </div>
 
    </div>
    """
 
    print(clean_html(test_html,'】15'))
 
if __name__=='__main__':
    test()


進一步

其他指令碼引用

在python的其他程式中,可以直接from clean_html import clean_html進行方便的呼叫(假設這個指令碼名字為clean_html.py)

完善

  1. 補充字元實體(可以用爬蟲爬下來,有空弄一下)
  2. html標籤可能因為前端框架不同而有所差異(雖然不大)。但是都有規律,如<Vue>..</Vue>等,有規律,正則表示式就容易構建了