"""LinkedIn の分析の書き出し(Excel)を読み、投稿の数字をまとめて出す(LOG386)。 使い方: python3 analyze_linkedin.py LinkedIn の「アナリティクス」→ 投稿のインプレッション →「エクスポート」で書き出したファイルを読む。 シートは 調査 / エンゲージメント / 最新の投稿 / 人気の投稿 / フォロワー / オーディエンス統計データ / コンテンツ統計データ。 数字は文字列で入っているので int に直して使う。 """ import collections import sys import urllib.parse import openpyxl def rows(ws): return [r for r in ws.iter_rows(values_only=True) if any(c not in (None, "") for c in r)] def post_name(url): """投稿の URL から、本文の書き出し(LinkedIn が URL に入れた語)を読める形にする""" tail = url.split("/posts/eiichi-shimazu_", 1)[-1] tail = urllib.parse.unquote(tail) for sep in ("-share-", "-ugcPost-", "-activity-"): tail = tail.split(sep, 1)[0] return tail.replace("-", " ")[:40] def main(path): wb = openpyxl.load_workbook(path, data_only=True) print("## 期間全体") for r in rows(wb["調査"]): print(" ", " / ".join(str(c) for c in r if c is not None)) print("\n## 月ごとのインプレッションと反応") daily = [(d, int(i), int(e)) for d, i, e in rows(wb["エンゲージメント"])[1:]] month = collections.OrderedDict() for d, i, e in daily: y, m, _ = d.split("/") k = f"{y}/{int(m):02d}" month.setdefault(k, [0, 0, 0]) month[k][0] += i month[k][1] += e month[k][2] += 1 print(" 月 日数 インプレッション 反応 1日あたり") for k, (i, e, n) in month.items(): print(f" {k} {n:>4} {i:>14} {e:>4} {i / n:>8.1f}") print("\n## インプレッションが多かった日(上位10日)") for d, i, e in sorted(daily, key=lambda x: -x[1])[:10]: print(f" {d:<11} インプレッション {i:>4} 反応 {e:>2}") print("\n## 反応が付いた日(反応1以上)") for d, i, e in daily: if e: print(f" {d:<11} インプレッション {i:>4} 反応 {e:>2}") top = rows(wb["人気の投稿"])[2:] by_eng = [(r[0], r[1], int(r[2])) for r in top if r[0]] by_imp = [(r[4], r[5], int(r[6])) for r in top if r[4]] print(f"\n## 反応の多い投稿({len(by_eng)}本・LinkedIn が出した全件)") for n, (u, d, e) in enumerate(by_eng, 1): print(f" {n:>2}. {d:<10} 反応 {e:>2} {post_name(u)}") print(f"\n## インプレッションの多い投稿({len(by_imp)}本・LinkedIn が出した全件)") for n, (u, d, i) in enumerate(by_imp, 1): print(f" {n:>2}. {d:<10} {i:>4} {post_name(u)}") eng = [e for _, _, e in by_eng] print("\n## 反応の数ごとの投稿の本数(反応の多い投稿の一覧から)") for k, v in sorted(collections.Counter(eng).items(), reverse=True): print(f" 反応 {k:>2}: {v}本") print("\n## 最新の投稿(LinkedIn が出した直近3本・ネットワークの内と外の割合つき)") head, *body = rows(wb["最新の投稿"]) for r in body: print(f" - {post_name(r[0])}") for h, v in zip(head[1:], r[1:]): print(f" {h}: {v}") print("\n## フォロワー") fr = rows(wb["フォロワー"]) print(" ", " / ".join(str(c) for c in fr[0])) fmonth = collections.OrderedDict() for d, n in fr[2:]: y, m, _ = d.split("/") k = f"{y}/{int(m):02d}" fmonth[k] = fmonth.get(k, 0) + int(n) for k, v in fmonth.items(): print(f" {k} 新規 {v}人") print(f" 期間の合計 新規 {sum(fmonth.values())}人") for name in ("オーディエンス統計データ", "コンテンツ統計データ"): print(f"\n## {name}({'フォロワーの内訳' if name.startswith('オーディエンス') else '投稿を見た人の内訳'})") for r in rows(wb[name])[1:]: print(f" {r[0]:<6} {r[1]:<28} {r[2]}") if __name__ == "__main__": main(sys.argv[1])