PythonでApacheログを分析してみた話

# はじめに
少し前の話ですが、「Apacheのアクセスログを何かに活用できないかしら」との話を社内で貰たので、勉強がてらデータ分析してみた内容を備忘録的に書きます。

データ分析・Apache・記事投稿、どれをとっても初心者のため拙い文章ですが、ご容赦くださいませ。

# アクセスログの分析

## モジュールのインポート
今回は下記のモジュールを使ってます。
```python
# データの処理
import pandas as pd
import numpy as np

# グラフ表示
import plotly.express as px
from plotly import tools
import seaborn as sns
import matplotlib.pyplot as plt

# その他
import datetime,time
import ipaddress
```

## データの読み込み
ログファイルをとりあえず全部読み込みました。
```python
col_names = ['ip','c1','c2', 'datetime', 'timezone', 'request', 'status', 'size', 'referer', 'user_agent']
df = pd.read_csv("データのパス", sep=' ', dtype=str, names=col_names)
df[:1]
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1660601/e1cb1c14-9569-0092-153b-ba49844112cf.png)

## データの整形

### IP version
IPv4とIPv6を分別したいのでipaddressモジュールを使って新しいカラムにIP Versionのデータを追加します。
```python
def ipcheck(x):
    return ipaddress.ip_address(x.iloc[0]).version

df["ip_ver"] = np.nan
df["ip_ver"] = df.apply(ipcheck, axis=1)
```

### 端末情報
強引ですが、UserAgentからOSを判別してデータを追加します。
```python
def device(x):
    ua = x.iloc[9]
    dev = "etc"
    if "iPhone" in ua:
        dev="iPhone"
    elif "Android" in ua:
        dev="Android"
    elif "Win" in ua:
        dev="Windows"
    elif "Mac" in ua:
        dev="Macintosh"
    elif "Linux" in ua:
        dev="Linux"
    return dev

df["device"] = np.nan
df["device"] = df.apply(device, axis=1)
```

### 日時
読み込んだ時に日時とタイムゾーンが分かれてしまったので、いい感じにPythonのdatetime型にして格納します。
```python
#datetime,timezoneをpythonのdatetime型に変換して挿入
datetime_str = df.datetime + df.timezone
time_trimmed = datetime_str.map(lambda s: s.strip('[]').split('+')[0])
df['datetime'] = pd.to_datetime(time_trimmed, format='%d/%b/%Y:%H:%M:%S')
```

ヒートマップ用に時間だけのデータも追加します。（わざわざ追加しなくてもよかったかもですが…）
```python
def hourcheck(x):
    dt = x.iloc[3]
    return dt.strftime('%H')

df["hour"] = np.nan
df["hour"] = df.apply(hourcheck, axis=1)
```

### 不要なデータの削除

カラム名をc1,c2と置いているところは「クライアントの識別子」と「認証ユーザー名」のようですが、今回のログでは特に情報無いため消します。タイムゾーンももう必要ないので消します。
```python
del df['c1']
del df['c2']
del df['timezone']
```

## 円グラフで可視化してみる
まずplotly.expressを使って可視化してみます。


### IPv4/IPv6の比率
数の多いstatus:200に絞ってIPv4/IPv6の比率を可視化してみます。

```python
df_ip = df.pivot_table(index="ip_ver",columns="status",aggfunc='size',fill_value=0)

ip_fig_title = 'IPv4/v6比率'
ip_fig = px.pie(data_frame=df_ip,
       values='200',
       names=df_ip.index,
       hover_name=df_ip.index,
       labels={'ip_ver':'IP version','200':'count'})
ip_fig.update_layout(xaxis={'categoryorder':'category ascending'},  title=ip_fig_title)
ip_fig.update_traces(textinfo='percent+label')
ip_fig.show()
```
![newplot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1660601/e4c3f2b3-9783-4c81-e010-96ac0467e45a.png)
若干ですが、v4の方が多いみたい。

### 端末種別の割合
こちらもstatus:200に絞って端末種別の割合を表示してみます。

```python
df_device = df.pivot_table(index="device",columns="status",aggfunc='size',fill_value=0)

