這實際上是兩個問題,第一個問題是如何導出自己的朋友圈數據,第二個問題是如何把這些數據上鏈永存。先說成果,最終我成功地在 ios 系統中從 8.0.32 版本的微信裡導出了自己的朋友圈數據並存儲在了 Crossbell 區塊鏈上:https://xfeed.app/u/wxd6bb23a9
我研究這件事的原因是在 2023.2.4 我的微信被封之後,我想要把自己的朋友圈內容導出來並重新展示。期間我搜索過不少資料,但是都是版本比較早的解決方法了。所以我覺得現在應該把自己的探索,和踩過的坑也記錄下來。
提前聲明:
- 我沒有導出評論數據。因為我覺得沒必要,畢竟上鏈的一部分意義在於確認所有權,在這件事上幫別人把內容上鏈意義不大。但是如果真的有需求實現起來應該也不會太麻煩,我看到 MyWC_Message01 這張表裡有明文存儲的評論,但不確定是否是完整的,如果有需求可以對照著本篇教程自己繼續研究。
- 我沒有導出別人的朋友圈數據,同理沒必要。但不難推測如果需要導出的话應該從哪張表入手。
- 我沒有導出微信好友 / 聊天記錄。我猜這可能是一個常見的需求,但這方面我需求不大所以沒有研究這個,不過大概率就是換張表導數據,不會很麻煩的。
- 我沒有解析出視頻號的分享。普通的鏈接分享是可以解析出來的,但是視頻號實在過於迷霧重重,難以恢復出真實視頻鏈接,而且我也很少轉發視頻號,沒有太多解析這個的需求。
明確目標:導出朋友圈數據#
假定現在的需求就是導出朋友圈數據,那麼其實也分好幾種情況,不同情況有不同的方法:
- 如果你是 WeChat User(而非微信用戶),那麼官方是提供導出數據接口的,可以參考之前的這篇 Blog。如果也想上鏈展示的話順帶可以看這篇 Blog。
- 如果是微信用戶
- 如果你的微信沒有被封,可以試試淘寶搜索 “微信朋友圈”,有很多把你的微信朋友圈導出做成電子書之類的服務(其實我還挺好奇淘寶商家是怎麼做的,不知道是不是也是通過緩存)
- 如果你的微信也和我一樣被封了,或者雖然沒有被封但是也對怎麼導出數據很感興趣,也可以試試接下來我會重點介紹的從手機緩存中恢復數據的這種方法。這個方法可行的原因是雖然微信賬號被封了,但是自己還是可以訪問自己的朋友圈的(萬幸)。
緩存恢復數據法#
顧名思義,此思路的方法是先確保微信本地已經把自己的朋友圈緩存了,然後把自己手機的數據導出,最後從導出的數據中找到相關的文件,然後從相關文件中提取出有用信息,如發佈時間,朋友圈內容等,最後拼湊還原出完整的朋友圈數據。
1. 本地緩存#
打開微信,清空一下緩存(此步非必須,但是可以減少備份和拷貝所需的等待時間),然後打開自己的朋友圈,往下翻到最早的一條,將自己所有的朋友圈緩存到本地,每張圖也都需要打開,不然緩存的只有縮略圖。為了確保全部緩存成功,可以翻頁完成之後斷網確認是否還能看到,能看到意味著已經緩存成功。
2. 導出緩存文件#
因為我的微信是在 ios 系統上登錄的,被封號之後我不確定是否還能在別的設備上登錄了(擔心夜長夢多,如果嘗試次數太多本身能登錄的 ios 設備也登不上去了就得不償失了),所以只能通過手機備份的方式導出緩存。安卓系統應該可以直接導出緩存文件,但是 ios 的機制是不能直接訪問 App 自己的緩存文件的,所以必須通過手機整體備份的方式。
我使用的工具是 iMazing,免費版的就夠。首先備份手機數據,然後找到微信的 Documents 文件,導出即可,步驟如下圖。免費版 iMazing 有 10 次導出機會。
在 Documents 文件夾裡,存在著至少一個以 Hash 字符串命名的文件夾,像這樣的
eb8a6093b56e2f1c27fbf471ee97c7f9
這樣的文件夾中就存放著微信用戶的個人數據。如果在這個手機上登錄過多個微信,則可能存在多個這樣的 Hash 命名的文件夾,如果不確定哪個是自己想要導出的,可以都導出看看。
找到 ./Documents/{hash}/wc/wc005_008.db 和 ./Documents/{hash}/DB/WCDB_Contact.sqlite,這兩個就是需要解析的緩存文件了。前者是和朋友圈數據相關的表格,後者是和好友數據相關的表,這裡我們需要這張表只是為了解析出自己賬戶的頭像。
(踩過的坑:新版本的 Mac 不能通過 iTunes 備份了)
3. 解析緩存#
TL;DR 下載這個 repo,把 wc005_008.db
和 WCDB_Contact.sqlite
拖到根目錄,修改 main.py
裡的 hash 為你自己的 hash,然後運行 python3 main.py
即可導出一份 moments.json。
關於腳本還需要特別說明的是:
-
我在腳本中我設了一個 dl_img 的參數,如果為 True 的話會把所有圖片都下載到本地。畢竟微信號都已經被封了,誰知道朋友圈的圖片會被 host 到什麼時候,況且如果頻繁的站外訪問朋友圈圖片誰知道會發生什麼,我建議趁還能下載的時候還是把圖片都下載到本地比較保險。
-
對於分享鏈接類的 moment,我不僅解析了分享的鏈接本身,還解析了微信本身對這個鏈接緩存的圖片 / 標題 / 簡介,完全還原了朋友圈如何對一個鏈接的渲染。這樣做是因為曾經分享的很多鏈接已經 404 了...... 如果只解析鏈接意義不大了,我覺得有必要把當時緩存的數據都解析出來,至少還原出 “封皮”。
當然這個 repo 裡的腳本之所以這樣編寫背後有很多的分析,我會簡要介紹一遍,大家可以依照自己的興趣選擇是否跳過本節接下來的部分。
首先微信使用的是 SQLlite 緩存,想要分析 wc005_008.db 這個數據庫的話可以使用這款開源的 sqlite browser。經過簡單分析,發現 db 裡有大量 MyWC01_
開頭的表,自己賬戶的朋友圈數據都存在 MyWC01_{$hash}
這張表裡,$hash 還是剛才目錄的那個 hash,這個 hash 應該是代表自己賬戶的某種 id,推測其他的 MyWC01_...
代表的是好友的朋友圈的數據。
進入存儲自己朋友圈數據的表,發現有兩個字段非常重要,Buffer
和 id
。如果以 utf-8 方式解碼 Buffer 字段,可以看到有很多明文字段,有的是圖片 url,有的是好友的名稱,有的是之前發過的朋友圈內容。進而推測出從 Buffer 字段中我們可以恢復出朋友圈數據。
這裡稍微先偏離一下主線,說一下 “以 utf-8 方式解碼 Buffer 字段” 這件事。我並沒有在這款 sqlite browser 中看到有辦法可以使用 utf8 方式解碼二進制文件然後直接閱讀,最後我的做法是把這個表中所有 Buffer 字段都寫入文件,然後使用 hex viewer/editor 來閱讀分析。不過這其實也不順利,我找了很多 hex viewer/editor,都不支持 utf8 的解碼方式,最後發現最好用的也是我發現的唯一支持 utf8 解析的軟件是 Synalyze It!,但是這個軟件僅有兩周的免費試用,之後需要支付 9.99 美元。我不知道是否有更好的方法來分析,希望能夠和大家交流。
回到主線,我們繼續分析 Buffer 字段,發現很難完整地理解這些數據的格式,但是儘管如此,我們完全可以根據一些固定的標識位識別出內容本身。我們可以發現一個典型的 payload 大概是這樣的:
不同的字段都有對應的標識 Flag,如圖片 / 內容 / 分享的鏈接等,這些字段在 Buffer 中表現的格式基本都是先出現 Flag,緊跟其後的一到兩個字節是標記 Message 長度的,再之後是 Message 本身。
以正文內容為例,下圖是兩條朋友圈內容的二進制文件,觀察後很容易發現,b'\xba\x01'
就是正文內容的標識符。
至此思路基本就清楚了,首先確認都有哪些字段需要解析(最終確認需要解析的字段有正文內容,圖片鏈接,分享鏈接,分享鏈接渲染出的圖片,分享鏈接渲染出的標題,分享鏈接渲染出的描述),然後識別出我們需要解析的字段的 flag,最後繼續想辦法解析 message 的長度和偏移量就好。
但是還差最後一塊拼圖 —— 發佈時間。直覺來講表格中的另外一個字段id
和時間非常相關,因為這些數字隨著實際時間一起遞增,所以猜測是某種基於時間戳的算法。這部分非常感謝 @kaii 的幫助,最後基本上確定了實際 create_time 和 id 的轉換算法是
create_time = id / 8388607990
這個公式中的 magic number 8388607990 由 MyWC_Message01 推斷而來。這是一張存儲評論的表,雖然我們沒有原內容的準確發佈時間,但是從這張表的 create time 字段我們可以得知評論的實際發佈時間。這張表中的另一個字段 id 對應的應該是原 post 的 id。
我們可以簡化的認為,每個 post 的第一個回復的 create time 和 id 之間的關係就是原 post 和 id 之間的關係,因為第一個回復的時間最接近原 post 實際的發佈時間。那麼我們可以直接取表中最大的 Id/CreateTime 作為我們的魔法系數。
SELECT MAX(ID/CreateTime) FROM MyWC_Message01
實際上通過評論得出的魔法系數還是略微小了一點點,為了更精確,最後我又抽樣幾條自己的朋友圈數據,對照微信 app 前端顯示的發佈時間,進行一些微調,最終得出了 8388607990 這個數字。基本上這個公式可以保證和實際發佈時間誤差都在 1 分鐘內。
總之大概思路就是如此了。當然具體還有很多的細節,如果有興趣可以直接參考代碼的實現。
(踩過的坑:最開始參考了這個 repo 的代碼很多,但是這個 repo 裡的代碼是按照 plist 格式解析 Buffer,而實際上現在版本的緩存不知道是什麼格式但反正不是 plist 格式)
4. 數據展示及鏈上永存#
既然現在數據已經導出了,其實就想幹什麼都可以了。我認為把數據上鏈是一個很浪漫的記錄方式,所以我選擇把朋友圈在 Crossbell 鏈上備份一遍,順便在重新在 xFeed 裡展示出來。
為了實現上鏈功能,以及方便調試觀察自己的數據是否正常導出,在倉庫裡除了導出腳本之外,我還寫了一個簡單的展示頁面。最後效果大致如圖:
如果數據的導出沒什麼問題的話,可以直接在頁面上點擊 “上鏈存儲”,跟著步驟進行即可。但是為了和區塊鏈順利交互,有一些準備工作需要做:
- 下載 Metamask 錢包 插件
- 在 水龍頭 領取交互需要的 gas。如果數據過多可能需要的 gas 數額較大,如果需要更多的 gas 也可以聯繫我。
這兩點準備好之後就可以在頁面中直接點擊上鏈了。
結語#
微信的版本一直在更新,緩存的結構也持續變化中,本篇內容肯定不可能完全通用,或者覆蓋所有的情況,但希望本篇內容能提供一些參考,給大家一些啟發。如果有其他的發現,歡迎一起交流。
最後再列一遍本文涉及的兩個倉庫:
- 從緩存中導出朋友圈數據:https://github.com/Atlasoin/wechat-moments-exporter
- 本地展示朋友圈數據及上鏈備份:https://github.com/Atlasoin/migrate-notes-2-csb
另外除了導出朋友圈之外,我也寫了一個導出 QQ 空間說說的油猴腳本(是的,因為我的 QQ 也一起被封了)。QQ 空間的導出要簡單很多,雖然內容已經導出在這,不過還沒整理完代碼,打算回頭也寫個簡單的教程。
參考#
感謝前人栽的樹: