이번에 프로젝트를 진행하면서 웹 로그 서버의 raw level을 직접 보고 전처리할 기회를 접하게 되었다. 어쨌든 웹로그를 데이터프레임화 시켜서 분포를 보고 데이터 탐색을 진행해야하기 때문에 공통된 룰로 전처리를 하고 데이터프레임화를 시켜야 해서 검색하다보니 아래와 같은 링크를 발견했고, 이에 대해 번역을 해서 기록해두려고 한다.
https://mmas.github.io/read-apache-access-log-pandas
1. Web log 살펴보기
우선 로깅 문서를 살펴보고 그 형식에 대해 알아보도록 하자. 일반적으로 다음과 같은 로그 형식을 사용하게 된다. (나도 회사에서 아래와 같은 형식의 로그를 사용했다)
%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"
- %h : client의 IP주소(원격 호스트)
- %l : RFC 1413 identity
- %u : HTTP 인증에 의해 결정되는 사용자 ID(보통 - - 일 경우가 많음)
- %t : [일/월/년:시:분:초 영역]형식의 시간
- \"%r%\" : 요청 문자열 "method resource protocol" (일반적으로 우리가 접속했을 때의 url이다)
- %>s : status code (에러및 정상 코드)
- %0 : 요청 사이즈
- \"%{Referer}i\" : referrer HTTP url
- \"%{User-Agent}i\" : User Agent HTTP request
예시를 들면, 아래와 같은게 하나의 요청이 들어올 때 쌓이는 로그라고 볼 수 있다.(아래의 예시는 댓글에서 참고해서 가져왔다)
103.15.66.130 - - [03/Aug/2018:10:50:47 +0000] "GET /v1/?act=encrypt&customerid=881329143866&customeremail=priyank@gowebbaby.com&customername=priyank%20gandhe&cdata=&domain=aaatheme4.myshopify.com HTTP/1.1" 200 212 "https://aaatheme4.myshopify..." "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36"
2. Pandas Package로 불러오기
이러한 정의로 판다스에서 문자열을 seperate할 때 고려해야한다.
pandas는 따옴표 붙은 문자를 건너띄기 위해서 그 따옴표 사이의 구분기호 문자를 분리하지는 않지만 그 이상에서는 제대로 작동을 하지 않는다. 그래서 열을 구분하기 위해서는 보다 복잡한 정규식이 필요하다. 우리는 9개의 필드를 얻기 위해 각 줄을 구분하는 정규식을 정읳야 한다.
그것은 큰따옴표 또는 대괄호와 일치해야 한다. 그럼 아래와 같은 구분자로 분리해야 한다.
\s # space로 분리
(?=(?:[^"]*"[^"]*")*[^"]*$) # 큰 따옴표로 둘러쌓여있지 않는
(?![^\[]*\]) # 대괄호 안에 둘러쌓여있지 않는
시간, reauest url, referer url, user agent들이 큰따옴표나 대괄호 안에 둘러쌓여있기 때문에 우리는 그 대괄호나 쌍따옴표를 제거할 함수가 필요하다. 그리고 시간은 datetime 으로 타입을 바꿔주어야 한다.
from datetime import datetime
import pytz
def parse_str(x):
"""
Returns the string delimited by two characters.
Example:
`>>> parse_str('[my string]')`
`'my string'`
"""
return x[1:-1]
def parse_datetime(x):
'''
Parses datetime with timezone formatted as:
`[day/month/year:hour:minute:second zone]`
Example:
`>>> parse_datetime('13/Nov/2015:11:45:42 +0000')`
`datetime.datetime(2015, 11, 3, 11, 45, 4, tzinfo=<UTC>)`
Due to problems parsing the timezone (`%z`) with `datetime.strptime`, the
timezone will be obtained using the `pytz` library.
'''
dt = datetime.strptime(x[1:-7], '%d/%b/%Y:%H:%M:%S')
dt_tz = int(x[-6:-3])*60+int(x[-3:-1])
return dt.replace(tzinfo=pytz.FixedOffset(dt_tz))
그리고 우리는 pandas를 이용해서 log 파일을 불러올 수 있다. 우리는 2번째와 3번째 컬럼은 가져오지 않을거다.
import re
import pandas as pd
data = pd.read_csv(
'data/access.log',
sep=r'\s(?=(?:[^"]*"[^"]*")*[^"]*$)(?![^\[]*\])',
engine='python',
na_values='-',
header=None,
usecols=[0, 3, 4, 5, 6, 7, 8],
names=['ip', 'time', 'request', 'status', 'size', 'referer', 'user_agent'],
converters={'time': parse_datetime,
'request': parse_str,
'status': int,
'size': int,
'referer': parse_str,
'user_agent': parse_str})
그럼 아래와 같이 데이터가 분리되어 dataframe 형식으로 저장된다.
여기서 request 문을 좀 더 분리해서 url만 따로 뽑아내고 싶다면, 아래와 같은 문장을 사용하면 된다.
request = data.pop('request').str.split()
data['resource'] = request.str[1]
이 아래의 문장들은 다른 데이터에 특수화된 코드 들이어서 생략하도록 한다.
추가적으로 에러로그 또한 같이 로깅하면서 에러가 발생할 떄가 있는데 이 경우는 아래와 같이 코드를 붙여 데이터를 좀 수정한 다음 다시 불러오면 된다.(에러문 사이에 큰따옴표로 이루어진 문장들이 있을 경우 발생한다)
with open('data/access.log') as infile:
with open('data/access.log', 'w') as outfile:
for line in infile:
outfile.write(line.replace('\\"', ''))
pd.read_csv( 'data/access.log', ...)
개인적으로 위 깃헙글이 웹로그를 분석할 때 도움이 많이 되어 다른 이들에게도 도움이 되길 바라며.. 이글을 마친다.
'Code > Python' 카테고리의 다른 글
[Python] inspect 모듈의 getsource() 함수 (0) | 2022.06.12 |
---|---|
[Python/Oracle] cx_Oracle timeout 설정하기 (0) | 2022.04.10 |
[Python] 주피터 노트북 테마 변경하기 (3) | 2021.05.17 |
[Python]Pytorch - RuntimeError:Error(s) in loading state_dict ... : Missing key(s) in state_dict: ... Unexpected key(s) in state_dict:... GPU 병렬 사용 문제 (0) | 2021.04.30 |
[Python] Data Frame apply 함수 병렬처리 하는 방법 (0) | 2021.04.15 |