🙌

GitHubで管理されているZennのtopicsを集計するコードをclaude codeに作らせた

に公開

今回はタイトル通り、GitHubでZennの記事を管理している場合に、どのようなtopicsがよく利用されているか集計するための機能をclaude codeに作らせてみました。私自身連続170記事以上出している関係で、どのような技術をよく利用しているか調べたくなり、作らせてみました。

ZennをGitHubで管理するためのフォルダ構成

違いはあるかもしれませんが、基本的には以下のフォルダ構成で管理されていると思います。

articles/
  hogehoge.md
  fugafuga.md
books/
  ...
images/
  ...
scrapes
  ...

今回はarticles以下にある.mdファイルを対象にtopics集計をさせます。

早速実装させる

インストラクションの作成

今回claude codeに以下の指示を与えました。条件は書いてある通りですが、GitHubで管理する場合はpublished: falseが設定されていると未公開の記事になります。今回は公開済みの記事だけを対象にしたかったので、published: falseのきじは無視するように指定しています。実装についてはanalyze_topicというフォルダに作成させ、uvを利用することを指示しています。

instruction.md
## 目的
articles以下に作成されているMarkdown形式のファイルからtopicsを取得し、どのtopicが多く利用されているか取得したい

## 要求
- articles以下にあるmarkdownファイル一覧全ての記事からtopicsをそれぞれ取得する
    - topicsは基本的に複数ある
- published: falseの記事については収集対象から除外すること
- どのtopicがよく利用されているか、matplotlibを利用してグラフとして表示すること

## 技術要件
- analyze_topicというフォルダ以下でuvを利用して管理する
- analyze_topic/analyze.pyでmarkdownファイルからtopicsを取得してグラフを表示させること
- 分析結果についてはanalyze_topic/result.csvに保存すること

生成結果

instruction.mdをclaude codeに与えて生成されたコードが以下になります。

analyze_topic/analyze.py
#!/usr/bin/env python3
import os
import yaml
import csv
import matplotlib.pyplot as plt
from collections import Counter
from datetime import datetime
from pathlib import Path


def extract_front_matter(file_path):
    """Markdownファイルからフロントマター(YAML)を抽出する"""
    with open(file_path, 'r', encoding='utf-8') as f:
        content = f.read()
    
    if not content.startswith('---'):
        return None
    
    try:
        parts = content.split('---', 2)
        if len(parts) >= 3:
            yaml_str = parts[1]
            return yaml.safe_load(yaml_str)
    except Exception as e:
        print(f"Error parsing {file_path}: {e}")
        return None
    
    return None


def analyze_topics():
    """articlesディレクトリ内のMarkdownファイルからtopicsを収集・分析する"""
    articles_dir = Path(__file__).parent.parent / 'articles'
    
    all_topics = []
    article_topics = []
    md_files = list(articles_dir.glob('*.md'))
    
    published_count = 0
    unpublished_count = 0
    
    for md_file in md_files:
        front_matter = extract_front_matter(md_file)
        
        if front_matter:
            # published: falseの記事を除外
            if front_matter.get('published', False) == False:
                unpublished_count += 1
                continue
            
            published_count += 1
            topics = front_matter.get('topics', [])
            
            if topics:
                article_topics.append({
                    'file': md_file.name,
                    'title': front_matter.get('title', 'Unknown'),
                    'topics': topics
                })
                all_topics.extend(topics)
    
    # トピックの頻度を集計
    topic_counter = Counter(all_topics)
    
    # CSVに結果を保存
    csv_path = Path(__file__).parent / 'result.csv'
    with open(csv_path, 'w', newline='', encoding='utf-8') as csvfile:
        writer = csv.writer(csvfile)
        writer.writerow(['Topic', 'Count'])
        for topic, count in topic_counter.most_common():
            writer.writerow([topic, count])
    
    # グラフの生成
    if topic_counter:
        # 上位20個のトピックを表示(多すぎる場合のため)
        top_n = 20
        top_topics = topic_counter.most_common(top_n)
        
        topics_list = [t[0] for t in top_topics]
        counts_list = [t[1] for t in top_topics]
        
        plt.figure(figsize=(12, 8))
        plt.barh(topics_list, counts_list, color='steelblue')
        plt.xlabel('Count', fontsize=12)
        plt.ylabel('Topics', fontsize=12)
        plt.title(f'Top {min(top_n, len(top_topics))} Most Used Topics in Published Articles', fontsize=14)
        plt.gca().invert_yaxis()  # 上位のトピックを上に表示
        
        # 各バーに数値を表示
        for i, count in enumerate(counts_list):
            plt.text(count + 0.1, i, str(count), va='center')
        
        plt.tight_layout()
        
        # グラフを表示
        plt.show()
        
    # 統計情報の表示
    print("\n=== Topic Analysis Summary ===")
    print(f"Total published articles: {published_count}")
    print(f"Total unpublished articles (excluded): {unpublished_count}")
    print(f"Total unique topics: {len(topic_counter)}")
    print(f"Total topic occurrences: {sum(topic_counter.values())}")
    
    if topic_counter:
        print("\nTop 10 most used topics:")
        for topic, count in topic_counter.most_common(10):
            print(f"  {topic}: {count}")


