习近平总书记是我党继毛泽东同志之后的又一划时代伟人,如果讲毛主席是近代中国的伟大舵手1. 0 ,那么习近平总书记则就是当代中国的伟大舵手2. 0。
这充分验证了习近平总书记与生具备的天降伟才
這是加速主義嗎?
习近平总书记是我党继毛泽东同志之后的又一划时代伟人,如果讲毛主席是近代中国的伟大舵手1. 0 ,那么习近平总书记则就是当代中国的伟大舵手2. 0。
这充分验证了习近平总书记与生具备的天降伟才
這是加速主義嗎?
@白脸角鸮 #101680 這實際上是 youBBS 的默認配色 (2049bbs 是基於 youBBS 二次開發,前期沒有改樣式)
@thphd 可以為 code block 加上代碼高亮嗎?
大家好,我是 习猪习 a.k.a. 新品蔥 和 Github 上的 PincongBot。有論壇用戶邀請我搶救性備份 Qdaily 好奇心日報,因此就趁這個機會,淺談一下我一直在做的網站備份,同時手把手地教大家寫一點代碼。
PincongBot 這個名字來自於小二使用的 TerminusBot
我寫這篇文章的原意是作為自己的日常筆記,但是希望這能夠成為為 2047 論壇引流的優質內容。
我先假設讀者們不懂技術,因此我會嘗試使用儘可能直白的語言和代碼為大家解釋。
和一些人想象的不同,我在備份網站時優先考慮使用的不是爬蟲,而是會尋找是否有 API 可供使用,因為 API 往往可以提供比網頁更多的信息。
以 http://www.qdaily.com/articles/65148.html 這一個頁面為例。按 開啟開發者工具中的網路監視器,點選工具條中的 XHR ,以過濾出 XHR 請求。
將頁面下拉以加載 lazy-loading 的剩餘內容,可以看到幾個 XHR 請求,其中看起來有用的是 http://www.qdaily.com/comments/article/65148/0.json。其中 65148 可以知道是 article id ,0 的意義目前還未知。
後面知道這個是
:key分頁索引 時間戳,第一頁為 0 ,下一頁的索引為返回結果中的 last_key 字段
http://www.qdaily.com/homes/articlemore/:key.json
http://www.qdaily.com/tags/tagmore/:tag_id/:key.json
http://www.qdaily.com/comments/paper/:paper_id/:key.json
http://www.qdaily.com/labs/papermore/:key.json
吐槽一下,都 2020 年了還沒有部署 HTTPS
通過解包 Android apk 檔案(如何解包 apk 就是另外一個話題了,挖坑待填),檢索代碼中的關鍵字,如 api,真的有。
前面都要加上 http://app3.qdaily.com/
app3/articles/detail/%s.json
app3/articles/info/%s.json
app3/authors/index/%s/%s.json
app3/boot_advertisements.json
app3/categories/index/%s/%s.json
app3/column_ads/info/%s.json
app3/columns/all_columns_index/%s.json
app3/columns/article_in_all_columns/%s.json
app3/columns/index/%s/%s.json
app3/columns/info/%s.json
app3/comments/create_comment
app3/devices/android
app3/feedbacks
app3/homes/index/%s.json
app3/homes/left_sidebar.json
app3/options/mob_create_option
app3/options/mob_create_praise
app3/paper/choices
app3/paper/choices/%s
app3/paper/choices/result/%s
app3/paper/tot_results/%s
app3/paper/tots
app3/paper/tots/%s
app3/paper/whos
app3/paper/whos/%s
app3/paper/whos/result/%s
app3/papers/detail/%s.json
app3/papers/done
app3/papers/index/%s.json
app3/praises/create_praise
app3/radars/index/%s/%s.json
app3/read_the_statistics
app3/reads
app3/searches/post_list
app3/shares
app3/subscribes/create_subscribe
app3/subscribes/remove_subscribe
app3/tags/index/%s/%s.json
app3/user_feedbacks
app3/users/center
app3/users/comment_on_my
app3/users/find_password
app3/users/my_comments
app3/users/my_praises
app3/users/my_subscription
app3/users/paper_detail?paper_id=%s
app3/users/papers
app3/users/praise_on_my
app3/users/praises
app3/users/profiles/message_number.json?uuid=%s
app3/users/radar
app3/users/scan_history
app3/users/setting
app3/users/sync_to_phone_praises
app3/users/sync_to_server_praises
app3/users/system_message_on_my
app3/users/tourist_praises
app3/users/update_my_personal_information
嘗試其中一個,如 http://app3.qdaily.com/app3/authors/index/589058/0.json 。WTF! 這就把行動端的 API 全部暴露給我們了。
這就是在生產環境將環境變量 (在這裡是
PASSENGER_APP_ENV) 設為development忘記改回來的後果。在生產環境中開發調試是一個不好的習慣。
暴露的 API 中能找到一些對我們有用的。
準備
pip3 install requests
main.py 文件main.py 文件代碼中使用到的 request_json, format_user_info, format_category_info, format_article_info, format_comment_info, format_child_comment, write_json 等函數,需要根據需求和實際情況自行實現
使用的 API 為 /wxapp/articles/info/:id
為什麼要用這個而不是
/app3/articles/info/:id? 因為它一次性提供的信息最全
定義 URL 模板
BASE_URL = "http://app3.qdaily.com"
ARTICLE_URL_TPL = BASE_URL + "/wxapp/articles/info/{id}"
定義函數 backup_article, 參數為 article_id
def backup_article(article_id):
# 用實際的 article id 替換 URL 模板中的 `:id`
url = ARTICLE_URL_TPL.format(id=article_id)
# API 請求
json = request_json(url)
if json is None:
return
post = json['response']['post']
# 格式化作者信息
author = format_user_info(json['response']['author'])
# 格式化分類信息
category = format_category_info(post['category'])
# 格式化文章數據,只保留我們想要的數據
article = format_article_info(post, author['id'])
###
# 寫入文章數據
###
write_json(article)
###
# 寫入作者信息
###
write_json(author)
###
# 寫入分類信息
###
write_json(category)
恰巧文章的 id 是遞增的整數。我們用暴力方法,備份所有文章(因為一些文章根本就不會出現在文章索引中)
我們需要從 article id 為 1 開始嘗試備份,一直到目前最新的 65270
因此寫一個 for range 循環
for article_id in range(1, 65270+1):
backup_article(article_id)
使用的 API 為 /wxapp/comments/index/:comment_type/:id/:last_key,其中 :last_key 是分頁索引,第一頁為 0 ,下一頁的索引為返回結果中的 last_key 字段
定義 URL 模板
COMMENT_URL_TPL = BASE_URL + "/wxapp/comments/index/{comment_type}/{id}/{last_key}"
定義函數 backup_comments, 參數為 parent (backup_article 中的 article 文章數據)
def backup_comments(parent):
last_key = 0 # 分頁索引第一頁為 0
# 不斷備份下一頁,直到跳出循環
while True:
url = COMMENT_URL_TPL.format(
comment_type=parent['type'], id=parent['id'],
last_key=last_key
)
json = request_json(url)
comments = json['response']['comments']
# 處理這一頁上的每一個評論
for c in comments:
# 格式化作者信息
author = format_user_info(c['author'])
# 格式化評論數據
comment = format_comment_info(c)
# 寫入作者信息
write_json(author)
# 寫入評論數據
write_json(comment)
# 備份子評論
for cc in c['child_comments']:
# 寫入子評論數據
write_json(format_child_comment(cc))
# 寫入子評論的作者信息
write_json(format_user_info(cc['author']))
# 有更多評論 (分頁)
if json['response']['has_more']:
# 下一頁的分頁索引為返回結果中的 last_key 字段
last_key = int(json['response']['last_key'])
# 備份下一頁
continue
else:
# 沒有更多頁了
# 跳出循環
break
在 backup_article 函數的最後加上
if article['comment_count'] > 0:
backup_comments(article)
由於圖片都儲存在好奇心日報主站的服務器上,如果主站挂了,圖片就全部丟失了。
這是小二曾提出的問題
其中一種解決方案是,在格式化文章數據時,使用 正則表達式 查找正文中的圖片,非阻塞地下載並保存
TODO
備份的成果在 https://github.com/PincongBot/qdaily ,欢迎 star 和 fork 。
我認為的國殤日是4月23日首都南京淪陷
等站長配置一下 markdown parser,設置將換行 \n 轉換為 HTML 的 <br> 就可以了
像 python-markdown2 就有 break-on-newline 設置
2049bbs 早期(2019-10-21 前)是提供精确到分钟的注册和发帖时间的,备份了下来。之后的注册时间就属于备份时带的私货,因为每15/20分钟整站备份一次,所以可以精确到15/20分钟之内
哇,欣賞你的高效率
既然論壇中有用戶已經知道了,這件事没有什么值得隐瞒的,我是 2049bbs.github.io 的备份者。
首先感谢站长 @thphd 能够利用好我的备份数据,恢复论坛。
如果不是有 2049bbs 的近乎完整的公開備份數據,加上站長的無私努力,很難想象能否在 2049bbs 关闭後,在這裡再次與大家相見。
2018年9月牆外樓关闭,10月30日,品葱爆破。一周多后的11月8日,旧膜乎 (mohu.club) 关闭了。2019年1月香港人站長的奇闻录也迫於壓力关站(不知多少人還記得這幾個網站)。很可惜,大部分數據沒能留存下來,煙消雲散了,只能从网页快照中恢复一些断垣残壁,心痛。所以對於任何一個抵抗者網站來說,在網站還正常運行的時候,就嘗試去備份它!能夠用及时的数据恢复完整内容,非常重要。
我非常希望站長 @thphd ,@NodeBE4 ,@沉默的广场,@Resistance ,我 ,還有任何有能力/有技術的同僚,可以一起合作完成一項有意义的开源项目。
To @九頭鳥,@NodeBE4,我自己,和所有不是共匪國安網警的用戶
無論是誰,有著共同目標的大家請間放下一切的猜疑、指責、内斗,坦诚相见。 我们共同的敌人是共匪的專制暴政,而抵抗者们原子化、散沙化是共匪乐意看见的。
@thphd #9265139 用 whitelist 取代现在的 blacklist 就不用特别挑出不希望备份的新 collection 了
專制政權一切的統治基礎在於謊言,只要謊言被世人識破,專制政權也就再也無法取得普遍民眾和武裝力量的積極支持
@20202047 #13818145 setTimeout的 call signature 是 (callback, ms, ...args),所以應該是
setTimeout(() => document.getElementById('editor_btnsubmit').click(), 1000000)
字很好看,有特别练过吗?
喜欢神隐少女
请问 https://github.com/thphd/2047/blob/f927c741e4ddbadd0544c1e3252d880640f5560d/make_archive_backup_release.py#L20 的 logs 这个 collection/table 有什么内容?找不到相关代码,被移除了?
@thphd #13755187 既然 2047 继承了 2049bbs,尊重 2049bbs 的传统,就不该为了追求流量,做个性化推荐
tor 上爬虫对于服务器的实际影响有多少(如果还远不到 DoS 的级别),为什么站长要费心防爬?
「我國萬事不進步,而獨防民之術乃突過於先進國,此真可為痛哭也。」
如果我是爬虫者,如何确保数据库备份的质量和频率稳定?
隔壁新品葱自从小二出事后,https://gitlab.com/pin-cong/data 的每周数据备份就没了
处理用户数据的理念不同,站长请见谅
既然备份都可以下载,那么站长将之前的备份都删除,只留最新版本的意义是什么? 如果是出于删帖隐私保护的考量,匿名网站做好隐私保护应该是用户自己的事
比起下流事做尽的共匪,我认为正常人更需要隱藏自己,不过爬虫完全可以用免费的 CI 服务
为什么爬站就一定是匪共的人,站长不就是靠别人爬虫的数据恢复了 2049bbs 的大部分内容的吗?
支持在五道口公司内建立党的领导
支持站长依法建站
以网站理念为中心才能尽可能避免出现人治
@BE4 现在在 2047 吗?
2049bbs像是超大型集市中的一个小茶馆。用户非常成熟独立,大部分时候在各自领域活动,偶尔来分享一下新发现和有趣的资源,但对2049bbs并无平台依赖。2049bbs则扮演一个精华资源的集散地,帮助这个小茶馆的过客拓展在整个互联网大集市中的视野,让用户更独立更成熟。这样一种完全开放、个人主义的建站思想我个人是非常推崇的。
某些论坛那样创造封闭空间、传销式拉人头,霸占用户的信息获取和交流带宽,目的是让用户对平台产生依赖,最终让用户群变成观点类似的封闭社区。这种遵循集体主义思维创立的论坛,我本身就无比反感,至于其在封闭体系下放纵职业五毛对公民发动战争的行为,更是让人感到生理不适了。
每次网站 5xx 都会担心人出事。小二的事已经过去五个月了