機器學習之字符正則匹配

           正則表達式 (Regular Expression) 又稱 RegEx, 是用來匹配字符的一種工具. 在一大串字符中尋找你需要的內容. 它常被用在很多方面, 比如網頁爬蟲, 文稿整理, 數據篩選等等. 最簡單的一個例子, 比如我需要爬取網頁中每一頁的標題. 而網頁中的標題常常是這種形式.

<title>我是標題</ title>

而且每個網頁的標題各不相同, 我就能使用正則表達式, 用一種簡單的匹配方法, 一次性選取出成千上萬網頁的標題信息. 正則表達式絕對不是一天就能學會和記住的, 因爲表達式裏面的內容非常多, 強烈建議, 現在這個階段, 你只需要瞭解正則裏都有些什麼, 不用記住, 等到你真正需要用到它的時候, 再反過頭來, 好好琢磨琢磨, 那個時候纔是你需要訓練自己記住這些表達式的時候.

如果需要找到潛在的多個可能性文字, 我們可以使用 [] 將可能的字符囊括進來. 比如 [ab] 就說明我想要找的字符可以是 a 也可以是 b. 這裏我們還需要注意的是, 建立一個正則的規則, 我們在 pattern 的 “” 前面需要加上一個 r 用來表示這是正則表達式, 而不是普通字符串. 通過下面這種形式, 如果字符串中出現 “run” 或者是 “ran”, 它都能找到.

# multiple patterns ("run" or "ran")
ptn = r"r[au]n"       # start with "r" means raw string
print(re.search(ptn, "dog runs to cat"))    # <_sre.SRE_Match object; span=(4, 7), match='run'>

同樣, 中括號 [] 中還可以是以下這些或者是這些的組合. 比如 [A-Z] 表示的就是所有大寫的英文字母. [0-9a-z] 表示可以是數字也可以是任何小寫字母.

print(re.search(r"r[A-Z]n", "dog runs to cat"))     # None
print(re.search(r"r[a-z]n", "dog runs to cat"))     # <_sre.SRE_Match object; span=(4, 7), match='run'>
print(re.search(r"r[0-9]n", "dog r2ns to cat"))     # <_sre.SRE_Match object; span=(4, 7), match='r2n'>
print(re.search(r"r[0-9a-z]n", "dog runs to cat"))  # <_sre.SRE_Match object; span=(4, 7), match='run'>

按類型匹配 

除了自己定義規則, 還有很多匹配的規則時提前就給你定義好了的. 下面有一些特殊的匹配類型給大家先總結一下, 然後再上一些例子.

  • \d : 任何數字
  • \D : 不是數字
  • \s : 任何 white space, 如 [\t\n\r\f\v]
  • \S : 不是 white space
  • \w : 任何大小寫字母, 數字和 “” [a-zA-Z0-9]
  • \W : 不是 \w
  • \b : 空白字符 (在某個字的開頭或結尾)
  • \B : 空白字符 (在某個字的開頭或結尾)
  • \\ : 匹配 \
  • . : 匹配任何字符 (除了 \n)
  • ^ : 匹配開頭
  • $ : 匹配結尾
  • ? : 前面的字符可有可無