if __name__ == "__main__":
    analyze_topics()

今回重要な点としては、記事のマークダウンファイルの構成になります。基本的に以下のようなテンプレートが利用されます。記事の冒頭で---で挟まれた部分にはタイトルやtopicsなどのメタデータが登録されており、この範囲はYAML形式となっています。

zenn_template.md
---
title: "cvat使ってみた"
emoji: "🕌"
type: "tech"
topics: []
published: false
---
...

この形式を利用し、YAML構造を取得する部分が以下の関数の実装になっています。この仕組みを前処理に利用することで、メタデータを取得しています。

def extract_front_matter(file_path):
    """Markdownファイルからフロントマター(YAML)を抽出する"""
    with open(file_path, 'r', encoding='utf-8') as f:
        content = f.read()
    
    if not content.startswith('---'):
        return None
    
    try:
        parts = content.split('---', 2)
        if len(parts) >= 3:
            yaml_str = parts[1]
            return yaml.safe_load(yaml_str)
    except Exception as e:
        print(f"Error parsing {file_path}: {e}")
        return None
    
    return None

あともう一つ重要なのは公開済みの記事からtopicsを集計する部分であり、以下の実装にて対応されています。

if front_matter:
    # published: falseの記事を除外
    if front_matter.get('published', False) == False:
        unpublished_count += 1
        continue
    
    published_count += 1
    topics = front_matter.get('topics', [])
    
    if topics:
        article_topics.append({
            'file': md_file.name,
            'title': front_matter.get('title', 'Unknown'),
            'topics': topics
        })
        all_topics.extend(topics)

それでは実行してみる

uvを利用しているので、以下のように実行することで結果を作成することができます。

uv run analyze.py

すると、まずはmatplotlibを利用してTop 20のトピックが表示されます。結果をみるとpythonが一番使われており、その次にllm、続いてgooglecloudということがわかりました。

合わせて前結果を集計したresult.csvも添付しておきます。やはりgooglecloudvertexaiterraformなどクラウドインフラに関連したものが多かったようです。

