Python第七周 學習筆記(1)

來源:互聯網
上載者:User

標籤:學習筆記

日誌分析
  • 業務中會產生大量的系統日誌、應用程式記錄檔、安全日誌等,通過對日誌的分析可以瞭解伺服器的負載、健康情況,可以分析客戶的分布情況、客戶的行為,甚至基於這些分析可以做出預測

  • 一般採集流程

    • 日誌產出 -> 採集(Logstash、Flume、Scribe)-> 儲存 -> 分析 -> 儲存(資料庫、NoSQL)-> 可視化

    • 開源即時日誌分析ELK平台
    • Logstash收集日誌,並存放到Elasticsearch叢集中,Kibana則從ES叢集中查詢資料組建圖表,返回瀏覽器端
分析的前提半結構化資料
  • 日誌是半結構化資料,是有組織的,有格式的資料。可以分割成行和列,就可以當作表理解和處理,分析裡面的資料
文本分析
  • 日誌是文字檔,需要依賴檔案IO、字串操作、Regex等技術
  • 通過這些技術就能夠把日誌中需要的資料提取出來

  • 目標資料形如:

    123.125.71.36 - - [06/Apr/2017:18:09:25 +0800] "GET / HTTP/1.1" 200 8642 "-" "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"124.```
  • nginx、tomcat等WEB Server都會產生這樣的日誌
提取資料一、分割
import datetimeline = ‘‘‘123.125.71.36 - - [06/Apr/2017:18:09:25 +0800] "GET / HTTP/1.1" 200 8642 "-" "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"‘‘‘CHARS = set(" \t")def makekey(line: str):    start = 0    skip = False    for i, c in enumerate(line):        if not skip and c in ‘"[‘:            start = i + 1            skip = True        elif skip and c in ‘"]‘:            skip = False            yield line[start:i]            start = i + 1            continue        if skip:            continue        if c in CHARS:            if start == i:                start = i + 1                continue            yield line[start:i]            start = i + 1    else:        if start < len(line):            yield line[start:]names = (‘remote‘, ‘‘, ‘‘, ‘datetime‘, ‘request‘, ‘status‘, ‘length‘, ‘‘, ‘useragent‘)ops = (None, None, None, lambda timestr: datetime.datetime.strptime(timestr, ‘%d/%b/%Y:%H:%M:%S %z‘),       lambda request: dict(zip([‘method‘, ‘url‘, ‘protocol‘], request.split())),       int, int, None, None)def extract(line: str):    return dict(map(lambda item: (item[0], item[2](item[1]) if item[2] is not None else item[1]),                    zip(names, makekey(line), ops)))print(extract(line))
二、Regex分割
PATTERN = r‘‘‘(?P<ip>[\d.]{7,})\s-\s-\s\[(?P<datetime>[^\[\]]+)\]\s"(?P<method>[^"\s]+)\s(?P<url>[^"\s]+)\s(?P<protocol>[^"\s]+)"\s(?P<status>\d{3})\s(?P<size>\d+)\s"(?:.+)"\s"(?P<useragent>[^"]+)"‘‘‘pattern = re.compile(PATTERN)ops = {‘datetime‘: (lambda x: datetime.datetime.strptime(x, ‘%d/%b/%Y:%H:%M:%S %z‘)), ‘status‘: int, ‘size‘: int}def extract(text):    mat = pattern.match(text)    return {k: ops.get(k, lambda x: x)(v) for k, v in mat.groupdict().items()}
異常處理
  • 日誌中不免會出現一些不匹配的行,需要處理
  • 這裡使用re.match方法,有可能匹配不上。所以要增加一個判斷
  • 採用拋出異常的方式,讓調用者獲得異常並自行處理
PATTERN = r‘‘‘(?P<ip>[\d.]{7,})\s-\s-\s\[(?P<datetime>[^\[\]]+)\]\s"(?P<method>[^"\s]+)\s(?P<url>[^"\s]+)\s(?P<protocol>[^"\s]+)"\s(?P<status>\d{3})\s(?P<size>\d+)\s"(?:.+)"\s"(?P<useragent>[^"]+)"‘‘‘pattern = re.compile(PATTERN)ops = {‘datetime‘: (lambda x: datetime.datetime.strptime(x, ‘%d/%b/%Y:%H:%M:%S %z‘)), ‘status‘: int, ‘size‘: int}def extract(text) -> dict:    mat = pattern.match(text)    if mat:        return {k: ops.get(k, lambda x: x)(v) for k, v in mat.groupdict().items()}    else:        raise Exception(‘No match‘)
  • 或者返回一個特殊值
def extract(text) -> dict:    mat = pattern.match(text)    if mat:        return {k: ops.get(k, lambda x: x)(v) for k, v in mat.groupdict().items()}    else:        return None
滑動視窗資料載入
def load(path):    with open(path) as f:        for line in f:            fields = extract(line)            if fields:                yield fields            else:                continue
時間視窗分析概念
  • 很多資料,例如日誌,都是和時間相關的,都是按照時間順序產生的
  • 產生的資料分析的時候,要按照時間求值

  • interval表示每一次求值的時間間隔
  • width時間窗介面寬度,指一次求值的時間視窗寬度
當width > interval

  • 資料求值時會有重疊
當width = interval

  • 資料求值沒有重疊
時序資料
  • 營運環境中,日誌、監控等產生的資料都是與時間相關的資料,按照時間先後產生並記錄下來的資料,所以一般按照時間對資料進行分析
資料分析基本程式結構
  • 無限的產生隨機數函數,產生時間相關的資料,返回時間和隨機數字典
import randomimport datetimeimport timedef source():    while True:        yield {‘value‘: random.randint(1, 100), ‘datetime‘: datetime.datetime.now()}        time.sleep(1)s = source()items = [next(s) for _ in range(3)]def handler(iterable):    return sum(map(lambda item: item[‘value‘], iterable)) / len(iterable)print(items)print("{:.2f}".format(handler(items)))
視窗函數實現
  • 將上面的程式擴充為window函數
import randomimport datetimeimport timedef source(second=1):    while True:        yield {‘value‘: random.randint(1, 100),               ‘datetime‘: datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=8)))}        time.sleep(second)def window(iterator, handler, width: int, interval: int):    start = datetime.datetime.strptime(‘20170101 000000 +0800‘, ‘%Y%m%d %H%M%S %z‘)    current = datetime.datetime.strptime(‘20170101 010000 +0800‘, ‘%Y%m%d %H%M%S %z‘)    buffer = []    delta = datetime.timedelta(seconds=width - interval)    while True:        data = next(iterator)        if data:            buffer.append(data)            current = data[‘datetime‘]        if (current - start).total_seconds() >= interval:            ret = handler(buffer)            print(‘{:.2f}‘.format(ret))            start = current            buffer = [x for x in buffer if x[‘datetime‘] > current - delta]def handler(iterable):    return sum(map(lambda item: item[‘value‘], iterable)) / len(iterable)window(source(), handler, 10, 5)

分發生產者消費者模型
  • 對於一個監控系統,需要處理很多資料,包括日誌。對其中已有資料的採集、分析。
  • 被監控對象就是資料的生產者producer,資料的處理常式就是資料的消費者consumer
  • 生產者消費者傳統模型
  • 傳統的生產者消費者模型,生產者生產,消費者消費。但這種模型有問題
  • 開發的代碼耦合高,如果產生規模擴大,不易擴充,生產和消費的速度很難匹配等。

  • 解決辦法:隊列
  • 作用:解耦、緩衝
  • 日誌生產者往往會部署好幾個程式,日誌產生的也很多,而消費者也會有很多個程式,去提取日誌分析處理
  • 資料生產是不穩定的。會造成段時間資料的潮湧,需要緩衝
  • 消費者消費能力不一樣,有快有慢,消費者可以自己決定消費緩衝區中的資料
  • 單機可以使用queue內建的模組構建進程內的隊列,滿足多個線程間的生產消費需要
  • 大型系統可以使用第三方訊息中介軟體:RabbitMQ、RocketMQ、Kafka
queue模組——隊列
  • queue.Queue(maxsize=0)

    • 建立FIFO隊列,返回Queue對象
    • maxsize小於等於0,隊列長度沒有限制
  • Queue.get(block=True,timeout=None)

    • 從隊列中移除元素並返回這個元素
    • block 阻塞,timeout 逾時
    • 如果block為True,是阻塞,timeout為None就是一直阻塞
    • 如果block為True但是timeout有值,就阻塞到一定秒數拋出異常
    • block為False,是非阻塞,timeout將被忽略,要麼成功返回一個元素,要麼拋出empty異常
  • Queue.get_nowait()

    • 等價於get(False)
  • Queue.put(item,block=True,timeout=None)

    • 把一個元素加入到隊列中去
    • block=True,timeout=None,一直阻塞直至有空位放元素
    • block=True,timeout=5,阻塞5秒就拋出Full異常
    • block=True,timeout失效,立刻返回,,一直阻塞直至有空位放元素
  • Queue.put_nowait(item)
    • 等價於put(item,False)
分發器實現
  • 生產者(資料來源)生產資料,緩衝到訊息佇列中

  • 資料處理流程:

    資料載入 -> 提取 -> 分析(滑動視窗函數)

  • 處理大量資料的時候,可能需要多個消費者處理
  • 需要一個分發器(調度器),把資料分發給不同的消費者處理
  • 每一個消費者拿到資料後,有自己的處理函數。所以要有一種註冊機制

資料載入 -> 提取 -> 分發 -> 分析函數1&分析函數2

  • 分析1和分析2可以是不同的handler、視窗寬度、間隔時間

  • 暫時採用輪詢策略,一對多的副本發送,一個資料通過分發器、發送到多個消費者

  • 訊息佇列

    • 在生產者和消費者之間使用訊息佇列,那麼所有消費者可以共有一個訊息佇列,或各自擁有一個訊息佇列
    • 公用一個訊息佇列需要解決爭搶問題。每個消費者擁有一個隊列較易實現
  • 註冊

    • 在調度器內部記錄消費者,每一個消費者擁有自己的隊列
  • 線程
    • 由於一條資料會被多個不同的註冊過的handler處理,所以最好的方式就是多線程
分發器代碼實現
def dispatcher(src):    reg_handler = []    queues = []    def reg(handler, width, interval):        q = Queue()        queues.append(q)        thrd = threading.Thread(target=window, args=(q, handler, width, interval))        reg_handler.append(thrd)    def run():        for i in reg_handler:            i.start()        for item in src:            for q in queues:                q.put(item)    return reg, runreg, run = dispatcher(load(‘test.log‘))reg(handler, 10, 5)run()
整合代碼
  • load函數就是從日誌中提取合格的資料產生函數
  • 它可以作為dispatcher函數的資料來源
import refrom pathlib import Pathimport datetimeimport timeimport threadingfrom queue import Queuefrom user_agents import parsePATTERN = r‘‘‘(?P<ip>[\d.]{7,})\s-\s-\s\[(?P<datetime>[^\[\]]+)\]\s"(?P<method>[^"\s]+)\s(?P<url>[^"\s]+)\s(?P<protocol>[^"\s]+)"\s(?P<status>\d{3})\s(?P<size>\d+)\s"(?:.+)"\s"(?P<useragent>[^"]+)"‘‘‘pattern = re.compile(PATTERN)def extract(text):    ops = {‘datetime‘: (lambda x: datetime.datetime.strptime(x, ‘%d/%b/%Y:%H:%M:%S %z‘)), ‘status‘: int, ‘size‘: int,           ‘useragent‘: lambda x: parse(x)}    mat = pattern.match(text)    return {k: ops.get(k, lambda x: x)(v) for k, v in mat.groupdict().items()}def openfile(filename):    with open(filename) as f:        for text in f:            fields = extract(text)            time.sleep(2)            if fields:                yield fields            else:                continue# producerdef load(*pathnames):    for path in pathnames:        pathname = Path(path)        if not pathname.exists():            continue        if pathname.is_file():            yield from openfile(pathname)        elif pathname.is_dir():            for filename in pathname.iterdir():                if filename.is_file():                    yield from openfile(filename)def sum_size_handler(iterable):    return sum(map(lambda x: x[‘size‘], iterable))def status_handler(iterable):    status = {}    for dic in iterable:        key = dic[‘status‘]        status[key] = status.get(key, 0) + 1    return {k: v / len(iterable) for k, v in status.items()}d = {}def ua_handler(iterable):    ua_family = {}    for item in iterable:        val = item[‘useragent‘]        key = (val.browser.family, val.browser.version_string)        ua_family[key] = ua_family.get(key, 0) + 1        d[key] = d.get(key, 0) + 1    return ua_family, d# consumerdef window(q: Queue, handler, width, interval):    st_time = datetime.datetime.strptime(‘19700101 000000 +0800‘, ‘%Y%m%d %H%M%S %z‘)    cur_time = datetime.datetime.strptime(‘19700101 010000 +0800‘, ‘%Y%m%d %H%M%S %z‘)    buffer = []    while True:        # src = next(iterable)        src = q.get()        print(src)        buffer.append(src)        cur_time = src[‘datetime‘]        if (cur_time - st_time).total_seconds() > interval:            val = handler(buffer)            st_time = cur_time            b, d = val            d = sorted(d.items(), key=lambda x: x[1], reverse=True)            print(val)            print(d)        buffer = [x for x in buffer if x[‘datetime‘] > (cur_time - datetime.timedelta(seconds=width - interval))]def dispatcher(src):    reg_handler = []    queues = []    def reg(handler, width, interval):        q = Queue()        queues.append(q)        thrd = threading.Thread(target=window, args=(q, handler, width, interval))        reg_handler.append(thrd)    def run():        for i in reg_handler:            i.start()        for item in src:            for q in queues:                q.put(item)    return reg, runif __name__ == ‘__main__‘:    import sys    # path=sys.argv[1]    path = ‘test.log‘reg, run = dispatcher(load(‘test.log‘))# reg(sum_size_handler, 20, 5)# reg(status_handler, 20, 5)reg(ua_handler, 20, 5)run()
完成分析功能
  • 分析日誌很重要,通過海量資料分析就能夠知道是否遭受了攻擊,是否被爬取及爬取高峰期,是否有盜鏈等
狀態代碼分析
  • 狀態代碼中包含了很多資訊。例如
    • 304,伺服器收到用戶端提交的請求參數,發現資源未變化,要求瀏覽器使用靜態資源的緩衝
    • 404,伺服器找不到大請求的資源
    • 304佔比大,說明靜態緩衝效果明顯。404佔比大,說明網站出現了錯誤連結,或者嘗試嗅探網站資源
    • 如果400、500佔比突然增大,網站一定出了問題。
def status_handler(iterable):    status = {}    for dic in iterable:        key = dic[‘status‘]        status[key] = status.get(key, 0) + 1    return {k: v / len(iterable) for k, v in status.items()}
瀏覽器分析useragent
  • 這裡指的是,軟體按照一定的格式向遠端的伺服器提供一個表示自己的字串
  • 在HTTP協議中,使用useragent欄位傳送這個字串

瀏覽器選項中可以修改此設定

資訊提取安裝
pip install pyyaml ua-parser user-agents
資料分析
d = {}def ua_handler(iterable):    ua_family = {}    for item in iterable:        val = item[‘useragent‘]        key = (val.browser.family, val.browser.version_string)        ua_family[key] = ua_family.get(key, 0) + 1        d[key] = d.get(key, 0) + 1    return ua_family, d

Python第七周 學習筆記(1)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.