GitHub `pushed_at` 誤導了 35 個爬蟲倉庫中的 14 個;其中 3 個已確認是機器人所致

最後更新於 August 14, 2026
GitHub `pushed_at` 誤導了 35 個爬蟲倉庫中的 14 個;其中 3 個已確認是機器人所致
AI 摘要
GitHub's repository page exposes pushedat, a timestamp that can move when any branch receives a push. That can diverge from the newest commit on the default branch. The default-branch date is a repository-maintenance signal; it is not necessarily the code installed by a package manager. Package managers normally resolve registry artifacts or module versions. That is why this audit checks repository activity and the published artifact separately: either can be current while the other is stale. So I pulled 35 repos that still show up in recommendations, and read the number GitHub doesn't put in the header: the date of the newest commit on the default branch.

GitHub 的儲存庫頁面會顯示 pushed_at,這是一個時間戳,只要任何分支有 push 就可能更新。這就可能和 default branch 上最新 commit 的時間不一致。default branch 的日期只能代表倉庫維護狀態;它不一定等於套件管理器實際安裝的程式碼。

套件管理器通常解析的是 registry 裡的發佈物或模組版本。因此這次稽核才會把「倉庫活動」和「已發佈的 artifact」分開檢查:兩者都可能是最新的,也可能其中一個已經過時。

所以我挑了 35 個仍然出現在推薦中的 repo,去看 GitHub 標題列沒寫出的那個數字:default branch 上最新 commit 的日期

結果確實存在落差:35 個裡有 14 個的 pushed_at 比 default branch 的最新 commit 早了超過 180 天,最極端的差距達到 1,802 天。其中 14 個裡有 3 個已明確確認是 bot 造成;排除 archived 與人為活動後,9 個候選裡只剩 2 個可確認是 bot。更有價值的發現是:repo 的新鮮度和已發佈 artifact 的新鮮度,常常是兩回事。

我測了什麼,以及怎麼測的

System diagram: What was measured, and on what

官方參考:GitHub repository API

文中所有數字都來自 2026-07-27 UTC 15:44 到 15:53 之間的即時 API 回應,並已快取保存。35 筆資料集repo 清單建構後的資料列,以及 artifacts/ 裡的抓取與建構腳本,都保留了這次稽核的輸入與轉換程式。

我們收集了四類資料;其中 registry 與活動查詢不是固定四次呼叫的同一流程,而是按需執行:

  • GET /repos/{owner}/{repo} — stars、archivedpushed_at、license、default_branch
  • GET /repos/{o}/{r}/commits?sha={default_branch}&per_page=1 — default branch 最新 commit,用來判斷 repo 是否仍在維護。
  • GET /repos/{o}/{r}/activity?per_page=30 — 對於前兩者不一致的 repo,確認到底是什麼更新了 pushed_at
  • GET /repos/{o}/{r}/releases 加上 PyPI 與 npm registry — 看 artifact 上次是什麼時候發佈的;事實證明,這比前兩者都更重要。

staleness_days 是從 default branch 最新 commit 到「截至當下」這個時間點之間的經過天數。pushed_at 與該 commit 的差距就是這個錯覺,單位也是天。任何超過 180 天的都會被標記。

有兩個方法學上的重點,因為它們真的改變了結果。

套件歸屬一律驗證,不靠推測。 README 提到某個 repo,不代表那就是該套件的 repo。每一個對應關係都必須用結構化欄位確認——要嘛是 registry 裡自己的 repository/project_urls,要嘛是 repo 內提交的 manifest。共有 6 個看起來很合理的對應在這個標準下沒通過,所以它們的下載數不會被歸到該 repo 名下。

其中一個被排除的案例,足以說明這條規則的重要性。curl-cffi 每月有 35,763,529 次下載,從外觀看很像 lwthiker/curl-impersonate 的 Python 封裝,而後者是一個已經冷卻 875 天的 repo。如果把兩者硬連起來,數字會變成 newspaper3k 的 44 倍,但那會是錯的:curl-cffi 自己的 PyPI metadata 指向的是 lexiforest/curl_cffi,這是另一個仍在活躍維護的專案,最近一次發佈是 2026-04-03。這裡能拿到的最驚人數字,偏偏是錯的那個。