analyze_topic/result.csv
Topic,Count
python,57
llm,22
googlecloud,21
apache,16
vertexai,13
claudecode,12
terraform,11
kubernetes,10
cli,9
zsh,9
CLI,9
genai,8
fastapi,7
git,7
技術書,6
ray,6
agent,6
openai,6
コマンド,6
ml,5
machinelearning,5
book,5
claude,5
O'Reilly,4
terminal,4
cloudstorage,4
kubeflow,4
cloudrun,4
java,4
matplotlib,4
coding,4
googlecloud認定資格,4
Apache,3
langchain,3
cncf,3
devtools,3
docker,3
data,3
command,3
go,3
math,3
colabenterprise,3
mlops,3
optuna,3
kind,3
aim,2
db,2
visualize,2
sqlite3,2
application,2
pytorch,2
lightning,2
deeplearning,2
techblog,2
pillow,2
search,2
regex,2
uv,2
書籍紹介,2
dvc,2
google,2
bigquery,2
pubsub,2
secretmanager,2
arxiv,2
aiagent,2
gonumplot,2
Python,2
openlit,2
自己紹介,2
observability,2
shell,2
cat,2
editor,2
tool,2
dataflow,2
jailbreak,2
numpy,2
agno,2
annotation,2
iam,2
security,2
featurestore,2
ci,2
githubactions,2
image,2
modelarmor,2
cloud,2
sklearn,2
pydantic,2
env,2
kfp,2
mlflow,2
game,2
workflow,2
Java,1
SpringBoot,1
restapi,1
graphql,1
datadog,1
Observability,1
hallucination,1
gemini,1
tracking,1
stats,1
programming,1
cocomo,1
kubernetex,1
gke,1
usd,1
preview,1
3d,1
graphics,1
wordcloud,1
blockchain,1
incubator,1
bubblesort,1
algorithm,1
pipelines,1
compressantlibrary,1
PyPI,1
library,1
eventarc,1
マネジメント,1
DNS,1
pyarmor,1
難読化,1
obfuscation,1
node,1
mise,1
information,1
slack,1
community,1
fractal,1
自己管理,1
filtering,1
grype,1
vulnerability,1
poetry,1
仮想化,1
llamaindex,1
workflows,1
pluto,1
apacheportals,1
ポータル,1
opentelemetry,1
async,1
class,1
dagshub,1
paper,1
research,1
alphaxiv,1
cursor,1
asyncio,1
http,1
teaching,1
グラフ,1
guardrails,1
自己学習,1
entry,1
peco,1
ghq,1
zenn,1
grpc,1
rpc,1
github,1
openllmetry,1
traceloop,1
celebration,1
terminalizer,1
record,1
tflint,1
linter,1
IaC,1
subscription,1
plan,1
airflow,1
apachebeam,1
agents,1
functioncalling,1
multiagents,1
ブログネタ,1
nvim,1
lua,1
keymap,1
aws,1
engineering,1
jupyter,1
pandas,1
ML,1
guardrail,1
mypy,1
type,1
serviceaccount,1
auth0,1
authentication,1
authorization,1
dataengineerimg,1
engineer,1
ls,1
shellscript,1
apikey,1
dashboard,1
monitoring,1
golang,1
test,1
testing,1
graph,1
montyhall,1
torch,1
BigQuery,1
CloudStorage,1
Dataprep,1
dataengineering,1
コマンドライン,1
click,1
gitlab,1
pipeline,1
devops,1
devsecops,1
roulette,1
kuberntes,1
lens,1
rag,1
datascience,1
legal,1
statstics,1
modelgarden,1
fundamental,1
distributedcomputing,1
multiprocess,1
macbook,1
dev,1
IDE,1
promptinjection,1
safety,1
blog,1
lakeFS,1
designpattern,1
GoF,1
chainofresponsibility,1
typing,1
document,1
optimization,1
akinator,1
slachcommand,1
tips,1
recover,1
agentbuilder,1
pytho,1
design,1
antipattern,1
network,1
proxy,1
apacheguacamole,1
visualization,1
nvidia,1
cuml,1
勉強法,1
ネットワーク,1
ant,1
maven,1
アドベントカレンダー,1
エンジニアリング,1
継続,1
pde,1
validation,1
notebook,1
argocd,1
kdash,1
mlflow3,1
promptengineering,1
rust,1
sql,1
PyCaret,1
drawio,1
chatgptで書いた,1
ツール,1
apachejclouds,1
pytorchlightning,1
mnist,1
データ圧縮,1
runlength,1
compress,1
cheetsheet,1
serialization,1
bash,1
mac,1
apachelinkis,1
円周率,1
モンテカルロ,1
spellcheck,1
english,1
notion,1
notionapi,1
langflow,1
mist.io,1
curl,1
sh,1
ethicalai,1
tesseract,1
parallel,1
cargo,1
ollama,1
deepseek,1
bigqueryml,1
automl,1
確率,1
ytt,1
yaml,1
googlecloudsdk,1
act,1
cicd,1
logging,1
kustomize,1
kubectl,1
dspy,1
task,1
make,1
taskfile,1
vibecoding,1
man,1
ai,1
secure,1
zenml,1
python3,1
newfeature,1
pyinstrument,1
profiling,1

まとめ

今回はZennの公開済みの記事からtopicsを集計する機能をclaude codeに実装させてみました。予想はしていましたが、Pythonが一番多かったのと、GoogleCloudとLLM関連の物も多かったです。Zennの記事をGitHubで管理されている方はぜひ利用してみてください!

Discussion