Python第七課----正則和日誌分析
阿新 • • 發佈:2017-11-07
python
一、正則表達式:
1、分類: 1、BRE基本正則,grep、sed,vi等軟件支持,vim有擴展 2、ERE擴展正則,egrep、grep-E,sed-r等 3、PCRE最重要的,高級語言中的
2、基本語法:
1、元字符 metacharacter
.
匹配除了換行符外任意一字符,叠代所有
.
[abc]
字符“集合”,只能從中拿出一個,然後一個個匹配一個位置
plain,匹配出a
[^abc]
字符集合,表示一個字符位置,匹配非abc
plain,匹配出plin
[a-z]
小寫字母集合,一個
[^a-z]
非小寫字母,一個
\b
匹配單詞的邊界
\B
不匹配單詞的邊界
\d
[0-9]匹配以為數字
\D
[^0-9]匹配一位非數字
\s
匹配一位空白字符,包括換行符,制表符,空格[\f\r\n\t\v]
\S
匹配一位非空白字符
\w
匹配[a-zA-Z0-9_],包括中文的字,多個匹配
\W
匹配\w之外的字符
2、單行模式: .可以匹配所有字符,包括換行符 ^表示整個字符的開頭,$表示整個字符的結尾
3、多行模式: .可以匹配除了換行符之外的字符 ^表示行首,行尾$ ^表示整個字符串的開始,$表示整個字符串的結尾. 開始指的的\n後緊接著的下一個字符,結束是指的/n前的字符 註意:註意字符串中看不見的換行符\r\n會影響e$的測試,e$只能匹配e\n
4、舉例 very very very happy harry key
5、轉義 特殊含義的符號,想使用本意,使用\,如果是\本身,則使用\ \r、\n還是轉義後代表回車換行
6、重復
*
表示前面的正則表達式重復0次或多次
e\w*,單詞中有e後面非空白字符
+
表示前面的正則表達式重復至少一次
e\w+,單詞中e,後面至少一個非空
?
表示前面的正則表達式0次或1次
e\w?,單詞中e,後面0個或1個非空
{n}
重復固定的n次
e\w{5},單詞e,後面5個非空
{n,}
重復至少n次
e\w{1,},單詞e,後面至少1個非空
{n,m}
重復n到m次
e\w{1,10},單詞e後面至少1至多10非空
7、手機號:\d{11}\d{3,4}-\d{7,8}
代碼
說明
舉例
x|y
匹配x或者y
wood,foodw|food或(w|f)ood
(pattern)
小括號指定一個分組,改變優先級,引入一個分組,1開始
\b(a|b)\w+
\數字
匹配對應的分組
(very) \1,捕獲的是very very
(?:pattern)
?:不要分組
(?<name>exp)(?‘name‘exp)
分組捕獲,但是可以通過name來訪問Python語法必須是(?P<name>exp)
(?=exp)
斷言exp一定在匹配的右邊出現,也就是說斷言後面一定要出現exp
f(?=oo)f後一定要出現oo
(?<=exp)
斷言exp一定出現在匹配的前面出現,一定有個exp前綴
(?<=f)ood,ood前面一定有個f
(?!exp)
斷言exp一定不會出現在右側,也就是後面一定不是exp
foo(?!d)foo後面一定不是d
(?<!exp)
斷言exp一定不會出現在左側,也就是前面一定不是exp
(?<!f)ood,ood前面一定不是f
(?#comment)
註釋
9、貪婪與非貪婪 1、默認貪婪模式,盡可能的匹配更長的字符串 2、非貪婪模式,在重復的符號後面,加上一個?問好,就盡量的少匹配了
*?
匹配任意次,但盡可能的少重復
f\w?k-----fk
+?
匹配至少一次,但盡可能的少重復
f\w+?k這個沒有
??
匹配0次或1次,但盡可能的少重復
f\w??k 匹配0次或一次的?,這個是fk
{n,}?
匹配至少n次,但盡可能的少重復
f\w{1,}?k 得到fook
{n,m}?
匹配至少n次,至多m次,但盡可能的少重復
f.{1,111}?k,f.{1,17}?k,f.{1,}?k,f.{1,3}?k
very very very very ---->v.*y,v.*?y
10、引擎選項:
Python
IgnoreCase
匹配時忽略大小寫
re.Ire.IGNORECASE
SingleLine
單行模式,.可以匹配所有,包括\n
re.Sre.DOTALL
Multiline
多行模式^行首$行尾
re.Mre.MULTILINE
IgnorePatternWhitespace
忽略表達式中的空白字符,然後要使用空白,需要轉義
re.Xre.VERBOSE
11、匹配0-999的數字: ^([1-9]\d\d?|\d)(?!w+)\r$ ^([1-9]\d\d?|\d)\r$
12、IP地址 (?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)
二、方法re模塊
1、編譯 re.compile(pattern,flags=0)設定flag--編譯模式,返回正則表達式對象regex pattern是正則表達式字符串(‘\d+‘)這種,編譯後的結果被保存
2、單次匹配 re.match(pattern,string,flags=0) regex.match(string[.pos[,endpos]]) 這兩個返回結果一樣,而且都是從頭開始,如果不設置pos的話
s = ‘1234abc‘ Match regex = re.compile(‘\d+‘) matcher = re.match(‘\d+‘,s) match只找一次 print(matcher) matcher = regex.match(s) 匹配從頭開始,開始沒有就是None print(matcher) regex = re.compile(‘[ab]‘) matcher = regex.match(s,4) 可以設定已知的pos和endpos print(matcher) Search matcher = re.search(‘[ab]‘,s) search是全局搜索,不過只找一次 print(matcher) matcher = regex.search(s) 也可以輸入pos,減少時間 print(matcher) Fullmatch 正則表達式必須能取到全部才可以 regex = re.compile(‘[ab]‘) matcher = re.fullmatch(‘[ab]‘,s) 這樣為None,因為全長需要\w+ print(matcher) matcher = regex.fullmatch(s) print(matcher)
3、全部匹配 re.findall(pattern,string,flags=0) 找到全部 regex.findall(string[,pos[,endpos]])
s = ‘bottle\nbag\nbig\nable‘ regex = re.compile(‘b‘) result = regex.findall(s) 找到四個 print(result) result = regex.finditer(s) 是個叠代器 next() print(result) s = ‘bottle\nbag\nbig\nable‘ regex = re.compile(‘^b\w+‘,re.M) result = regex.findall(s) print(result)
4、匹配替換 re.sub(pattern,replacement,string,count=0,flags=0) regex.sub(replacement,string,count=0) 使用pattern對string匹配,對匹配項repl re.sub(pattern,replacement,string,count=0,flags=0) regex.sub(replacement,string,count=0)
s = ‘bottle\nbag\nbig\nable‘ regex = re.compile(‘b\wg‘) result = regex.sub(‘magedu‘,s) print(result) # 全部替換 result = regex.subn(‘magedu‘,s,1) print(result) # 替換1次 s = ‘‘‘01 bottle 02 bag 03 big1 100 able‘‘‘ regex = re.compile(‘\s+|(?<!\w)\d+‘) # big1的問題 # regex = re.compile(‘\s\d+\s+‘) 這個需要在第一行加空格 result = regex.split(s) print(result)
5、分組 1、使用()的pattern捕獲的數據放到了組group中 2、使用了分組後,在match對象中,可以看到分組 3、group(N)方式返回對應的分組,1-N是對應的分組,0返回整個匹配的字符串 4、如果是用了name,可以使用group(‘name’)的方式取分組 5、group()返回所有分組 6、使用groupdict()返回所有命名的分組
s = ‘‘‘01 bottle 02 bag 03 big12 100 a0ble‘‘‘ regex = re.compile(‘(b)(\w)(g)‘) result = regex.finditer(s) for x in result: print(x.groups()) print(x.group(1)) print(x.group(2)) print(‘~~~~~~~~~‘)
6、匹配郵箱地址:^\w[\w\.-]*@\w[\w\.-]*\.[a-z]{2,3}
7、匹配html標記內的內容(取反的思想) <a herf="www.magedu.com"; traget=‘_blank‘>馬哥<br>教育</a><font>abc</font> <a[^<>]*>(.+)</a>取馬哥教育 <a[^<>]*herf=([^<>]+)>
8、身份證:^\d{17}[xX\d]|^\d{15}$
三、日誌分析項目:
import random import datetime import time from queue import Queue import threading import re from pathlib import Path from user_agents import parse # 數據源 ops = { ‘datetime‘:lambda timestr:datetime.datetime.strptime(timestr,‘%d/%b/%Y:%H:%M:%S %z‘), ‘status‘:int, ‘length‘:int, ‘request‘:lambda request:dict(zip((‘method‘,‘url‘,‘protocol‘),request.spilt())), ‘useragent‘:lambda useragent:parse(useragent) } pattern = ‘‘‘(?P<remote>[\d\.]{7,15}) - - \[(?P<datetime>[^\[\]]+)\] "(?P<method>\w+) (?P<url>\S+) (?P<protocol>[\w/\d.]+)" (?P<status>\d+) (?P<size>\d+) "[^"]+" "(?P<useragent>[^"]+)"‘‘‘ regex = re.compile(pattern) def extract(line): ‘‘‘返回字段的字典,返回None則表示匹配失敗‘‘‘ matcher = regex.match(line) info = None if matcher: info = {k:ops.get(k,lambda x:x)(v) for k,v in matcher.groupdict().items()} return info def openfile(path:str): with open(path) as f: for line in f: fields = extract(line) if fields: yield fields else: continue def load(*paths): ‘‘‘裝載日誌文件‘‘‘ for item in paths: p = Path(item) if not p.exists(): continue if p.is_dir(): for file in p.iterdir(): if file.is_file(): yield from openfile(str(file)) elif p.is_file(): yield from openfile(str(p)) # def source(): # 演示代碼 # while True: # yield {‘value‘:random.randint(1,100),‘datetime‘:datetime.datetime.now()} # time.sleep(1) def window(src:Queue,handler,width:int,interval:int): # 窗口函數 ‘‘‘ 窗口函數 :param src:數據源,生成器,用來拿數據 :param handler: 數據處理函數 :param width: 時間窗口寬度,秒 :param interval: 處理時間間隔,秒 ‘‘‘ start = datetime.datetime.strptime(‘20170101 00:00:00 +0800‘,‘%Y%m%d %H:%M:%S %z‘) # 設定一個開始時間 current = datetime.datetime.strptime(‘20170101 01:00:00 +0800‘,‘%Y%m%d %H:%M:%S %z‘) # 設定一個現在的時間 buffer = [] # 窗口中的待計算數據 delta = datetime.timedelta(seconds=width-interval) while True: # 從數據源獲取數據 data = src.get() # 從格式化後的日誌中獲取數據,src是數據源,yield數據 if data: # 存入臨時緩沖等待計算 buffer.append(data) # 收集窗口大小的日誌數量 current = data[‘datetime‘] # 修改現在的時間為日誌內的時間 if (current - start).total_seconds() >= interval: # delta和時間間隔相同時,取出窗口數據 ret = handler(buffer) # print("{}".format(ret)) start = current # 重疊方案 buffer = [x for x in buffer if x[‘datetime‘] > current - delta] # # 隨機數測試處理函數 # def handler(iterable): # vals = [x[‘value‘] for x in iterable] # return sum(vals) / len(vals) # # 測試 # def donothing_handler(iterable): # return iterable # 狀態碼占比 def status_handler(iterable): # 一批時間窗口內的數據 status = {} for item in iterable: key = item[‘status‘] if key not in status.keys(): status[key] = 0 status[key] += 1 total = sum(status.values()) return {k:v/total*100 for k,v in status.items()} # 瀏覽器分析 def browser_handler(iterable): browsers = {} for item in iterable: ua = item[‘useragent‘] key = (ua.browser.family,ua.browser.version_string) browsers[key] = browsers.get(key,0) + 1 return browsers def dispatcher(src): handlers = [] queues = [] def reg(handler,width,interval): ‘‘‘ 註冊窗口處理函數 :param handler:註冊的數據處理函數 :param width: 時間窗口寬度 :param interval: 時間間隔 ‘‘‘ q = Queue() queues.append(q) h = threading.Thread(target=window,args=(q,handler,width,interval)) handlers.append(h) def run(): for t in handlers: t.start() # 啟動線程 for item in src: for q in queues: q.put(item) return reg,run if __name__ == "__main__": import sys path = ‘D:/test.log‘ reg,run = dispatcher(load(path)) reg(status_handler,10,5) # 註冊 reg(browser_handler,5,5) run()
Python第七課----正則和日誌分析