1. 程式人生 > >python筆記-正則表示式

python筆記-正則表示式

萬用字元的使用

* : 0個到無窮多個任意字元
? :一個必定存在的任意字元
. :當前路徑
.. :上一級路徑
[0-9]:0到9所有數字
[a-z]:a到z所有小寫字母
[A-Z]:A到Z所有大寫字母
[a-zA-Z]:所有字母

官方參考程式碼:

>>> import glob
>>> glob.glob('./[0-9].*')
['./1.gif', './2.txt']
>>> glob.glob('*.gif')
['1.gif', 'card.gif']
>>> glob.glob('?.gif')
['1.gif']
>>> glob.glob('**/*.txt', recursive=True)
['2.txt', 'sub/3.txt']
>>> glob.glob('./**/', recursive=True)
['./', './sub/']


>>> import glob
>>> glob.glob('*.gif')
['card.gif']
>>> glob.glob('.c*')
['.card.gif']

正則表示式常用邏輯及方法

#     re: regular experssion ==== 正則表示式

# 作用: 對於字串進行處理, 會檢查這個字串內容是否與你寫的正則表示式匹配,
        如果匹配, 拿出匹配的內容;
        如果不匹配, 忽略不匹配內容;
        
#  學習正則的規則:

import re

s = "kiosk/home/kiosk/westosanaconda2/envs/blog/bin/python3.6/home/kiosk/Desktop/day25"
# s.find('westos')

# ***************************findall方法********************************
# 1. 編寫正則的規則
pattern1 = r'westos'
pattern2 = r'kiosk'

# 2. 通過正則去查詢匹配的內容
print(re.findall(pattern1, s))
print(re.findall(pattern2, s))

# ************************match********************************
#   match嘗試從字串的起始位置開始匹配,
#       - 如果起始位置沒有匹配成功, 返回一個None;
#       - 如果起始位置匹配成功, 返回一個物件;
print(re.match(pattern1, s))
matchObj = re.match(pattern2, s)
# 返回match匹配的字串內容;
print(matchObj.group())

# **********************search**********************
#  search會掃描整個字串, 只返回第一個匹配成功的內容;
#       - 如果能找到, 返回一個物件, 通過group方法獲取對應的字串;
match1Obj = re.search(pattern1, s)
print(match1Obj.group())
match2Obj = re.search(pattern2, s)
print(match2Obj.group())

正則表示式特殊字元類

字元匹配:
  r'westos'