ip_fig_title = 'IPv4/v6比率'
ip_fig = px.pie(data_frame=df_ip,
       values='200',
       names=df_ip.index,
       hover_name=df_ip.index,
       labels={'ip_ver':'IP version','200':'count'})
ip_fig.update_layout(xaxis={'categoryorder':'category ascending'},  title=ip_fig_title)
ip_fig.update_traces(textinfo='percent+label')
ip_fig.show()
```
![newplot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1660601/a84ad8ff-f73e-2e84-9f1d-b92d041d449b.png)
Windows、iPhone、Androidで９割越えの結果に。
やはりAndroidよりiPhoneユーザの方が多いですね。

### IPv4/IPv6それぞれの端末種別の割合
今度はIPv4/IPv6それぞれで端末種別の割合を見てみます。

```python
df_device_ip = df.pivot_table(index="device",columns="ip_ver",aggfunc='size',fill_value=0)

device_fig_ip_title = '端末種別比率(v4)'
device_fig_ip = px.pie(data_frame=df_device_ip,
       values=4,
       names=df_device_ip.index,
       hover_name=df_device_ip.index,
       labels={'device':'Device',4:'count'})
device_fig_ip.update_layout(xaxis={'categoryorder':'category ascending'},  title=device_fig_ip_title)
device_fig_ip.update_traces(textinfo='percent+label')
device_fig_ip.show()

device_fig_ip6_title = '端末種別比率(v6)'
device_fig_ip6 = px.pie(data_frame=df_device_ip,
       values=6,
       names=df_device_ip.index,
       hover_name=df_device_ip.index,
       labels={'device':'Device',6:'count'})
device_fig_ip6.update_layout(xaxis={'categoryorder':'category ascending'},  title=device_fig_ip6_title)
device_fig_ip6.update_traces(textinfo='percent+label')
device_fig_ip6.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1660601/70a946cb-f18e-3773-6c1b-514c427056de.png)

若干ではありますがIPv4はPCが多く、逆にIPv6ではモバイル端末が多いのが見えてきました。
今回は何で差が出るかまでは調べてないですが、ちょっと気になりますね。


## ヒートマップで可視化してみる
次はseabornを使ってヒートマップを見てみます。


### 時間帯ごとのアクセス数

+ IPv4/IPV6

```python
figure = sns.heatmap(pd.crosstab(df['ip_ver'], df['hour']), fmt="1.1f", lw=0.7, cmap='Blues')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1660601/e09459b1-7490-f3cd-eb27-7d101e284b44.png)

+ 端末種別ごと

```python
figure = sns.heatmap(pd.crosstab(df['device'], df['hour']), fmt="1.1f", lw=0.7, cmap='Blues')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1660601/29c1a9d2-2d36-f9ab-8202-78ab1575ff0f.png)

ヒートマップもなんとなく特徴が出ました。
・昼間はPCから（社内のアクセスも多いのかな？）
・夜の20-21時は帰宅中の電車でスマホから見てるのかなー
とか思いながらヒートマップを見てみると意外と面白かったです。

# 参考にしたページ
https://qiita.com/tamura__246/items/79cd4fad1d7ea1fed12b


# 余談
勉強がてらデータ分析をしてみたところ、若干ですが特徴が見えてきて、いろいろ想像も膨らんで面白かったです。
今後の展開でアクセスログの活用方法として、サービスの障害情報を発信するページへのアクセスログを分析できないかという案が出ました。「アクセス増加」⇒「サービス使えていない人が増加」⇒「サービス障害の可能性」となり、アラームを検知しないサイレント障害の気づきになるのでは…!?　と思っていましたが、弊社WEBサイトの更改がありApacheではなくなってしまったので、そちらはまた別途取り組んでいけたらなと思っています。。。