模糊匹配(https://www.cnblogs.com/dangrui0725/p/9446639.html

# re模塊 正則表達式,對字符串進行模糊匹配
import re

# 元字符:. ^ $ * + ? {} [] | () \

# . 代表任意的一個符號,除了\n

# ^ 代表必須是從字符串的開頭進行匹配

# $ 代表必須是從字符串的結尾進行匹配

# * 代表按*左邊的字符進行匹配,包含0-無窮次 默認貪婪匹配,按最多的進行匹配

# + 代表按+左邊的字符進行匹配,包含1-無窮次 默認貪婪匹配,按最多的進行匹配

# ? 代表按?左邊的字符進行匹配,包含0-1次 默認貪婪匹配,按最多的進行匹配

# | 代表匹配左邊的字符或右邊的字符

# () 分組,將()中的字符作爲一個整體

# (?P<分組名>正則) 命名分組,將取到的結果放到相應的分組名下

# {} 定義匹配的範圍 默認貪婪匹配,按最多的進行匹配
# {0, }  表示0到窮次 相當於*
# {1, }  表示1到無窮次,相當於+
# {0,1}  表示0到1次,相當於?

# 將貪婪匹配改爲惰性匹配,只需要在上面幾個元字符後加?

# [] 字符集 字符集中的字符都是或的作用,在字符集有功能的符號是 - ^ \
# - 代表左邊的字符到右邊的字符
# [a-z] 代表a到z
# [A-Z] 代表A到Z
# [0-9] 代表0到9
# ^ 代表非
# [^a-z] 代表非a-z的所有字符

# \ 代表轉義符,後面跟元字符則去除特殊功能,後面跟某些字符實現特殊功能
# \d 匹配任何十進制數;相當於[0-9]
# \D 匹配任何非數字字符;相當於[^0-9]
# \s 匹配任何空白字符; 相當於[\t\n\r\f\v]
# \S 匹配任何非空白字符; 相當於[^\t\n\r\f\v]
# \w 匹配任何字母數字字符與下劃線;相當於[a-zA-Z0-9_]
# \W 匹配任何非字母數字字符與下劃線;相當於[^a-zA-Z0-9_]
# \b 匹配任何一個特殊字符邊界;如空格 & # 等
# 因爲的python中,\b代表轉義字符退格,當引入re模塊想使用\b來匹配任何一個特殊字符邊界時,應當使用\\b

下面就是具體的舉例說明啦.

# \d : decimal digit
print(re.search(r"r\dn", "run r4n"))           # <_sre.SRE_Match object; span=(4, 7), match='r4n'>
# \D : any non-decimal digit
print(re.search(r"r\Dn", "run r4n"))           # <_sre.SRE_Match object; span=(0, 3), match='run'>
# \s : any white space [\t\n\r\f\v]
print(re.search(r"r\sn", "r\nn r4n"))          # <_sre.SRE_Match object; span=(0, 3), match='r\nn'>
# \S : opposite to \s, any non-white space
print(re.search(r"r\Sn", "r\nn r4n"))          # <_sre.SRE_Match object; span=(4, 7), match='r4n'>
# \w : [a-zA-Z0-9_]
print(re.search(r"r\wn", "r\nn r4n"))          # <_sre.SRE_Match object; span=(4, 7), match='r4n'>
# \W : opposite to \w
print(re.search(r"r\Wn", "r\nn r4n"))          # <_sre.SRE_Match object; span=(0, 3), match='r\nn'>
# \b : empty string (only at the start or end of the word)
print(re.search(r"\bruns\b", "dog runs to cat"))    # <_sre.SRE_Match object; span=(4, 8), match='runs'>
# \B : empty string (but not at the start or end of a word)
print(re.search(r"\B runs \B", "dog   runs  to cat"))  # <_sre.SRE_Match object; span=(8, 14), match=' runs '>
# \\ : match \
print(re.search(r"runs\\", "runs\ to me"))     # <_sre.SRE_Match object; span=(0, 5), match='runs\\'>
# . : match anything (except \n)
print(re.search(r"r.n", "r[ns to me"))         # <_sre.SRE_Match object; span=(0, 3), match='r[n'>
# ^ : match line beginning
print(re.search(r"^dog", "dog runs to cat"))   # <_sre.SRE_Match object; span=(0, 3), match='dog'>
# $ : match line ending
print(re.search(r"cat$", "dog runs to cat"))   # <_sre.SRE_Match object; span=(12, 15), match='cat'>
# ? : may or may not occur
print(re.search(r"Mon(day)?", "Monday"))       # <_sre.SRE_Match object; span=(0, 6), match='Monday'>
print(re.search(r"Mon(day)?", "Mon"))          # <_sre.SRE_Match object; span=(0, 3), match='Mon'>

如果一個字符串有很多行, 我們想使用 ^ 形式來匹配行開頭的字符, 如果用通常的形式是不成功的. 比如下面的 “I” 出現在第二行開頭, 但是使用 r"^I" 卻匹配不到第二行, 這時候, 我們要使用 另外一個參數, 讓 re.search() 可以對每一行單獨處理. 這個參數就是 flags=re.M, 或者這樣寫也行 flags=re.MULTILINE.

string = """
dog runs to cat.
I run to dog.
"""
print(re.search(r"^I", string))                 # None
print(re.search(r"^I", string, flags=re.M))     # <_sre.SRE_Match object; span=(18, 19), match='I'>

重複匹配 

如果我們想讓某個規律被重複使用, 在正則裏面也是可以實現的, 而且實現的方式還有很多. 具體可以分爲這三種:

  • * : 重複零次或多次
  • + : 重複一次或多次
  • {n, m} : 重複 n 至 m 次
  • {n} : 重複 n 次

舉例如下:

# * : occur 0 or more times
print(re.search(r"ab*", "a"))             # <_sre.SRE_Match object; span=(0, 1), match='a'>
print(re.search(r"ab*", "abbbbb"))        # <_sre.SRE_Match object; span=(0, 6), match='abbbbb'>

# + : occur 1 or more times
print(re.search(r"ab+", "a"))             # None
print(re.search(r"ab+", "abbbbb"))        # <_sre.SRE_Match object; span=(0, 6), match='abbbbb'>

# {n, m} : occur n to m times
print(re.search(r"ab{2,10}", "a"))        # None
print(re.search(r"ab{2,10}", "abbbbb"))   # <_sre.SRE_Match object; span=(0, 6), match='abbbbb'>

分組 

我們甚至可以爲找到的內容分組, 使用 () 能輕鬆實現這件事. 通過分組, 我們能輕鬆定位所找到的內容. 比如在這個 (\d+) 組裏, 需要找到的是一些數字, 在 (.+) 這個組裏, 我們會找到 “Date: “ 後面的所有內容. 當使用 match.group() 時, 他會返回所有組裏的內容, 而如果給 .group(2) 里加一個數, 它就能定位你需要返回哪個組裏的信息.

match = re.search(r"(\d+), Date: (.+)", "ID: 021523, Date: Feb/12/2017")
print(match.group())                   # 021523, Date: Feb/12/2017
print(match.group(1))                  # 021523
print(match.group(2))                  # Date: Feb/12/2017

有時候, 組會很多, 光用數字可能比較難找到自己想要的組, 這時候, 如果有一個名字當做索引, 會是一件很容易的事. 我們字需要在括號的開頭寫上這樣的形式 ?P<名字> 就給這個組定義了一個名字. 然後就能用這個名字找到這個組的內容.

match = re.search(r"(?P<id>\d+), Date: (?P<date>.+)", "ID: 021523, Date: Feb/12/2017")
print(match.group('id'))                # 021523
print(match.group('date'))              # Date: Feb/12/2017

斷言

正則斷言的使用

  • 零寬斷言

    • 匹配寬度爲零,滿足一定的條件/斷言。
    • 零寬斷言用於查找在某些內容(但並不包括這些內容)之前或之後的東西,也就是說它們像\b,^,$那樣用於指定一個位置,這個位置應該滿足一定的條件(即斷言),因此它們也被稱爲零寬斷言。
    • 斷言用來聲明一個應該爲真的事實。正則表達式中只有當斷言爲真時纔會繼續進行匹配
  • 零寬斷言又分四種

    • 先行斷言(零寬度正預測先行斷言)

      • 表達式:(?=表達式)
      • 表示匹配表達式前面的位置
      • 先行斷言的執行步驟是這樣的先從要匹配的字符串中的最右端找到第一個ing(也就是先行斷言中的表達式)然後 再匹配其前面的表達式,若無法匹配則繼續查找第二個ing 再匹配第二個 ing前面的字符串,若能匹配 則匹配
      • .*(?=d) 可c以匹配abcdefghi 中的abc
    • 後發斷言(零寬度正回顧後發斷言)

      • 表達式: (?<=表達式)
      • 表示匹配表達式後面的位置
      • 後發斷言跟先行斷言恰恰相反 它的執行步驟是這樣的:先從要匹配的字符串中的最左端找到第一個abc(也就是先行斷言中的表達式)然後 再匹配其後面的表達式,若無法匹配則繼續查找第二個abc 再匹配第二個abc後面的字符串,若能匹配 則匹配
      • 例如(?<=abc).* 可以匹配abcdefg中的defg
    • 負向斷言

      • 負向零寬先行斷言 :(?!表達式)

      • 負向零寬後發斷言:(?<!表達式)

      • 負向零寬斷言 (?!表達式) 也是匹配一個零寬度的位置,不過這個位置的“斷言”取表達式的反值,例如 (?!表達式) 表示 表達式 前面的位置,如果 表達式 不成立 ,匹配這個位置;如果 表達式 成立,則不匹配:同樣,負向零寬斷言也有“先行”和“後發”兩種,負向零寬後發斷言爲 (?<!表達式)

參考鏈接:https://morvanzhou.github.io/tutorials/python-basic/basic/13-10-regular-expression/

https://www.cnblogs.com/he-qing-qing/p/11331080.html

2、BeautifulSoup

BeautifulSoup是一個可以從HTML或XML文件中提取數據的python庫,簡單來說,他能將HTML的標籤文件解析成樹形結構,然後方便地獲取到指定標籤的對應屬性。通過beautifulsoup庫,可以將指定的class或id值作爲參數直接獲取對應標籤的相關數據。

 


補:

正則表達式中re.match、re.search、re.findall,re.finditer的用法和區別
re.match語法:re.match(pattern,string,flags=0)  ##從字符起始位置匹配,若起始位置匹配不成功返回none。即只匹配起始位置

re.search可以掃描整個字符串並返回第一個成功的匹配。

re.findall可以找到所有滿足匹配條件的結果,並以列表的形式返回,而上面兩者只能返回一個值。

re.finditer返回string中所有與pattern相匹配的全部字串,返回形式爲迭代器。

å¨è¿éæå¥å¾çæè¿°

參考鏈接:https://blog.csdn.net/qq_42156420/article/details/80784673

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章