第七個案例更怪:steel-dev/steel-mcp-server 在自己的 package.json 裡宣告了 @steel-dev/mcp-server,但 npm 回傳 404。它從來沒有正式發佈過,所以根本不能說它「還在被安裝」。

拿不到數字的地方,我就明說拿不到。 Go、JVM、.NET 和 PHP 工具沒有 PyPI 或 npm 的存在,因此都標成 N/A (no PyPI/npm package),絕不是 0。35 個裡有 17 個完全沒有 GitHub Releases;這記為 none,不是缺資料。

這些欄位刻意不被合併成一個「健康分數」。default branch 過時、側分支很新、沒有 GitHub Release、registry artifact 很老,這些都在回答不同問題。每一列的證據都可在 35 筆資料集 中找到,並與 repo 輸入建構後紀錄 並列。把它們看成是分流判斷訊號,用來決定下一步要查什麼,而不是對專案是否存活的四張投票。

先說明抽樣限制

這份清單是我手工挑出來的,目的是找那些看起來是在吃老本的工具。它不是整個爬蟲生態的隨機樣本,所以「35 個裡有 31 個過時」不是生態系統的比例——那只是我挑得準不準的近似值。真正有趣的不是過時數量,而是:即使在一個刻意挑出這種現象的樣本裡,我要驗證的那個機制也只解釋了少數案例,而且能明確確認的更少。

這個錯覺是真的,而且最極端的例子就在這裡

sjdirect/abot,一個有 2,308 顆 stars 的 .NET 爬蟲。GitHub 顯示它在 2026-07-17 有 push,距離取樣時間只差 10 天;但 default branch 上最後一次變動其實是 2021-08-09

兩者相差 1,802 天。整整五年。標題列卻讓人以為是上週。

35 個 repo 裡,有 14 個的差距超過 180 天:

Repo差距(天)default branch 最後變動pushed_at
sjdirect/abot1,8022021-08-092026-07-17
dragnet-org/dragnet1,5202021-05-092025-07-08
paquettg/php-html-parser1,3762020-11-012024-08-09
seomoz/simhash-py1,1592020-03-122023-05-15
internetarchive/wayback1,0392021-04-272024-03-01
Rhizome-Conifer/conifer1,0132023-10-122026-07-22
kohlschutter/boilerpipe8562015-08-302018-01-03
scrapinghub/splash8192022-05-052024-08-02
tomnomnom/waybackurls7562022-04-052024-05-01
geziyor/geziyor6892024-08-122026-07-02
crawlab-team/crawlab4882024-10-092026-02-10
ArchiveTeam/wpull4682023-01-162024-04-29
yasserg/crawler4j3962020-10-032021-11-04
apache/any233812022-06-032023-06-20

14 / 35。這現象是真的、值得知道,但它只是我刻意選出來的樣本中的少數。

三個被確認是 bot 的標記 repo,而過濾後只剩兩個

這類故事的民間版本,總是直接說是 dependabot。我實際做法是把每個被標記 repo 的 activity feed 抓下來,並分類最後一次 default branch commit 之後的每個 push ref。這個「之後」很重要:早於最後 commit 的事件,無法解釋是什麼把差距拉大;如果把整個 feed 都算進去,問題就被悄悄換掉了。