字元類:
[pP]ython
westos[pP]
[aeiou]
[a-z]
[A-Z]
[a-zA-Z0-9]
[^aeiou]
[^0-9]
特殊字元類:
.: 匹配除了\n之外的任意字元; [.\n]
\d:  digit--(數字), 匹配一個數字字元, 等價於[0-9]
\D: 匹配一個非數字字元, 等價於[^0-9]
\s:  space(廣義的空格: 空格, \t, \n, \r), 匹配單個任何的空白字元;
\S:  匹配除了單個任何的空白字元;
\w:  字母數字或者下劃線, [a-zA-Z0-9_]
\W: 除了字母數字或者下劃線, [^a-zA-Z0-9


import  re
# 字元類:
print(re.findall(r'[^0-9]', 'westos123westos'))
print(re.findall(r'[0-9]', 'westos123westos'))

# 特殊字元類 .
print(re.findall(r'.', 'westos\n'))


# 特殊字元類\d
print(re.findall(r'\d', '當前文章閱讀量為8'))
# 等待學習匹配次數的規則
print(re.findall(r'\d', '當前文章閱讀量為8000'))


# 特殊字元類\D
print(re.findall(r'\D', '當前文章閱讀量為8'))
# 等待學習匹配次數的規則
print(re.findall(r'\D', '當前文章閱讀量為8000'))



# 特殊字元類\s, \S
print(re.findall(r'\s', '\n當前\r文章閱\t讀量為8'))
# 等待學習匹配次數的規則
print(re.findall(r'\S', '\n當前\r文章閱\t讀量為8'))



# 特殊字元類\w, \W
print(re.findall(r'\w', '12當前westos文章閱_讀量為8&'))
# 等待學習匹配次數的規則
print(re.findall(r'\W', '12當前westos文章閱_讀量為8&'))

在這裡插入圖片描述

匹配IP

匹配字元出現次數:
*: 代表前一個字元出現0次或者無限次;    d*,  .*
+: 代表前一個字元出現一次或者無限次;     d+
?: 代表前一個字元出現1次或者0次;   假設某些字元可省略, 也可以不省略的時候使用
第二種方式:
{m}: 前一個字元出現m次;
{m,}: 前一個字元至少出現m次;  * == {0,}; + ==={1,}
{m,n}: 前一個字元出現m次到n次; ? === {0,1}
import re

pattern = r'[1-9]\d{0,2}\.[1-9]\d{0,2}\.[1-9]\d{0,2}\.[1-9]\d{0,2}'
print(re.findall(pattern, '172.25.0.2'))
print(re.findall(pattern, '172.25.1.2'))
print(re.findall(pattern, '172.25.1.278'))

#  | 代表或者的意思
pattern1 = r'^(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)$'
# print(re.findall(pattern1, '172.25.1.278'))
# print(re.findall(pattern1, '172.25.1.178'))

Obj = re.match(pattern1, '172.25.1.178')
if Obj:
    print("查詢到匹配的內容:", Obj.group())
else:
    print('No Found')

Obj = re.match(pattern1, '172.25.1.278')
if Obj:
    print("查詢到匹配的內容:", Obj.group())
else:
    print('No Found')

在這裡插入圖片描述

獲取貼吧總頁數

from itertools import chain
from urllib.request import urlopen
import re
def getPageContent(url):
    """
        獲取網頁原始碼
    :param url: 指定url內容
    :return: 返回頁面的內容(str格式)
    """
    with urlopen(url) as html:
        return  html.read().decode('utf-8')

def parser_page(content):
    """
    根據內容獲取所有的貼吧總頁數;
    :param content: 網頁內容
    :return: 貼吧總頁數
    """
    pattern = r'<span class="red">(\d+)</span>'
    data = re.findall(pattern, content)
    return  data[0]


def parser_all_page(pageCount):
    """
    根據貼吧頁數, 構造不同的url地址;並找出所有的郵箱
    :param pageCount:
    :return:
    """
    emails = []
    for page in range(int(pageCount)):
        url = 'http://tieba.baidu.com/p/2314539885?pn=%d' %(page+1)
        print("正在爬取:%s" %(url))
        content = getPageContent(url)
        # pattern = r'\w[-\w.+]*@[A-Za-z0-9][-A-Za-z0-9]+\.+[A-Za-z]{2,14}'
        pattern = r'[a-zA-Z0-9][-\w.+]*@[A-Za-z0-9][-A-Za-z0-9]+\.+[A-Za-z]{2,14}'
        findEmail = re.findall(pattern, content)
        print(findEmail)
        emails.append(findEmail)
    return  emails
def main():
    url = 'http://tieba.baidu.com/p/2314539885'
    content = getPageContent(url)
    pageCount = parser_page(content)
    emails = parser_all_page(pageCount)
    print(emails)
    with open('tiebaEmail.txt', 'w') as f:
        for tieba in chain(*emails):
            f.write(tieba + '\n')
main()

在這裡插入圖片描述

正則中需要轉義的字元

因為這些字元在正則中有特殊含義, 所有必須轉義: ., +, ?, * 表示分組: | : 匹配| 左右任意一個表示式即可; (ab): 將括號中的字元作為一個分組 \num: 引用分組第num個匹配到的字串 (?P): 分組起別名

import  re
# 當使用分組時, findall方法只能獲取到分組裡面的內容;
print(re.findall(r'(westos|hello)\d+', 'westos1hello2'))
pattern  = r'<span class="red">(\d+)</span>'
s = '<span class="red">31</span>'
print(re.findall(pattern, s))

#
pattern  = r'(<span class="red">(\d+)</span>)'
s = '<span class="red">31</span>'
print(re.findall(pattern, s))

print(re.findall(r'((westos|hello)\d+)', 'westos1hello2'))

# findall不能滿足時, 考慮使用search 或者match
Obj = re.search(r'(westos|hello)(\d+)', 'westos1hello2')
if Obj:
    print(Obj.group())
    print(Obj.groups())
else:
    print('Not Found')
# 匹配IP
pattern = r'(([1-9]\d{0,2}\.){3}[1-9]\d{0,2})'
print(re.findall(pattern, '172.25.0.3'))
print(re.findall(pattern, '172.25.3.3')[0][0])


# \num
s = '<html><title>正則</title></html>'
# 目前有三個分組, \1: 代指第一個分組的內容, \2: 代指第一個分組的內容,
pattern = r'<(\w+)><(\w+)>(\w+)</\w+></\w+>'
print(re.findall(pattern, s))

s = '<html><title>正則</tite></html>'
# 目前有三個分組, \1: 代指第一個分組的內容, \2: 代指第一個分組的內容,
pattern = r'<(\w+)><(\w+)>(\w+)</\2></\1>'
print(re.findall(pattern, s))

s1 = 'http://www.westos.org/linux/book/'
pattern = 'http://[\w\.]+/(?P<courseName>\w+)/(?P<courseType>\w+)/'

Obj = re.match(pattern, s1)
if Obj:
    print(Obj.group())
    print(Obj.groups())
    print(Obj.groupdict())
else:
    print('Not Found')


#     身份證號: 610 897 19900415 4534
s = '610000199001013456'
pattern = r'(?P<Province>\d{3})[\s-]?(?P<City>\d{3})[\s-]?(?P<Year>\d{4})[\s-]?' \
          r'(?P<Month>\d{2})(?P<Day>\d{2})(\d{4})'
Obj = re.search(pattern, s)
if Obj:
    print(Obj.groupdict())
else:
    print('Not Found')

在這裡插入圖片描述

爬取指定貼吧圖片

import re
from urllib.request import urlopen

def get_content(url):
    """
        獲取網頁內容
    :param url:
    :return:
    """
    with urlopen(url) as html:
        return  html.read()

def parser_get_img_url(content):
    """
    解析貼吧內容, 獲取所有風景圖片的url
    :param content:
    :return:
    """
    pattern = r'<img class="BDE_Image".*?src="(http://.*?\.jpg)".*?>'
    imgUrl = re.findall(pattern, content.decode('utf-8').replace('\n', ' '))
    return  imgUrl

def main():
    url = 'http://tieba.baidu.com/p/5437043553'
    content = get_content(url)
    imgLi = parser_get_img_url(content)
    for index,imgurl in enumerate(imgLi):
        # 根據圖片的url獲取每個圖片的內容;
        content = get_content(imgurl)
        with open('img/%s.jpg' %(index+1), 'wb') as f:
            f.write(content)
            print("第%s個圖片下載成功...." %(index+1))
main()

在這裡插入圖片描述

批量替換和批量分隔符分離


# split()方法: 指定多個分隔符進行分割;
import re
ip = '172.25.254.250'
print(ip.split('.'))
s = '12+13-15/16'
print(re.split(r'[\+\-\*/]', s))


#
s = 'westos is a company'
print(s.replace('westos', 'fentiao'))

# 希望替換的時數字, 但數字的值不固定, 則通過正則來實現;
s = "本次轉發數為100"
print(re.sub(r'\d+', '0', s))


# 自動會給addNum傳遞一個引數, 引數時匹配到的SRE物件
def addNum(sreObj):
    """在原有基礎上加1"""
    # 年末任情況字串中匹配到的內容還是字串
    num = sreObj.group()  # ‘100’ ‘99’
    new_num = int(num) + 1
    return  str(new_num)

s1 = "本次轉發數為100, 分享數量為99"
print(re.sub(r'\d+', addNum, s1))

在這裡插入圖片描述