156 lines
4.7 KiB
Python
156 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
fetch_projects.py — 抓取同档比较项目的元数据(arXiv + Semantic Scholar + GitHub)
|
|
输出 JSON 到 stdout 与 projects.json,供后续 comparison.md 编写引用。
|
|
"""
|
|
from __future__ import annotations
|
|
import json
|
|
import pathlib
|
|
import sys
|
|
import time
|
|
from typing import Any
|
|
import urllib.parse
|
|
import urllib.request
|
|
|
|
OUT = pathlib.Path(__file__).parent / "projects.json"
|
|
|
|
# 待研究项目列表
|
|
PROJECTS = [
|
|
{
|
|
"key": "concept-graphs",
|
|
"name": "ConceptGraphs",
|
|
"arxiv": "2309.16650",
|
|
"gh": "concept-graphs/concept-graphs",
|
|
"homepage": "https://concept-graphs.github.io/",
|
|
},
|
|
{
|
|
"key": "hov-sg",
|
|
"name": "HOV-SG",
|
|
"arxiv": "2403.17846",
|
|
"gh": "hovsg/HOV-SG",
|
|
"homepage": "https://hovsg.github.io/",
|
|
},
|
|
{
|
|
"key": "openscene",
|
|
"name": "OpenScene",
|
|
"arxiv": "2211.15654",
|
|
"gh": "pengsongyou/openscene",
|
|
"homepage": "https://pengsongyou.github.io/openscene",
|
|
},
|
|
{
|
|
"key": "3d-llm",
|
|
"name": "3D-LLM",
|
|
"arxiv": "2307.12981",
|
|
"gh": "UMass-Foundation-Model/3D-LLM",
|
|
"homepage": "https://vis-www.cs.umass.edu/3dllm/",
|
|
},
|
|
{
|
|
"key": "ok-robot",
|
|
"name": "OK-Robot",
|
|
"arxiv": "2401.12202",
|
|
"gh": "ok-robot/ok-robot",
|
|
"homepage": "https://ok-robot.github.io/",
|
|
},
|
|
{
|
|
"key": "openmask3d",
|
|
"name": "OpenMask3D",
|
|
"arxiv": "2306.13631",
|
|
"gh": "OpenMask3D/openmask3d",
|
|
"homepage": "https://openmask3d.github.io/",
|
|
},
|
|
{
|
|
"key": "conceptfusion",
|
|
"name": "ConceptFusion",
|
|
"arxiv": "2302.07241",
|
|
"gh": "concept-fusion/concept-fusion",
|
|
"homepage": "https://concept-fusion.github.io/",
|
|
},
|
|
{
|
|
"key": "clio",
|
|
"name": "Clio (MIT-SPARK)",
|
|
"arxiv": "2404.13696",
|
|
"gh": "MIT-SPARK/Clio",
|
|
"homepage": "https://clio-cmu.github.io/",
|
|
},
|
|
]
|
|
|
|
UA = "Mozilla/5.0 (research) PRISM-comparison/1.0"
|
|
|
|
|
|
def _get(url: str, headers: dict[str, str] | None = None,
|
|
timeout: int = 20) -> str:
|
|
h = {"User-Agent": UA, "Accept": "application/json"}
|
|
if headers:
|
|
h.update(headers)
|
|
req = urllib.request.Request(url, headers=h)
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=timeout) as r:
|
|
return r.read().decode("utf-8", errors="replace")
|
|
except Exception as e:
|
|
return json.dumps({"_error": str(e)})
|
|
|
|
|
|
def fetch_arxiv(arxiv_id: str) -> dict[str, Any]:
|
|
"""Semantic Scholar 一次查到 abstract、引用数、年份"""
|
|
url = (
|
|
"https://api.semanticscholar.org/graph/v1/paper/"
|
|
f"arXiv:{arxiv_id}?fields=title,abstract,year,citationCount,"
|
|
"authors.name,venue,publicationVenue,referenceCount,influentialCitationCount,"
|
|
"openAccessPdf,fieldsOfStudy"
|
|
)
|
|
raw = _get(url)
|
|
try:
|
|
return json.loads(raw)
|
|
except Exception as e:
|
|
return {"_error": f"json: {e}", "_raw": raw[:300]}
|
|
|
|
|
|
def fetch_github(repo: str) -> dict[str, Any]:
|
|
url = f"https://api.github.com/repos/{repo}"
|
|
raw = _get(url, headers={"Accept": "application/vnd.github+json"})
|
|
try:
|
|
d = json.loads(raw)
|
|
# 只保留关心字段
|
|
if "stargazers_count" in d:
|
|
return {
|
|
"stars": d.get("stargazers_count"),
|
|
"forks": d.get("forks_count"),
|
|
"subscribers": d.get("subscribers_count"),
|
|
"language": d.get("language"),
|
|
"license": (d.get("license") or {}).get("spdx_id"),
|
|
"open_issues": d.get("open_issues_count"),
|
|
"created_at": d.get("created_at"),
|
|
"updated_at": d.get("updated_at"),
|
|
"pushed_at": d.get("pushed_at"),
|
|
"topics": d.get("topics"),
|
|
"description": d.get("description"),
|
|
"default_branch": d.get("default_branch"),
|
|
"archived": d.get("archived"),
|
|
"homepage": d.get("homepage"),
|
|
}
|
|
return d
|
|
except Exception as e:
|
|
return {"_error": f"json: {e}", "_raw": raw[:300]}
|
|
|
|
|
|
def main() -> None:
|
|
out: dict[str, Any] = {}
|
|
for p in PROJECTS:
|
|
sys.stderr.write(f"[fetch] {p['name']} ({p['arxiv']}) ...\n")
|
|
out[p["key"]] = {
|
|
"meta": p,
|
|
"arxiv_via_s2": fetch_arxiv(p["arxiv"]),
|
|
}
|
|
time.sleep(1.2) # 礼貌:S2 rate limit
|
|
out[p["key"]]["github"] = fetch_github(p["gh"])
|
|
time.sleep(0.8)
|
|
|
|
OUT.write_text(json.dumps(out, ensure_ascii=False, indent=2),
|
|
encoding="utf-8")
|
|
sys.stderr.write(f"[ok] wrote {OUT}\n")
|
|
print(f"saved {OUT}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|