依照「只看 commit 後事件」的方法,已確認由 bot 驅動的有三個: scrapinghub/splash(4/4 的 post-commit 事件都來自 dependabot/pip/*)、geziyor/geziyor(5/5 都來自 dependabot/go_modules/*)、apache/any23(16/16 都來自 dependabot/maven/*)。這三個裡面,any23 又是正式 archived,所以它不會進入後面的過濾集合——最後只剩 2 個 同時滿足其他條件、也確定是 bot 的案例。

**完全判斷錯誤的有兩個,**而且都比 bot 故事更有意思。

dragnet-org/dragnet 的 1,520 天差距,來自一位人類推送了一個叫 mp/py3.10 的分支——那是一個還沒合併的 Python 3.10 移植。有人想把它往前推,最後停下來了。這不是自動化噪音把時間戳撐大;這是一次救火嘗試留下的、可見且有日期的紀錄。嚴格說,這可能是整份資料裡最有用的訊號,而「是 dependabot 幹的」這種說法反而會把它抹掉。

混合型,而且規模還比兩者都大:兩個。 crawlab-team/crawlab 有 12,250 顆 stars,是這份樣本裡第二高的;它在 main 上有 488 天的差距。它的 feed 同時包含 dependabot 分支,還有 24 次來自人類的 post-commit push,而且全部都在 developtest。只看標題列的人會看到 2026 年 2 月,以為它還健康;只看 main 的人會看到 2024 年 10 月,以為它已經死了。兩種解讀都錯。開發已經轉到 default branch 以外,而這正是很多專案的實際做法,GitHub 的摘要頁卻無法呈現。sjdirect/abot 是另一個混合案例:把它推成標題 pushed_at 的那次 push 確實是 dependabot,但 2024 年又有人推了 upgrade1,所以它之後才會在過濾集合裡被排除。

Rhizome-Conifer/conifer 是最模稜兩可的一個,我一開始也看錯了。它的 default branch 是 main,不是 master,而 main 自 2023-10-12 後就沒有再動過——feed 裡唯一的 main 事件是 2025 年 1 月的分支建立,和改名一致。另一方面,只有一個帳號在 2026-07-22conifer-twilighttwilight/read-only 推送,距離取樣時間只差 5 天。這確實是人為活動,但「仍在積極開發」這種說法,資料其實撐不起來:只有一個貢獻者,而且分支名稱裡還有 read-only,這至少和「受控退場」一樣合理。能確定的只有一件事,而且這件事本身就值得說:1,013 天的差距不是 bot 噪音,但也不能直接拿來當成棄置證據。

未知:七個。 php-html-parsersimhash-pyinternetarchive/waybackboilerpipewaybackurlswpullcrawler4j 都有可驗證的差距,但 activity feed 回傳的是 空白

直覺上最容易想到的解釋是保留期限——GitHub 的 activity feed 不會永久往回保留。快取結果對大多數案例否定了這點。這 123 個回應中最早的事件是 2023-03-10,而這 7 個裡有 5 個的 pushed_at 明明都在這個窗口內:php-html-parser 2024-08-09、wpull 2024-04-29、waybackurls 2024-05-01、internetarchive/wayback 2024-03-01、simhash-py 2023-05-15。能更新這些時間戳的事件理應出現在 feed 裡,但沒有。保留期限只能解釋 boilerpipe(2018)和 crawler4j(2021)。

所以老實說,結論比漂亮的解釋更薄:這 7 個 repo 的差距是已被驗證的事實,但原因沒有被建立起來——endpoint 回傳了空白,而其中 5 個我也無法告訴你為什麼。把它們直接說成「dependabot 幹的」只是臆測。

把 14 個被標記 repo 加總後,結果如下:

造成 pushed_at 膨脹的原因Repo 數哪些,以及依據是什麼
已確認由 bot 驅動3scrapinghub/splash(4/4 的 post-commit 事件都在 dependabot/pip/*)、geziyor/geziyor(5/5 在 dependabot/go_modules/*)、apache/any23(16/16 在 dependabot/maven/*)— any23 已 archived,因此最後只剩 2 個 同時滿足其他條件
混合型,bot 與人類都有2crawlab-team/crawlab(dependabot 分支加上 24 次人類 post-commit push,全部在 developtest)、sjdirect/abot(把它推成標題 pushed_at 的那次 push 確實是 dependabot,但 2024 年有人推了 upgrade1
完全不是那樣 —— 人為工作、沒有 bot 分支2dragnet-org/dragnet(3 個 post-commit 事件,0 個在 bot 分支)— 人類推送了 mp/py3.10,一個未合併的 Python 3.10 移植。Rhizome-Conifer/conifer(30 個 post-commit 事件,0 個在 bot 分支)— 2026-07-22 有一個帳號推送了 conifer-twilighttwilight/read-only。至於 conifer 是否「被棄置」,如上所述仍然模糊;但能確定的是,沒有 bot 把它的 pushed_at 撐大
無法建立原因 — activity feed 空白7php-html-parsersimhash-pyinternetarchive/waybackboilerpipewaybackurlswpullcrawler4j

總數就是 14。這些只是分類誰更新了 pushed_at;並不能獨立證明某個專案是否被棄用。

通過完整過濾條件的有誰,而「通過」又代表什麼

原始說法需要同時滿足四件事:過時超過一年、pushed_at 被拉大超過 180 天、不是 archived、而且沒有跡象顯示這個膨脹來自人工作業。35 個候選裡有 9 個完全通過這四關,如下,旁邊也列出兩個最重要的排除案例:

Repo是否通過四關?有利於判斷是 bot 造成膨脹的證據
splash是 — 4/4 的 post-commit 事件都在 dependabot/pip/*
waybackurls雙向都沒有證據
crawler4j雙向都沒有證據
geziyor是 — 5/5 都在 dependabot/go_modules/*
php-html-parser雙向都沒有證據
boilerpipe雙向都沒有證據
wpull雙向都沒有證據
internetarchive/wayback雙向都沒有證據
simhash-py雙向都沒有證據
any23否 — 已 archived是 — 16/16 都在 dependabot/maven/*,是整份稽核中最強的單一確認
abot否 — 紀錄中包含人為 push(upgrade1,2024)混合型 — 把它推成標題 pushed_at 的那次 push 確實是 dependabot

這個數字還需要一個過濾器本身裝不下的修飾語。這 9 個裡只有 2 個——splashgeziyor——有正向證據證明是 bot 推高了時間戳。 其餘 7 個只是因為「沒有看到反證」而通過第四關。它們是「這個說法仍成立」的案例,不是「這個說法已被證實」的案例。而整份稽核中最強的單一確認,any23 的 16/16 dependabot 事件,因為 repo 已 archived,所以被排除在外。

另外也要注意,1,802 天的那個 abot 並不在這 9 個之中。它的紀錄裡有一個人為 push,所以它不符合第四個條件——這份資料裡最戲劇化的錯覺,反而不是它所示範那個機制的乾淨案例。

35 個裡有 21 個,GitHub 其實有老實顯示過時

這是最傷我原先論點的一個發現。14 個 repo 的差距剛好是 0,另外 7 個也都在 180 天以內。也就是說,35 個候選裡有 21 個,pushed_at 本來就是 default branch 的最後 commit。GitHub 沒有在遮掩什麼。

包括這份樣本裡一些最老舊的項目:

RepoStars過時(天)差距
Janpot/microdata-node571,8660
1e0ng/simhash1,0371,60621
ekzhu/SetSimilaritySearch6031,3840
GerbenJavado/LinkFinder4,4318340
hakluke/hakrawler5,0995820
lavague-ai/LaVague6,3885510
my8100/scrapydweb3,4115220
getomni-ai/zerox12,2584320
scrapinghub/frontera1,3324150
BuilderIO/gpt-crawler22,3743840

BuilderIO/gpt-crawler 有 22,374 顆 stars,而且從 2025-07-07 以來,它的標題列一直都說同一件事。沒有被隱藏,安裝量也還在持續。

這迫使原本的說法縮小成更精確的版本:GitHub 會在少數案例中掩蓋過時,但在大多數案例裡它其實講得很清楚,只是安裝照樣繼續。 為什麼還在裝,這份資料無法回答——這裡的數字只算安裝量,不算決策。但就算要改介面,也改不了第二群,也就是規模更大的那一群。

倉庫活動與已發佈 artifact 可以完全脫鉤

資料集中最醒目的那一列,直接把原本的框架打破了。

codelucas/newspaper — 15,126 顆 stars — 是活著的。它最後一次 default branch commit 的日期是 2026-07-21,距離取樣日 2026-07-27 只差幾天,而且那次是由維護者本人提交的。repo 層級的檢查全部都過。

但大家真正安裝的是 newspaper3k 0.2.8,其發佈日期是 2018-09-28。這已經是 2,858 天 前的版本,而且每月仍有 813,513 次下載。

default branch 是最新的,但 PyPI artifact 自 2018 年後就沒有再發佈過。這只能證明 release 有落差,不能證明為什麼沒發佈,或是不是 pipeline 壞了。若只看 repo 的維護狀況,這種風險是看不到的,因為 pip install newspaper3k 後真正執行的,就是 registry 裡的 artifact。

一旦把觀察重點從 repo 轉到 package,這個模式到處都是。在 17 個已驗證 repo 歸屬的 package 裡,16 個距離上次發佈都超過一年,而這 16 個合計約占 230 萬次/月 安裝量中的 228 萬次/月

Package每月安裝量最近發佈套件年齡(天)
newspaper3k813,5132018-09-282,858
tls-client790,3052024-02-02905
simhash317,6152022-03-031,606
microdata-node204,0252020-05-112,267
@modelcontextprotocol/server-puppeteer127,2322025-05-12440
extract-thinker10,9272025-06-09412
SetSimilaritySearch7,9842022-10-111,384
frontera4,7092019-04-052,669
zerox3,3032025-05-20432
scrapydweb1,1632025-02-16525
lavague6062024-08-05720
splash3332020-06-162,231
dragnet2132019-04-162,658
@builder.io/gpt-crawler1372025-01-23549
lmnr-index1202025-06-05416
simhash-py1132017-03-223,413

tls-client 值得單獨提:一個 repo 已經冷卻 905 天,卻還有每月 790,305 次安裝;而在這種領域裡,跟著變化跑本來就是全部工作。瀏覽器的 TLS 行為會變;如果一個函式庫在 2024 年初之後就沒再跟進,那它其實是在用 2024 年初的假設運作。

這張表還有兩點要注意。registry 下載數包含 CI 與 mirror,而且不會去重,所以它量的是安裝流量,不是人數。再者,各個 rolling window 的截止日也不一樣——npm 接近 2026-07-24,而 pypistats 是以抓取時間為準——所以總和其實是幾個略有偏移的月份相加,應該讀成「大約 228 萬」,不是精確到個位數。

值得注意的名稱撞衫

internetarchive/wayback 是已經死掉的 Java OpenWayback,已經冷卻 1,916 天。PyPI 上的 wayback 則是完全不同的專案——edgi-govdata-archiving/wayback——而且狀態良好,曾在 2026-06-19 發佈 0.5.1,距離取樣日只有五週。同名、相反狀態、沒有關聯。這是前面 6 個被排除歸屬中的其中一個,也是最容易真的害到使用者的一個:你搜尋同一個名字,兩個都會出現,但任何頁面都不會告訴你你找到的是哪一個。

已 archived、已 deprecated,但每月仍有 127,232 次安裝

樣本裡有 6 個 repo 帶著 archived: true,GitHub 會把它渲染成整條橫幅。我的第一個解讀是:這證明了大家會無視很明顯的警告。但快取顯示,事情比這更糟。

官方參考:npm download-count API documentation

官方參考:npm's deprecation documentation

@modelcontextprotocol/server-puppeteer 每月有 127,232 次安裝,而且來自 modelcontextprotocol/servers-archived。但它的 npm repository 欄位是 null —— 也就是說,package 頁面根本沒有連回 repo 的路徑,因此也不存在「看到橫幅卻直接略過」這件事。多數在裝這個 package 的人,從一開始就沒有經過該 repo 的路徑。

npm 真正會送出的,是 deprecation。這個 package 的最新版本帶有 deprecated: "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",安裝時 npm 會直接印到終端機上。所以警告確實有送達,而且是送到使用者真的所在的地方,但 127,232 次/月的安裝還是照樣發生。這比橫幅本身更有力,也指向不同的問題:訊號不是沒有到,而是到了,但被塞進一大串安裝輸出裡,沒人被迫去看。

browserbase/mcp-server-browserbase 則示範了什麼叫乾淨收尾:它最後一個 default branch commit 在 2026-07-20,內容就是 「Mark repository as archived and unmaintained (#198)」。維護者有宣布、有日期,也在 API 裡標記了。它的 20,389 次安裝值得仔細看——npm 的觀察窗口是 2026-06-25 到 2026-07-24,所以這 30 天裡有 26 天 其實早於 archive commit。這個數字大多反映的是公告前的需求,不是公然反抗公告。至於公告之後發生什麼,從這個快照看不出來;如果要下結論,我會想再隔一個月重讀一次。

57 顆 stars,卻有每月 204,025 次安裝

Janpot/microdata-node 只有 57 顆 stars,但它來自一個 2020-05-11 的發佈,卻有每月 204,025 次下載。

以 57 顆 stars 來看,microdata-node 在 repo 端的可見度遠低於它在 registry 端的流量。3,579 比 1 的安裝/star 比例,很符合間接使用、CI 重複拉取、mirror,或直接機器端消費的情境。本次稽核沒有抓依賴圖,因此無法在這些說法中做選擇。

這一列提醒我們應該去盤點間接暴露,但要證明它真的被轉相依使用,還需要 reverse-dependency 或 lockfile 的證據,而這份稽核沒有收集。

過時,不等於壞掉

一份誠實的稽核必須說清楚:**這些數字都不能證明功能壞了。**它們只是在衡量還有沒有人在維護。

有些工具本來就已經完成使命。SetSimilaritySearch 實作的是集合相似度演算法;這類東西不會因為時間而腐壞。simhash 來自 2007 年的論文。boilerpipe 的內容擷取演算法,到了 2026 年和 2015 年其實還是同一套——不論它對現代網頁的準確度如何,程式碼本身並沒有自己漂走。

真正會腐壞的是任何一端會持續變動的東西:

  • 瀏覽器自動化——每一次 Chrome 發版都可能把它弄壞。
  • 模擬真實瀏覽器的 HTTP client 行為——瀏覽器會變,凍結的函式庫就跟不上;tls-client 就屬於這一類。
  • 站點專屬 parser 和每站規則——每次網站改版都是 bug。
  • 任何包裝第三方 API 的工具——供應商一改 schema,你通常就是在 production 才知道。
  • 任何包裝 LLM 的工具——模型退役的速度比這些都快。

所以「過時 1,606 天」對某些類別是五級警報,對雜湊工具卻幾乎無關痛癢。這裡沒有測任何工具是否真的壞了,也沒有對此做出主張;但你自己把依賴按類別分一分,成本幾乎是零,幫助卻遠比單看過時天數大。

真正回答問題的四個檢查

System diagram: The four checks that actually answer the question

這四個都不是 repo 標題列。

#檢查項目應該從哪裡看能抓到什麼
1default branch 上最後一次 commitGET /repos/{owner}/{repo}/commits?sha={default_branch}&per_page=1標題列不會直接顯示的那個數字。
2artifact 最後一次發佈的時間pypi.org/pypi/{pkg}/jsonregistry.npmjs.org/{pkg} → 最新版本與上傳日期這就是抓到 newspaper3k 的地方,而檢查 1 永遠抓不到。順便看 npm 的 deprecated 欄位,這正是 @modelcontextprotocol/server-puppeteer 告訴你的方式。
3archived 標記repo 回應中的單一欄位,清楚而直接但前提是你得先有 repo;像 repository: null 的 package 根本不會讓你走到這一步。
41 與 2 之間的差距—(由前兩項推得)一個 repo 如果有新 commit,但 release 卻是三年前的版本,這是另一種失敗:代表維護者還在,但沒有發佈。這是一個要睜大眼睛決定的訊號,本身不必然是紅旗。同樣的邏輯反過來也適用於 crawlab:先確認工作是不是已經轉到非 default branch,再下結論。

先把 1、2、3 當快速分流。接著在 repository metadata 不存在、package 對應不明、活動已轉到非 default branch,或 registry artifact 與 repo 脫鉤時,再升級處理。GitHub 的未認證限制與 registry 延遲,讓你不可能保證一秒內完成所有判斷。

如果某個檢查發現某個動作頻繁變動的類別已經很冷,那麼在我們自己的測試裡,這些仍在維護中的替代方案已經有紀錄:Trafilatura 可取代 dragnetboilerpipe 之前做的內容擷取;ScrapyCrawlee 可作為爬取框架;Crawl4AIFirecrawl 適合偏向 LLM 的擷取;而 Scrapling 則適合重視韌性的場景。我們的 開源爬蟲總覽 也整理了更完整的範圍。以上都是第一手評測;在相信任何推薦,包括我們自己的推薦之前,先自己跑完這四項檢查。

這份資料的限制

  • 抽樣方式。 這是人工挑選、且偏向懷疑已被棄置的工具。不能從中讀出整個生態系的比率。
  • 沒有做破壞性測試。 這 35 個工具沒有任何一個真的拿去跑現場網站。過時只是維護訊號,不是功能裁決。
  • 下載數包含機器。 CI、mirror、沒有去重,而且各窗口的截止日也不同。這是安裝量,不是使用者數,也不是決策數。
  • 7 個未知原因仍然未知。 activity feed 什麼都沒回;其中 5 個也不能用保留期限解釋。留空比硬塞一個大家愛猜的答案更誠實。
  • staleness_days 用的是 committer date。 被重寫或回填日期的歷史會扭曲它。這裡沒有偵測到,但「沒偵測到」不等於「不存在」。
  • 只有一個截至時間。 2026-07-27。等你看到這篇文章時,這些 repo 很可能都已經變了——特別是 newspaper,它本來就經常在提交。請重跑四項檢查,不要引用我的日期。

由託管服務改變的,不只是工具形式

這裡的每一個檢查之所以存在,是因為如果你用的是自架函式庫,過時問題就由你自己承擔。一個凍結的 HTTP client 如果開始不再像現代瀏覽器,那它在哪個時刻出問題,那就是你的 incident。

作者註: Thunderbit 是我們的託管式爬取產品。託管服務會把一部分維護責任轉給供應商,但覆蓋範圍、回應速度、鎖定風險與供應商持續性,也都會變成風險模型的一部分。本次 repo 稽核沒有評估 Thunderbit。

老實的取捨是:你放棄了自己讀原始碼、固定版本、以及在凌晨兩點自己修掉的能力。對已經在維護爬蟲的團隊來說,開源方案往往仍是正解——而四項檢查,就是讓你把它變成有意識的決策,而不是預設。

試試 Thunderbit 進行網頁資料擷取

簡短版總結

我原本建了一份清單,想證明 GitHub 會隱藏棄置狀態。結果這種錯覺在 35 個 repo 裡有 14 個是真的,而且有一個案例特別誇張:sjdirect/abot 的 default branch 自 2021 年以來就沒動,但標題列卻顯示它上週還有 push,差距高達 1,802 天。

但機制比故事窄。9 個 repo 完全通過了這個說法要求的所有條件,而這 9 個裡只有 2 個——splashgeziyor——有正向證據證明是 bot 推高了時間戳;其餘只是因為沒有看到證據而過關。另有兩個被標記的 repo,是人類試圖修復但失敗的案例。crawlab 則有 12,250 顆 stars,只是把開發搬到 develop。還有 7 個,activity feed 直接是空的,其中 5 個也不能用保留期限解釋,所以原因是未建立,而不是先假設。至於 35 個 repo 裡,有 21 個,GitHub 其實都把過時狀態說得很準。

資料集中最糟的案例,反而是 repo 層級檢查全都過關的那一個。codelucas/newspaper 在 2026-07-21 還有 commit;但 newspaper3k 上一次發佈是 2018-09-28,卻在當月仍被安裝了 813,513 次。整個樣本裡,16 個發佈超過一年沒更新的 package,合計大約占 228 萬次/月 安裝量。

初步分流時,先看 default branch commit 日期、registry 發佈日期、archived 標記,以及 npm 的 deprecation 欄位。等到要下結論時,再去確認 package 與 repo 的對應關係,以及是否存在非 default branch 的實際開發。

試試 Thunderbit 進行網頁資料擷取 Get Started Free

常見問題

什麼是 pushed_at,為什麼它不等於「最後更新」? pushed_at 是 GitHub API 中對應 repo 頁面活動時間戳的欄位,只要任何分支有 push 就會更新。default branch 的最新 commit 只是 repo 維護狀態的一個訊號;而套件管理器通常安裝的是 registry artifact 或已解析的模組版本。在這次稽核裡,35 個 repo 中有 14 個的這兩個 GitHub 日期相差超過 180 天。

時間戳總是因為 dependabot 被撐大的嗎? 不是,而且這也證明了民間故事最薄弱的地方。若只看最後一次 default branch commit 之後的事件,14 個被標記 repo 裡有 3 個已確認是 bot 造成(splash 4/4、geziyor 5/5、any23 16/16)。有 2 個則完全不是:dragnet 的差距來自人類推送一個未合併的 Python 3.10 移植。還有 2 個是混合型,包括 crawlab,它在 main 不動的同時,有 24 次人類 push 到 developtest。另外 7 個 activity feed 完全空白,所以原因未建立——其中只有 2 個可用保留期限解釋。

repo 過時,就代表工具壞了嗎? 不能從這份證據下這個結論——這裡沒有任何工具真的拿去跑現場網站。過時的嚴重性取決於目標變動有多快:瀏覽器自動化、模擬瀏覽器行為的 HTTP client、站點專屬 parser 與 API/LLM wrapper 都很快會老;但像 SetSimilaritySearchsimhash 這種演算法型函式庫,即使多年沒動也可能完全沒問題。tls-client 是這份樣本裡最明顯的案例:repo 冷卻了 905 天,但每月仍有 790,305 次安裝。

repo 可以還在活躍,但 package 卻已經死了嗎? 可以,codelucas/newspaper 就是最關鍵的例子。它的 default branch 在 2026-07-21 還有 commit,距離取樣日只差幾天;但 PyPI 上的 newspaper3k 最後一次發佈是 2018-09-28 的 0.2.8——已經 2,858 天前——而且每月仍有 813,513 次下載。repo 層級檢查全都通過;但你實際安裝的 artifact 已經是八年前的版本。務必把 registry 的最新發佈日期和 commit log 分開看。

Archived repo 能解決問題嗎?GitHub 不是有橫幅嗎? 不一定,@modelcontextprotocol/server-puppeteer 就說明了原因。它的 npm repository 欄位是 null,package 頁面根本沒有連到 archived repo 的路徑,也沒有什麼橫幅可跳過。npm 真正會送達的是 package 的 deprecated 字串——「Package no longer supported」——安裝時直接印在終端機上,但 127,232 次/月 的安裝仍然照樣發生。browserbase/mcp-server-browserbase 則是在最後一次 commit 裡把停用公告寫得很清楚;它的 20,389 次安裝大多早於那個 commit,所以單看這數字很難得出什麼。

我要怎麼快速檢查自己的依賴? 先看 default branch 的 commit 日期、registry 版本/上傳日期、npm deprecation 欄位,以及 repo 是否 archived。接著再確認 package 與 repo 的對應關係,檢查活動是否轉到非 default branch,最後用 dependency graph 或 lockfile 判斷是不是間接暴露。像 Java 與 PyPI 這種彼此無關卻同名的 wayback 專案,就更需要這種升級判斷。

Ke
Ke
Thunderbit 技術長|資深資料科學家與機器學習專家 Ke Shen 在機器學習與資料科學領域擁有近十年經驗,畢業於哥倫比亞大學,曾任 Walmart Labs 資深資料科學家。他精通 Python、R、Java 與統計學,且具備深受同儕認可的深厚專業,分享如何將複雜的 AI 演算法從理論落實到可投入生產的架構的實戰見解。
目錄
Thunderbit · AI 網頁資料代理

1 次點擊 內擷取任何頁面的資料

獲 250,000+ 用戶信賴
提供免費方案
從網頁到試算表
描述你需要什麼——Thunderbit 的 AI Agent 會幫你抓取並匯出到 Excel、Google Sheets、Airtable 或 Notion。可免費開始。
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week