python基礎 之 正規表示式和re模組

ckxllf發表於2020-03-12

  1.正規表示式

  正規表示式,又稱規則表示式。(英語:Regular Expression,在程式碼中常簡寫為regex、regexp或RE),電腦科學的一個概念。正規表示式通常被用來檢索、替換那些符合某個模式(規則)的文字。——百度百科

  下面是正規表示式常見的使用場景:

  檢查字串的合法性

  驗證使用者名稱 (a-z,0-9,不能全是數字,不能全是字母)

  驗證郵箱格式 (xxx@qq.com)

  驗證電話號碼 (11位數字)

  驗證身份證 (18位 )

  驗證號碼格式(5-12純數字,第一位不能為0);

  提取字串中資訊

  提取一條簡訊中數字;

  提取檔名的字尾;

  採集器(網路爬蟲)

  替換字串

  替換字串中的非法字元;

  對電話號碼進行遮蔽;(1852****0102)

  替換佔位符 “hello {{name}} ” hello 王老二 (模板框架)

  分割字串

  將一個字串按照指定的規則進行分割;

  通俗地說,正則的功能是檢索特定形式的字串,物件是字串。

  1.1 元字元

  使用元字元匹配單個字元

  字元  功能

  .  匹配任意1個字元(除了\n)

  [ ]  匹配[ ]中列舉的字元

  \d  匹配數字,即0-9

  \D  匹配非數字,即不是數字

  \s  匹配空白,即 空格,tab鍵

  \S  匹配非空白

  \w  匹配單詞字元,即a-z、A-Z、0-9、_

  \W  匹配非單詞字元

  *  匹配前一個字元出現0次或者無限次,即可有可無

  +  匹配前一個字元出現1次或者無限次,即至少有1次

  import re

  text = '''

  這是用來匹配的字串

  from:1427319758@qq.com

  tel:88888888

  '''

  針對上述字串進行元字元的正則匹配演示

  使用點.匹配任意字元

  res = re.findall('.',text)

  print(res)

  執行結果(注意返回的是列表):

  ['這', '是', '用', '來', '匹', '配', '的', '字', '符', '串', 'f', 'r', 'o', 'm', ':', '1', '4', '2', '7', '3', '1', '9', '7', '5', '8', '@', 'q', 'q', '.', 'c', 'o', 'm', 't', 'e', 'l', ':', '8', '8', '8', '8', '8', '8', '8', '8']

  \d匹配數字

  res = re.findall('\d',text)

  print(res)

  執行結果:

  ['1', '4', '2', '7', '3', '1', '9', '7', '5', '8', '8', '8', '8', '8', '8', '8', '8', '8']

  + * 匹配多個字元

  res = re.findall('\d+',text)

  res_1 = re.findall('\d*',text)

  print(res,res_1)

  執行結果:

  ['1427319758', '88888888']

  ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '1427319758', '', '', '', '', '', '', '', '', '', '', '', '', '88888888', '', '']

  *匹配前一字元可以是0次,因此\d*會匹配每個不是數字的字元,即為空。\d+等同\d\d*

  [](字符集)

  人為規定只能匹配出現在字符集中的字元,如要找尋字串中的qq號,qq號不能以0開頭,且位數是5~12

  可用[1-9]\d{4,11}來限制

  郵箱可能出現為字串,可用[1-9a-zA-Z]\w+[@]\w+[.][a-zA-z]+可以匹配出任意常規格式的郵箱

  res = re.findall('[1-9a-zA-Z]\w+[@]\w+[.][a-zA-z]+','''

  1427319758@163.com開會就

  1427319758@edu.cn是否

  1427319758@xx.mail 阿薩德

  asdfbglsafhlf上單發順豐打打分

  ''')

  print(res)

  執行結果:

  ['1427319758@163.com', '1427319758@edu.cn', '1427319758@xx.mail']

  使用.*匹配任意多個字元

  res = re.findall('.*',text)

  print(res)

  執行結果:

  ['', '這是用來匹配的字串', '', 'from:1427319758@qq.com', '', 'tel:88888888', '', '']

  由於re.findall()函式預設遇到換行符 '\n’會終止當前的匹配,即不匹配換行符,每一行單獨匹配,因此會出現空元素。

  1.2 數量詞

  使用數量詞匹配多個字元

  字元  功能

  {m}  匹配前一個字元出現m次

  {m,n}  匹配前一個字元出現從m到n次

  限制匹配字元出現的次數

  res = re.findall('[1-9]\d{4,11}',text)

  res_1 = re.findall('([1-9]\d{4,11})@',text)

  print(res,res_1)

  執行結果:

  ['1427319758', '88888888'] ['1427319758']

  第一種方式匹配了同樣滿足規則的手機號,第二種方式是考慮到qq號隱藏在郵箱地址裡,所以在後面加了@來限制匹配的區域,即只匹配@前的第1位非0的5-12位純數字。

  1.3 精確匹配與泛匹配

  泛匹配

  泛匹配是匹配包括特徵字元在內的所有的東西

  res = re.findall('Hello.*like','Hello world! I like python!')

  print(res)

  執行結果:

  ['Hello world! I like']

  精確匹配

  精確匹配是匹配括號裡面的東西

  res_1 = re.findall('Hello(.*)like','Hello world! I like python!')

  print(res_1)

  執行結果:

  [' world! I ']

  我想要Hello和like之間的字元,泛匹配的結果會包含首尾的特徵字元Hello和like;而精確匹配只會得到特徵字元之間的字串。

  1.4 貪婪匹配與非貪婪匹配

  Python裡數量詞預設是貪婪的(在少數語言裡也可能是預設非貪婪),總是嘗試匹配儘可能多的字元;

  非貪婪則相反,總是嘗試匹配儘可能少的字元。

  在"*","?","+","{m,n}"後面加上?,使貪婪變成非貪婪。

  res = re.findall('Hello(.*)like','Hello world! I like python! I like you!')

  res_1 = re.findall('Hello(.*?)like','Hello world! I like python! I like you!')

  print(res,res_1)

  執行結果:

  [' world! I like python! I ']

  [' world! I ']

  res返回的是貪婪匹配,它會在找到Hello後,匹配儘可能多的字元,然後在最後一個like停下;

  res_1返回的是非貪婪匹配,它在找到Hello後,只匹配第一個like前的字元。

  2. re模組

  一直以來我們都是使用 re.search() 函式,其實在正規表示式模組中還有一些函式可以很方便的對字串進行操作。re模組的使用可以分為兩種:第一種是物件式的方式,第二種是函式式的方式。

  2.1 re.match

  match() 用於查詢字串的頭部(也可以指定起始位置),它是一次匹配,只要找到了一個匹配的結果就返回,而不是查詢所有匹配的結果。它的一般使用形式如下:

  match(pattern, string[, flag])

  其中,pattern是正規表示式規則字串,string 是待匹配的字串,flag 是可選引數。

  當匹配成功時,返回一個 Match 物件,如果沒有匹配上,則返回 None。

  import re

  pattern = 'Python'

  string = 'dsgfaPythonahsdgjasghPythonasdjajsk'

  result = re.match(pattern,string)

  result_1 = re.match(pattern,string[5:])

  print(result,result_1)

  執行結果:

  None

  <_sre.SRE_Match object; span=(0, 6), match='Python'>

  2.2 re.search

  search() 用於查詢字串的任何位置,它也是一次匹配,只要找到了一個匹配的結果就返回,而不是查詢所有匹配的結果,它的一般使用形式如下:

  search(pattern, string[, flag])

  當匹配成功時,返回一個 Match 物件,如果沒有匹配上,則返回 None。

  ret = re.search('\d+', "python = 9999, c = 7890, c++ = 12345")

  print(ret.group())

  執行結果:

  9999

  2.3 re.findall 劃重點!

  上面的 match 和 search 方法都是一次匹配,只要找到了一個匹配的結果就返回。然而,在大多數時候,我們需要搜尋整個字串,獲得所有匹配的結果。findall() 的使用形式如下:

  findall(pattern, string[, flag])

  findall() 以列表形式返回全部能匹配的子串,如果沒有匹配,則返回一個空列表。

  ret = re.findall(r"\d+", "python = 9999, c = 7890, c++ = 12345")

  print(ret)

  執行結果:

  ['9999', '7890', '12345']

  2.4 re.split

  split()按照能夠匹配的子串將字串分割後返回列表,它的使用形式如下:

  split(pattern, string[, maxsplit, flags])

  其中,maxsplit 用於指定最大分割次數,不指定將全部分割。

  '''

  split():

  分割字串 去掉了匹配到的字串

  結果是列表形式

  maxsplit: 預設是0 表示全部切割

  1 代表切割一次

  2 代表切割兩次

  '''

  pattern = '\d+'

  string = 'Pythonasdkjasd464654adhuiaghsdk564654akjsdhkashdkja'

  result = re.split(pattern,string,2)

  print(result)

  執行結果:

  ['Pythonasdkjasd', 'adhuiaghsdk', 'akjsdhkashdkja']

  實際上就是面向字串操作的string.split的升級版

  2.5 re.sub

  sub()用於替換,使用形式如下:

  sub(pattern, repl, string[, count, flags])

  第一個引數為對應的正規表示式,第二個引數為要替換成的字串,第三個引數為源字串,第四個引數為可選項,代表最多替換的次數,如果忽略不寫,則會將符合模式的結果全部替換。

  pattern = 'Java'

  repl = '********'

  string = 'kjasdJavaadhuiaghsdkJavaakjsd'

  result = re.sub(pattern,repl,string,1)

  print(result)

  執行結果:

  kjasd********adhuiaghsdkJavaakjsd

  string.replace的升級版

  引數flags

  方法1:

  ret = re.sub("\d+", '18', "age = 12")

  print(ret)

  執行結果:

  age = 18

  方法2 用函式:

  re.sub()的本質是在字串中檢索符合pattern格式的子串,然後把子串當做引數輸入repl中,預設的repl功能可以看成是函式,即不論我輸入的子串是什麼樣的,輸出都用repl引數代替,此處repl無法跟子串產生關係

  def replace(string,repl):

  return repl

  如果我們要讓輸出的repl和子串產生關係,如將字串中的電話號碼15654862043輸出成1565****043,僅僅透過設定一個repl字串是不能實現的,就需要在repl處傳入一個函式。

  import re

  text = '''

  15654561654

  13905641750

  15646575635

  18976534547

  '''

  def replace(string):

  string = string.group()

  repl = string[0:4] + '****' + string[-4:-1]

  return repl

  ret = re.sub("\d+", repl = replace, string = text)

  print(ret)

  執行結果:

  1565****165

  1390****175

  1564****563

  1897****454

  2.6 re.compile

  使用 compile() 函式將正規表示式的字串形式編譯為一個 Pattern 物件。透過該物件提供的一系列方法對文字進行匹配查詢,獲得匹配結果(Match物件)。編譯可以實現更高效的匹配查詢等。

  compile()函式

  compile() 函式用於編譯正規表示式,生成一個 Pattern 物件,它的一般使用形式如下:

  import re

  # 將正規表示式編譯成 Pattern 物件

  pattern_1 = re.compile('\d+', re.S)

  pattern_2 = re.compile('\D+', re.l)

  pattern_3 = re.compile('\w+', re.S)

  之前定義pattern都是不包括flags引數的,因此不用re.compile,僅用賦值語句讓pattern = ‘\d+’也能實現,compile函式的優點在於:1.可以包含flags引數;2.形成模組,便於後續複用

  results1 = re.findall(pattern_1, '540775360@qq.com')

  results2 = re.findall(pattern_2, "python = 9999, c = 7890, c++ = 12345")

  results3 = re.findall(pattern_3, "python = 997")

  print(results1, results2, results3)

  2.7 原生字串

  >>> mm = "c:\\a\\b\\c"

  >>> mm

  'c:\\a\\b\\c'

  >>> print(mm)

  c:\a\b\c

  >>> re.match("c:\\\\",mm).group()

  'c:\\'

  >>> ret = re.match("c:\\\\",mm).group()

  >>> print(ret)

  c:\

  >>> ret = re.match("c:\\\\a",mm).group()

  >>> print(ret)

  c:\a

  >>> ret = re.match(r"c:\\a",mm).group()

  >>> print(ret)

  c:\a

  >>> ret = re.match(r"c:\a",mm).group()

  Traceback (most recent call last):

  File "", line 1, in

  AttributeError: 'NoneType' object has no attribute 'group'

  >>> 鄭州做流產多少錢

  Python中字串前面加上 r 表示原生字串,

  與大多數程式語言相同,正規表示式裡使用"\"作為跳脫字元,這就可能造成反斜槓困擾。假如你需要匹配文字中的字元"\\",那麼使用程式語言表示的正規表示式裡將需要4個反斜槓"\":前兩個和後兩個分別用於在程式語言裡轉義成反斜槓,轉換成兩個反斜槓後再在正規表示式裡轉義成一個反斜槓。

  Python裡的原生字串很好地解決了這個問題,有了原生字串,你再也不用擔心是不是漏寫了反斜槓,寫出來的表示式也更直觀。

  >>> ret = re.match(r"c:\\a",mm).group()

  >>> print(ret)

  c:\a

  2.8 匹配開頭結尾

  字元  功能

  ^  匹配字串開頭

  $  匹配字串結尾

  末尾匹配

  需求:匹配163.com的郵箱地址

  #coding=utf-8

  import re

  email_list = ["xiaoWang@163.com", "xiaoWang@163.comheihei", ".com.xiaowang@qq.com"]

  for email in email_list:

  ret = re.match("[\w]{4,20}@163\.com", email)

  if ret:

  print("%s 是符合規定的郵件地址,匹配後的結果是:%s" % (email, ret.group()))

  else:

  print("%s 不符合要求" % email)

  執行結果:

  xiaoWang@163.com 是符合規定的郵件地址,匹配後的結果是:xiaoWang@163.com

  xiaoWang@163.comheihei 是符合規定的郵件地址,匹配後的結果是:xiaoWang@163.com

  .com.xiaowang@qq.com 不符合要求

  完善後

  email_list = ["xiaoWang@163.com", "xiaoWang@163.comheihei", ".com.xiaowang@qq.com"]

  for email in email_list:

  ret = re.match("[\w]{4,20}@163\.com$", email)

  if ret:

  print("%s 是符合規定的郵件地址,匹配後的結果是:%s" % (email, ret.group()))

  else:

  print("%s 不符合要求" % email)

  執行結果:

  xiaoWang@163.com 是符合規定的郵件地址,匹配後的結果是:xiaoWang@163.com

  xiaoWang@163.comheihei 不符合要求

  .com.xiaowang@qq.com 不符合要求

  這個例子只用於展示,用來匹配郵箱是沒有意義的。因為它匹配不出末尾不是com但包含了郵箱資訊的多行字串,如

  '''

  xiaoWang@163.com

  xiaoKang@163.com

  以上就是郵箱

  '''

  萬能正則

  (.*?) 匹配除了換行以外的任意字串。無論長短,最多匹配一次,非貪婪匹配。

  這個正規表示式可以解決你想要提取的大部分資料,在寫正規表示式的時候可以首先嚐試這個組合,也許能達到事半功倍的效果。並且常常結合re.findall()函式。

  2.9 案例:抓取電影天堂資料

  '''

  電影天堂思路:

  1. 進入最新的電影更多 --> 更多的第一頁

  2. 翻頁 {}.html

  1. > 提取每一頁的資料電影的詳情頁網址

  2. > 傳送請求 得到響應

  3. > 正則提取連結

  4. > 儲存資料(檔案)

  抓取之前儘量熟悉網頁的佈局和結構!! 熟悉網址的關係, 經常查詢網頁原始碼中的資料(Ctrl+F).

  '''

  import re

  import requests

  for page in range(1, 5):

  url_list = f'{page}.html'

  # 找到詳情頁的網址 先進入 列表頁

  r_list = requests.get(url_list)

  # 指定編碼

  r_list.encoding = 'gb2312'

  # 提取詳情頁的網址 返回列表

  url_detail = re.findall('', r_list.text)

  for u in url_detail:

  url = ' + u

  # print(url)

  # 再次發請求 得到詳情頁的響應

  response = requests.get(url)

  # 也會亂碼

  response.encoding = 'gb2312'

  # 提取資料

  result = re.findall('.*?', response.text)[0:]

  print(result)

  try:

  with open('dytt.txt', 'a', encoding='utf-8') as fp:

  # write 只能字串 和 二進位制的 不能寫字典 列表 等

  fp.write(result[0]+'\n')

  except:

  print('沒有提取到資料!!')

  '''

  歌曲下載:

  可見即可爬 電影(VIP) 歌曲

  思路:

  1. 抓包 找到翻頁

  2. 進入上面的網址 提取歌曲id

  3. 下載歌曲 {id}/mp3/1

  '''

  import re

  import requests

  for page in range(1, 3): # 1, 2

  # 翻頁的

  url_song = f'{page}&pageSize=20&order=hot'

  # 傳送請求得到響應 提取響應中的歌曲ID

  response_song = requests.get(url_song)

  # 提取ID 返回列表

  id_songs = re.findall('value="(\d+)">

  # 遍歷歌曲的id 然後下載

  for ids in id_songs:

  song_url = '{}/mp3/1'.format(ids)

  try:

  # 請求歌曲的網址 然後得到響應

  response = requests.get(song_url, timeout=5)

  # 儲存歌曲

  with open(f'{ids}.mp3', 'wb') as fp:

  fp.write(response.content)

  except:

  print(f'這個歌曲{ids}出錯')


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69945560/viewspace-2679777/,如需轉載,請註明出處,否則將追究法律責任。

相關文章