googleアナリティクスでdirect/noneの参照元を解析する方法
特定のBLOG(レジャーへ行こうよ)で、新規記事を投稿した直後にPVが急増するようになりました。
ただgoogleアナリティクスのレポート等で参照元を見てもdirect/none表示となり、どこからの流入か不明の状態でした。
Google Discover(Googleアプリ等)に掲載されたことを疑いましたが、Google DiscoverだとGoogleサーチコンソールに「Discover」レポートが表示されるそうですが、それも無い状態でした。
このため参照元/流入元を解析するため、Wordpress(BLOG)を運用しているサーバのアクセスログから特定したので、その方法を記載します。
WordPressのアクセスログ解析【BLOG PV急増の原因解析】
WordPressを運用しているサーバに入り当日のアクセスログを取得して解析することにしました。
自分が契約しているシンレンタルサーバーの場合、アクセス解析からアクセスログを日単位で取得できます。

User-Agent毎のアクセス数解析【Yahoo!アプリからの流入が多いことが判明】
該当日のアクセスログを下欄で紹介しているプログラム「all-useragent.py access.log」で解析した結果です。
赤字がUser-Agent毎のアクセス数で、残りがアクセスログに記載されているUser-Agentです。
アクセス数(User-Agent別 上位15個):
—————————————————-
9993: Mozilla/5.0 (iPhone; CPU iPhone OS 18_6_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 YJApp-IOS jp.co.yahoo.ipn.appli/4.148.0
3201: Mozilla/5.0 (compatible; AhrefsBot/7.0; +http://ahrefs.com/robot/)
1866: Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm) Chrome/116.0.1938.76 Safari/537.360
1227: Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 YJApp-IOS jp.co.yahoo.ipn.appli/4.148.0
1061: Mozilla/5.0 (iPhone; CPU iPhone OS 18_7_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 YJApp-IOS jp.co.yahoo.ipn.appli/4.148.0
607: Mozilla/5.0 (iPhone; CPU iPhone OS 16_7_12 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 YJApp-IOS jp.co.yahoo.ipn.appli/4.148.0
570: Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 YJApp-IOS jp.co.yahoo.ipn.appli/4.148.0
435: Mozilla/5.0 (Linux; Android 16; Pixel 8a Build/BP3A.250905.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/140.0.7339.207 Mobile Safari/537.36 YJApp-ANDROID jp.co.yahoo.android.yjtop/3.204.0:
9993で一番アクセスの多いのは「YJApp-IOS jp.co.yahoo.ipn.appli/4.148.0」からの流入なのが判りました。
上のログの青字部分着目するとIOSまたはANDROIDのYahoo!アプリからの流入なのが判りました。
YJApp-IOS
・iOS版 Yahoo!アプリ からのアクセス
・jp.co.yahoo.ipn.appli/4.148.0 はアプリのバージョン番号(4.148.0)
YJAppからどの記事へのアクセス数が多いか解析【新規投稿記事への流入が大半】
該当日のアクセスログを下欄で紹介しているプログラム「specificuseragent-url.py access.log YJApp」で解析した結果です。
YJAppを含むUser-Agentが、どのURLにアクセスしたかの解析結果です。(画像やフォント等へのアクセスは集計対象外にしてます。)
YJAppから「/archives/11158 」の新規投稿記事への流入が大半なのが判ります。
対象User-Agentからのアクセス数(URL別 上位15個):
—————————————————-
1763: GET /archives/11158 HTTP/2.0
71: GET /archives/11131 HTTP/2.0
6: GET /archives/11105 HTTP/2.0:
Yahooアプリで記事が紹介されてるで確定【Yahoo Discoverから流入】
YJAppからBLOG記事「/archives/11158」への時系列の流入もグラフ化してみました。(プログラムは非公開ですが要望あれば体裁整えてあげるかも)
記事をBLOGへ投稿してから3時間から4時間ほど急増しています。8時間もすると、ほぼアクセスが無くなる感じです。
結果からYahoo DiscoverでYahooアプリ内の「おすすめ記事」や「Discover」セクションに表示されている可能性が高いことが判りました。
後日、Yahooアプリを見てみましたが、たしかに掲載されていることも確認できましたよ!
やっと判ってすっきりしました(笑) 掲載して頂きありがとうございます!(喜)

サーバのアクセスログ解析プログラム【apache 2.4系やngin系】
シンサーバのアクセスログ解析に対応させたのでapache 2.4系やngin系のサーバ アクセスログなら動くのではと思います(保証はしませんよ!(笑))
自分はWindows11のコマンドラインからPython起動して利用しています。
各自自己責任でご利用ください。使用に際して直接的・間接的な損害があっても一切責任は負いません。動作や結果は保証しません。
User-Agent毎のアクセス数表示Python
使用方法
Python all-useragent.py <アクセスログ名>
使用例
>Python all-useragent.py access.log
対象アクセスログファイル: access.log
User-AgentのKEY文字: YJApp
対象User-Agentからのアクセス数(URL別 上位15個):
—————————————————-
1763: GET /archives/11158 HTTP/2.0
71: GET /archives/11131 HTTP/2.0
6: GET /archives/11105 HTTP/2.0
:
all-useragent.py プログラム
import re
import sys
from collections import defaultdict
# Apacheログファイルのパス()
args = sys.argv[1:]
# 引数の数をチェック
if len(args) < 1:
print("ERROR: 引数が足りません。")
print("使い方: python all-useragent.py <対象アクセスログファイル>")
sys.exit(1)
# 第1引数・第2引数を取得
arg1 = args[0]
print(f"対象アクセスログファイル: {arg1}")
log_file_path = arg1
# User-Agentごとのアクセス数をカウント
ua_count = defaultdict(int)
# ログのパターン(Combined Log Format想定)
log_pattern = re.compile(r'(\d+\.\d+\.\d+\.\d+) \S+ \S+ \[[^\]]+\] "(GET [^"]+)" \d+ \d+ "[^"]*" "([^"]+)"')
try:
with open(log_file_path, "r", encoding="utf-8") as f:
for line in f:
match = log_pattern.search(line)
if match:
request = match.group(2)
user_agent = match.group(3)
# GETリクエストかつ対象URLのみ
if request.startswith("GET") :
ua_count[user_agent] += 1
# User-Agentごとのアクセス数表示(多い順 15個まで)
print(f"\nアクセス数(User-Agent別 上位15個):")
print(f"----------------------------------------------------")
loopno = 15
for ua, count in sorted(ua_count.items(), key=lambda x: x[1], reverse=True):
print(f"{count}: {ua}")
loopno -= 1
if loopno == 0:
sys.exit(0)
except FileNotFoundError:
print(f"ERROR: ファイル '{log_file_path}' が見つかりません")
except IOError as e:
print(f"ERROR: ファイルを開けません ({e})")
特定User-AgentからURL毎のアクセス数表示Python
使用方法
Python specificuseragent-url.py <アクセスログ名> <User-Agent特定の為のキーワード>
使用例
>Python specificuseragent-url.py access.log YJApp
対象アクセスログファイル: access.log
User-AgentのKEY文字: YJApp
対象User-Agentからのアクセス数(URL別 上位15個):
—————————————————-
1763: GET /archives/11158 HTTP/2.0
71: GET /archives/11131 HTTP/2.0
6: GET /archives/11105 HTTP/2.0
:
specificuseragent-url.py プログラム
import re
import sys
from collections import defaultdict
# Apacheログファイルのパス()
args = sys.argv[1:]
# 引数の数をチェック
if len(args) < 2:
print("ERROR: 引数が足りません。")
print("使い方: python specificuseragent-url.py <対象アクセスログファイル> <User-AgentのKEY文字>")
sys.exit(1)
# 第1引数・第2引数を取得
arg1 = args[0]
arg2 = args[1]
#除きたい拡張子
exclude_extensions = ['.jpg', '.jpeg', '.png', '.gif', '.svg', '.woff', '.woff2', '.ttf', '.css', '.js', '.ico','.php']
print(f"対象アクセスログファイル: {arg1}")
print(f"User-AgentのKEY文字: {arg2}")
log_file_path = arg1
# URLアクセス数を保存する辞書
url_count = defaultdict(int)
# ログのパターン(Combined Log Format想定)
log_pattern = re.compile(r'(\d+\.\d+\.\d+\.\d+) \S+ \S+ \[[^\]]+\] "(GET [^"]+)" \d+ \d+ "[^"]*" "([^"]+)"')
try:
with open(log_file_path, "r", encoding="utf-8") as f:
for line in f:
match = log_pattern.search(line)
if match:
request = match.group(2)
user_agent = match.group(3)
# GETリクエストかつ対象URLのみ
if request.startswith("GET") :
if arg2 in user_agent:
#除きたい拡張子が含まれないrequestの場合
if not any(ext in request.lower() for ext in exclude_extensions):
#対象外の拡張子が無かったら、カウント対称
url_count[request] += 1
# User-Agentごとのアクセス数表示(多い順 15個まで)
print(f"\n対象User-Agentからのアクセス数(URL別 上位15個):")
print(f"----------------------------------------------------")
loopno = 15
for ua, count in sorted(url_count.items(), key=lambda x: x[1], reverse=True):
print(f"{count}: {ua}")
loopno -= 1
if loopno == 0:
sys.exit(0)
except FileNotFoundError:
print(f"ERROR: ファイル '{log_file_path}' が見つかりません")
except IOError as e:
print(f"ERROR: ファイルを開けません ({e})")