summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEltrac <hi@guhub.cn>2025-05-23 19:32:43 +0800
committerEltrac <hi@guhub.cn>2025-05-23 19:32:43 +0800
commitc5ecf0d1ddc27e42c83ddfcd50d2cc2b9b108694 (patch)
tree17dadd07a52dd64459bd138cb65bc254465049bb
parent3bb1e46b2a4a91bdc7480fd43528757cde9d7820 (diff)
chore: comebine two link analysis script
Former-commit-id: e50c574d8d969a38eb1c3068b537fa220503adb7
-rw-r--r--scripts/analyze_links.py (renamed from scripts/extractLinks.py)91
-rw-r--r--scripts/listExternalLinks.py53
2 files changed, 37 insertions, 107 deletions
diff --git a/scripts/extractLinks.py b/scripts/analyze_links.py
index eb172353..598f4a94 100644
--- a/scripts/extractLinks.py
+++ b/scripts/analyze_links.py
@@ -1,15 +1,18 @@
import os
import re
import json
+from urllib.parse import urlparse
+from collections import Counter
content_dir = "content"
internal_link_pattern = re.compile(r"\[[^\]]*\]\((/[^)]+)\)")
+markdown_link_re = re.compile(r"\[.*?\]\((http.*?)\)")
url_map = {} # file path -> canonical url
alias_map = {} # alias url -> canonical url
link_graph = {} # canonical url -> list of canonical urls
title_map = {} # canonical url -> title
-
+external_links = Counter()
def parse_frontmatter(filepath):
with open(filepath, encoding="utf-8") as f:
@@ -19,12 +22,8 @@ def parse_frontmatter(filepath):
return {}, 0
frontmatter = {}
- aliases = []
- slug = None
- draft = False
- title = None
- related_cards = []
- mentioned_books = []
+ aliases, related_cards, mentioned_books = [], [], []
+ slug, draft, title = None, False, None
i = 1
while i < len(lines):
line = lines[i].strip()
@@ -39,8 +38,7 @@ def parse_frontmatter(filepath):
aliases = []
i += 1
while i < len(lines) and lines[i].startswith(" - "):
- alias = lines[i].strip()[3:].strip()
- aliases.append(alias)
+ aliases.append(lines[i].strip()[3:].strip())
i += 1
continue
elif key == "slug":
@@ -74,24 +72,18 @@ def parse_frontmatter(filepath):
"mentioned_books": mentioned_books
}, i + 1
-
def get_canonical_url(filepath, frontmatter):
rel_path = os.path.relpath(filepath, content_dir)
dirname, filename = os.path.split(rel_path)
name = os.path.splitext(filename)[0]
slug = frontmatter.get("slug") or name
parts = dirname.split(os.sep)
- if parts == [""]:
- return f"/{slug}/"
- return f"/{'/'.join(parts)}/{slug}/"
-
+ return f"/{'/'.join(parts)}/{slug}/" if parts != [""] else f"/{slug}/"
def resolve_url(url):
return alias_map.get(url, url)
-
def resolve_url_from_dir(link, subdir):
- """Only search for link in a specific content subdir (like 'cards', 'library')"""
target_dir = os.path.join(content_dir, subdir)
for path, canonical in url_map.items():
if path.startswith(target_dir):
@@ -100,21 +92,14 @@ def resolve_url_from_dir(link, subdir):
return canonical
return None
-
def get_icon(url):
- if url.startswith("/cards/"):
- return "📄"
- elif url.startswith("/posts/"):
- return "📜"
- elif url.startswith("/library/"):
- return "📖"
- elif url.startswith("/fictions/"):
- return "🧙‍♀️"
- elif url.startswith("/en/"):
- return "🇬🇧"
+ if url.startswith("/cards/"): return "📄"
+ if url.startswith("/posts/"): return "📜"
+ if url.startswith("/library/"): return "📖"
+ if url.startswith("/fictions/"): return "🧙‍♀️"
+ if url.startswith("/en/"): return "🇬🇧"
return "📁"
-
def collect_urls():
for root, _, files in os.walk(content_dir):
for file in files:
@@ -134,54 +119,52 @@ def collect_urls():
for alias in frontmatter.get("aliases", []):
alias_map[alias] = canonical_url
-
-def build_link_graph():
+def build_link_graph_and_external_links():
for path, canonical_url in url_map.items():
with open(path, encoding="utf-8") as f:
content = f.read()
- # markdown links
+ # 内部链接
for match in internal_link_pattern.finditer(content):
link = match.group(1)
normalized = resolve_url(link)
if normalized and normalized in link_graph:
link_graph[canonical_url].append(normalized)
- frontmatter, _ = parse_frontmatter(path)
+ # 外部链接
+ for match in markdown_link_re.findall(content):
+ domain = urlparse(match).netloc
+ if domain and domain not in {"image.guhub.cn", "www.geedea.pro"}:
+ external_links[domain] += 1
- # related_cards → only from cards
+ # 相关卡片和图书
+ frontmatter, _ = parse_frontmatter(path)
for card in frontmatter.get("related_cards", []):
target = resolve_url_from_dir(card, "cards")
if target and target in link_graph:
link_graph[canonical_url].append(target)
-
- # mentioned_books → only from library
for book in frontmatter.get("mentioned_books", []):
target = resolve_url_from_dir(book, "library")
if target and target in link_graph:
link_graph[canonical_url].append(target)
-
def main():
collect_urls()
- build_link_graph()
-
- nodes = [
- {"id": url, "label": title_map[url], "url": url}
- for url in link_graph
- ]
-
- edges = []
- for source, targets in link_graph.items():
- for target in targets:
- edges.append({"from": source, "to": target})
-
- graph = {"nodes": nodes, "edges": edges}
-
- output_path = os.path.join("public", "link-graph.json")
- with open(output_path, "w", encoding="utf-8") as f:
- json.dump(graph, f, indent=2, ensure_ascii=False)
- print(f"✅ Link graph generated: {output_path}")
+ build_link_graph_and_external_links()
+
+ # 写入内部链接图
+ os.makedirs("public", exist_ok=True)
+ nodes = [{"id": url, "label": title_map[url], "url": url} for url in link_graph]
+ edges = [{"from": src, "to": tgt} for src, targets in link_graph.items() for tgt in targets]
+ with open("public/link-graph.json", "w", encoding="utf-8") as f:
+ json.dump({"nodes": nodes, "edges": edges}, f, indent=2, ensure_ascii=False)
+ print("✅ Link graph generated: public/link-graph.json")
+
+ # 写入外部链接统计
+ os.makedirs("data", exist_ok=True)
+ with open("data/externallinks.json", "w", encoding="utf-8") as f:
+ json.dump(external_links.most_common(), f, indent=2, ensure_ascii=False)
+ print("✅ External link data written to data/externallinks.json")
if __name__ == "__main__":
main()
diff --git a/scripts/listExternalLinks.py b/scripts/listExternalLinks.py
deleted file mode 100644
index 89df0605..00000000
--- a/scripts/listExternalLinks.py
+++ /dev/null
@@ -1,53 +0,0 @@
-import os
-from urllib.parse import urlparse
-from collections import Counter
-import json
-import re
-
-content_dir = "content"
-external_links = Counter()
-
-# 匹配 Markdown 中的 URL 链接 [text](url)
-markdown_link_re = re.compile(r'\[.*?\]\((http.*?)\)')
-
-def extract_front_matter_and_content(path):
- with open(path, 'r', encoding='utf-8') as f:
- lines = f.readlines()
-
- if not lines or lines[0].strip() != '---':
- return {}, ''.join(lines)
-
- front_matter = {}
- end_index = None
- for i in range(1, len(lines)):
- if lines[i].strip() == '---':
- end_index = i
- break
- key_value = lines[i].split(':', 1)
- if len(key_value) == 2:
- key, value = key_value
- front_matter[key.strip()] = value.strip()
-
- content = ''.join(lines[end_index+1:]) if end_index else ''.join(lines)
- return front_matter, content
-
-def extract_links_from_file(path):
- _, content = extract_front_matter_and_content(path)
-
- # 提取 Markdown 格式链接
- for match in markdown_link_re.findall(content):
- domain = urlparse(match).netloc
- if domain and domain != 'image.guhub.cn' and domain != 'www.geedea.pro':
- external_links[domain] += 1
-
-for root, dirs, files in os.walk(content_dir):
- for file in files:
- if file.endswith(".md"):
- extract_links_from_file(os.path.join(root, file))
-
-# 写入 JSON
-os.makedirs("data", exist_ok=True)
-with open("data/externallinks.json", "w", encoding="utf-8") as f:
- json.dump(external_links.most_common(), f, indent=2, ensure_ascii=False)
-
-print("✅ External link data written to data/externallinks.json")