Forum:中文Minecraft Wiki第一个离线版本的诞生
如何才能让网络不佳的用户访问MCW?在openZIM的消息传到茶馆之后,我就一直希望能借助这项技术解决这个问题。我在openZIM Wiki上找到了Build your ZIM file这篇教程。
方法有很多,但大部分都不能用。于是我开始了探索……
第一次报错
由于我的电脑上没有Linux系统,只有一个学习用的Linux虚拟机,所以我最初的设想是在我的平板上运行Termux。
许多软件不是停止维护了就是使用了奇奇怪怪的构建工具,最终我选定了MWoffliner这个软件,它目前仍在积极维护。后来我才知道,openZIM官方使用的软件正是MWoffliner。
MWoffliner支持通过npm或Docker安装,但是一来我没有Docker,二来Docker镜像已经被删除了[注 1]。于是我在Termux上安装了Node.js,然后安装了MWoffliner:
pkg install nodejs
mkdir mwoffliner
cd mwoffliner/
npm install mwoffliner
很快便得到了报错:
gyp ERR! UNCAUGHT EXCEPTION gyp ERR! stack TypeError: Cannot assign to read only property 'cflags' of object '#<Object>' gyp ERR! stack at createConfigFile (/data/data/com.termux/files/home/mwoffliner/node_modules/node-gyp/lib/configure.js:118:21) gyp ERR! stack at /data/data/com.termux/files/home/mwoffliner/node_modules/node-gyp/lib/configure.js:85:9 gyp ERR! stack at /data/data/com.termux/files/home/mwoffliner/node_modules/node-gyp/node_modules/mkdirp/index.js:30:20 gyp ERR! stack at FSReqCallback.oncomplete (node:fs:188:23) gyp ERR! System Linux 4.19.113-perf-g954cdddf4c24 gyp ERR! command "/data/data/com.termux/files/usr/bin/node" "/data/data/com.termux/files/home/mwoffliner/node_modules/.bin/node-gyp" "rebuild" "-v" gyp ERR! cwd /data/data/com.termux/files/home/mwoffliner/node_modules/@openzim/libzim gyp ERR! node -v v20.2.0 gyp ERR! node-gyp -v v6.1.0 gyp ERR! This is a bug in `node-gyp`. gyp ERR! Try to update node-gyp and file an Issue if it does not help: gyp ERR! <https://github.com/nodejs/node-gyp/issues> npm ERR! code 7 npm ERR! path /data/data/com.termux/files/home/mwoffliner/node_modules/@openzim/libzim npm ERR! command failed npm ERR! command sh -c npm run download && node-gyp rebuild -v && npm run bundle
第二次报错
看来平板上是不适合跑了……11月30日(四)的晚上,我启动了电脑上的Debian虚拟机,又开始了尝试:
sudo apt install nodejs npm
mkdir mwoffliner
cd mwoffliner/
npm install mwoffliner
于是又一次得到了报错:
npm ERR! code 1 npm ERR! path /home/co-eda/mwoffliner2/node_modules/sharp npm ERR! command failed npm ERR! command sh -c (node install/libvips && node install/dll-copy && prebuild-install) || (node install/can-compile && node-gyp rebuild && node install/dll-copy) npm ERR! sharp: Please see https://sharp.pixelplumbing.com/install for required dependencies npm ERR! sharp: Installation error: Expected Node.js version >=14.15.0 but found 12.22.12
第三次报错
报错信息中已经说了:Node的版本太低了。于是我又上网查找如何升版本,很快便找到了官方仓库中的安装说明。只需要敲几条命令,就能导入证书,添加源,并安装最新版了。[注 2]安装好Node 20之后,我们重新运行最后一条命令:
npm install mwoffliner
滚动的进度条变成了六个点[注 3],看起来非常现代。但是报错还是一如既往地来了:
npm ERR! gyp ERR! UNCAUGHT EXCEPTION npm ERR! gyp ERR! stack TypeError: Cannot assign to read only property 'cflags' of object '#<Object>' npm ERR! gyp ERR! stack at createConfigFile (/home/co-eda/mwoffliner2/node_modules/node-gyp/lib/configure.js:118:21) npm ERR! gyp ERR! stack at /home/co-eda/mwoffliner2/node_modules/node-gyp/lib/configure.js:85:9 npm ERR! gyp ERR! stack at /home/co-eda/mwoffliner2/node_modules/node-gyp/node_modules/mkdirp/index.js:30:20 npm ERR! gyp ERR! stack at FSReqCallback.oncomplete (node:fs:189:23) npm ERR! gyp ERR! System Linux 5.10.0-8-amd64 npm ERR! gyp ERR! command "/usr/bin/node" "/home/co-eda/mwoffliner2/node_modules/.bin/node-gyp" "rebuild" "-v" npm ERR! gyp ERR! cwd /home/co-eda/mwoffliner2/node_modules/@openzim/libzim npm ERR! gyp ERR! node -v v20.10.0 npm ERR! gyp ERR! node-gyp -v v6.1.0 npm ERR! gyp ERR! This is a bug in `node-gyp`. npm ERR! gyp ERR! Try to update node-gyp and file an Issue if it does not help: npm ERR! gyp ERR! <https://github.com/nodejs/node-gyp/issues>
很好,看来又回到老路上了。
第四次报错
这条评论说降级到Node 18就能解决问题。让我试试。
卸载Node.js,重走第二、三步。只不过这次把set NODE_MAJOR 20
(我用的是Fish,不支持NODE_MAJOR=20
这种语法)改成set NODE_MAJOR 18
。
再次安装,再次失败。
npm ERR! gyp ERR! build error npm ERR! gyp ERR! stack Error: `make` failed with exit code: 2 npm ERR! gyp ERR! stack at ChildProcess.onExit (/home/co-eda/mwoffliner2/node_modules/node-gyp/lib/build.js:194:23) npm ERR! gyp ERR! stack at ChildProcess.emit (node:events:517:28) npm ERR! gyp ERR! stack at ChildProcess._handle.onexit (node:internal/child_process:292:12) npm ERR! gyp ERR! System Linux 5.10.0-8-amd64 npm ERR! gyp ERR! command "/usr/bin/node" "/home/co-eda/mwoffliner2/node_modules/.bin/node-gyp" "rebuild" "-v" npm ERR! gyp ERR! cwd /home/co-eda/mwoffliner2/node_modules/@openzim/libzim npm ERR! gyp ERR! node -v v18.19.0 npm ERR! gyp ERR! node-gyp -v v6.1.0 npm ERR! gyp ERR! not ok
第五次报错
至少报错信息不一样了,这说明我们有进步⸺我这样安慰着自己。
这条评论说安装libvips-dev
就能解决问题。让我试试。
sudo apt install libvips-dev
但还是报错了。[注 4]
第六次失败
上述评论的下方又有一条评论,这次是需要安装Yarn并为依赖手动指定版本。
安装成功了!我为mwoffliner编写了脚本,并认真填入了每一项参数。脚本命名为dump.sh
,内容如下:
#!/bin/bash
yarn run mwoffliner \
--mwUrl=https://zh.minecraft.wiki/ \
--adminEmail=[email protected] \
--articleList=article_list.txt \
--mwWikiPath=/w/ \
--mwApiPath=/api.php \
--mwRestApiPath=/rest.php/v1 \
--mwModulePath=/load.php \
--verbose=info \
2>&1| tee output.txt
其中的article_list.txt
包含我精心挑选的几个页面,包括首页、迁移通知、(当时的)最新版,以及旧站热度排名前三的页面(可在旧站的页面底部看到):
Minecraft Wiki 交易 附魔 村民 Minecraft Wiki:迁移通知 Java版1.20.3-rc1
运行脚本(./dump.sh
),开始报错:
[error] [2024-02-12T09:40:27.326Z] Redis Client Error Error: connect ECONNREFUSED 127.0.0.1:6379 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1555:16) { errno: -111, code: 'ECONNREFUSED', syscall: 'connect', address: '127.0.0.1', port: 6379 } error Command failed with exit code 3.
第七次失败
这是因为我没有安装Redis!README上已经说了,要自行安装Redis。
sudo apt install redis-server
[error] [2024-02-12T09:57:06.432Z]
**********
Unable to find appropriate API end-point to retrieve article HTML
**********
error Command failed with exit code 2.
成功!
最后一次debug显得尤为艰难,因为我必须深挖源代码。
我打开了node_modules/mwoffliner/lib
,定位到报错信息产生的位置,并在其周围插入了许多调试语句。经过一些努力,我终于找到了问题所在(Downloader.js
):
async checkCapabilities(testArticleId = 'MediaWiki:Sidebar') {
// By default check all API's responses and set the capabilities
// accordingly. We need to set a default page (always there because
// installed per default) to request the REST API, otherwise it would
// fail the check.
logger.log(`checkCapabilities(${testArticleId})...`);
testArticleId = 'MediaWiki:Sidebar';
this.mwCapabilities.mobileRestApiAvailable = await this.checkApiAvailabilty(this.mw.getMobileRestApiArticleUrl(testArticleId));
this.mwCapabilities.desktopRestApiAvailable = await this.checkApiAvailabilty(this.mw.getDesktopRestApiArticleUrl(testArticleId));
this.mwCapabilities.veApiAvailable = await this.checkApiAvailabilty(this.mw.getVeApiArticleUrl(testArticleId));
this.mwCapabilities.apiAvailable = await this.checkApiAvailabilty(this.mw.apiUrl.href);
// Coordinate fetching
const reqOpts = objToQueryString({
...this.getArticleQueryOpts(),
});
const resp = await this.getJSON(`${this.mw.apiUrl.href}${reqOpts}`);
const isCoordinateWarning = resp.warnings && resp.warnings.query && (resp.warnings.query['*'] || '').includes('coordinates');
if (isCoordinateWarning) {
logger.info('Coordinates not available on this wiki');
this.mwCapabilities.coordinatesAvailable = false;
}
}
这个函数默认通过MediaWiki:Sidebar
来检查各个API是否可用。但是,这个函数在被调用的时候传入的总是首页名称,而获取首页名称的逻辑不适用于中文MCW。解决方法是在函数开始将页面强制更改为MediaWiki:Sidebar
(上述代码高亮部分)。
我将生成的ZIM文件和生成用到的其他一些文件打包成mcwzh-testdump-20231201.zip
,发到了茶馆群里,希望群友能帮我测试一下。但是没有人理我
成功的捷径
我在官方仓库里提的存档请求时隔一个月都没有任何回复。我在1月5日催询问了一下进展,很快就有人创建了配方(recipe)。
创建配方的时候,参数写得漏洞百出。我每次帮他们改,都要等一周才能回复,后来索性不回我了(不知道是不是消息太多了没看见)。看到他们在用Docker,我就想着自己搭建一个Docker环境。
Docker
我简单学习了一下Docker官网上的教程,并按照Docker官网上的教程在虚拟机上安装了Docker。之后,我拷贝了配方中使用的参数,进行修改后制成了如下的脚本:
#!/bin/bash
# Usage: sudo ./run.sh
# For docker:
# Added: --rm
# Modified: -v
# Removed: --detach, --cpu-shares, --memory-swappiness, --memory
# For mwoffliner:
# Modified: --adminEmail, --customZimDescription
# Removed: --optimisationCacheUrl, --osTmpDir
docker run \
-v /home/co-eda/mwoffliner-docker/output:/output:rw \
--name mwoffliner_minecraftwiki_zh_all \
--rm \
ghcr.io/openzim/mwoffliner:1.13.0 \
mwoffliner \
--adminEmail="[email protected]" \
--customZimDescription="Docker test" \
--customZimFavicon="https://zh.minecraft.wiki/images/Wiki2x.png" \
--customZimLanguage="zho" \
--customZimTitle="Minecraft Wiki (zh)" \
--format="novid:maxi" \
--mwApiPath="/api.php" \
--mwUrl="https://zh.minecraft.wiki/" \
--outputDirectory="/output" \
--publisher="openZIM" \
--webp
之后就可以不断运行并根据报错调整参数了。
直到那个棘手问题的重现:
Unable to find appropriate API end-point to retrieve article HTML
Git
Docker是封闭环境,这意味着我无法动源代码一分一毫。这样就回到原点了⸺还是得亲自构建。但是与上次不同的是,安装方式并非在空文件夹中执行npm install mwoffliner
,而是克隆仓库后直接npm install
。
npm ERR! code 1 npm ERR! path /home/co-eda/mwoffliner-git/mwoffliner/node_modules/jpegtran-bin npm ERR! command failed npm ERR! command sh -c node lib/install.js npm ERR! ⚠ connect ECONNREFUSED 0.0.0.0:443 npm ERR! ⚠ jpegtran pre-build test failed npm ERR! ℹ compiling from source npm ERR! ✖ Error: Command failed: /bin/sh -c ./configure --disable-shared --prefix="/home/co-eda/mwoffliner-git/mwoffliner/node_modules/jpegtran-bin/vendor" --bindir="/home/co-eda/mwoffliner-git/mwoffliner/node_modules/jpegtran-bin/vendor" npm ERR! configure: error: no nasm (Netwide Assembler) found
根据GitHub issue可知,安装 nasm 即可解决问题。
最初我是在v1.13.0
这一分支上进行测试。后来发现源代码问题无法通过调节参数来解决,于是一位叫kelson42的贡献者改用git main运行配方。于是我在本地将分支切换到了main
,并重新运行npm install
。出现如下报错:
node_modules/@types/imagemin-gifsicle/index.d.ts:7:24 - error TS1479: The current file is a CommonJS module whose imports will produce 'require' calls; however, the referenced file is an ECMAScript module and cannot be imported with 'require'. Consider writing a dynamic 'import("imagemin")' call instead. 7 import { Plugin } from 'imagemin'; 2024年3月21日 (四) 03:33 (UTC)2024年3月21日 (四) 03:33 (UTC) node_modules/@types/imagemin-webp/index.d.ts:7:24 - error TS1479: The current file is a CommonJS module whose imports will produce 'require' calls; however, the referenced file is an ECMAScript module and cannot be imported with 'require'. Consider writing a dynamic 'import("imagemin")' call instead. 7 import { Plugin } from 'imagemin'; 2024年3月21日 (四) 03:33 (UTC)2024年3月21日 (四) 03:33 (UTC) Found 2 errors in 2 files. Errors Files 1 node_modules/@types/imagemin-gifsicle/index.d.ts:7 1 node_modules/@types/imagemin-webp/index.d.ts:7 Build Complete at [2024年 03月 07日 星期四 10:54:42 CST]
解决方法很离谱,就是在那两行代码上方添加忽略报错的命令。
+// @ts-expect-error
import { Plugin } from 'imagemin';
后记
这篇文章是我从寒假开始写的。也不知道为什么要把这些乱七八糟的debug历程写出来,或许只是为了好玩。于是就这样断断续续写了很久,花了不知道多少时间,到今天终于完稿了。
那么为什么debug这么久呢?一方面当然是因为我是个小白,不知道可以克隆源代码再自己构建,也不会用Docker搭建构建好的镜像文件,于是花了好大的劲去安装官方提供的npm包,浪费了很多时间。另一方面也该怪openZIM这个组织,做出来的程序漏洞百出,安装也容易失败。可是当我看到他们时间精力不足,issue挤压多、处理慢,实在感到无奈。
目前经过我的努力,已经找到了修复配方的方法。接下来就可以开始爬取整个网站了。但是这个过程将花费约5个小时,所以只能在openZIM的机器上跑了。kelson42目前还没有回复我,不知道什么时候才能看到中文MCW的第一个完整离线版本。希望他们不要让我失望。
注释
TripleCamera(留言) 2024年3月21日 (四) 03:33 (UTC)