大概是几十天前,我在查询 whois 时发现, SixHz.com 的前任主人已经不再续费了,但域名还没开放购买。于是把相关信息发给 AI,问了下大致的释放时间。从那之后,就会偶尔搜一下,看看能不能买到。

这个世界上,有些事情可遇不可求,而这一次我很幸运,今天终于蹲到了。

域名是在 Cloudflare 购买的,价格是 10.46 美元 / 年。

顺手查了下历史,这个域名之前被用作 Go 组件托管地址 sixhz.com/szamlazzhu,似乎和匈牙利的发票业务有关,还挺有意思的。

关键词:MIUI13;小米手机;红米手机;广电流量卡;广电卡;广电流量

最近办了张广电流量卡插入 MIUI13 中,可以打电话,但是开启数据流量没有网。

解决办法:设置中搜索 APN,点击 cbnet,下滑找到 MNC 改为 15,其他不动,退出并保存。

想到一个问题:如果有一天我不再更新博客,甚至不再使用原本的服务器,这些博客内容会怎样?经过实测,如果网站不能访问,搜索引擎会在短时间内检测到并“下架”。而我想让它们继续被看见,让它们独立于服务器依赖而存在。

于是就有了这个小项目:一个用 Python + wget 做的极简博客镜像工具,可以把你的 Typecho(或其他兼容的博客)抓成一套完整的、可直接部署的静态网站。

✨ 它能做什么?

  • 一个用 uv 管理的 Python 小工具,用 wget 把 博客 抓成可直接部署的静态站点。
  • 典型用法:把 Typecho 网站静态页面部分爬虫,其他网站自行测试。

    • 只抓公开页面(文章、分页、分类、归档、作者页、主题资源等),排除后台登录、新增评论等功能。
    • 输出目录为 ./site/,可以直接推送到 Cloudflare Pages / GitHub Pages。
    • 抓取后会重写所有站内链接为本地相对路径,确保离线可用。
    • 针对 img src图片类资源也会保存并重写为本地相对链接,确保离线可用。
  • 每次镜像前都会先 --spider 探活;镜像在 site_tmp/ 完成后替换 site/,探活或下载失败都会保留旧的 site/ 不被清空。
  • 本项目由 AI 10分钟完成,又压力 AI 改细节。

换句话说:你的博客在任何地方都能继续「活着」。

🛠️ 怎么用?

  • 环境需求:Python 3.9+,推荐用 uv 管理项目。
  • 需本地安装 wget.exe
    添加环境变量;也可以将 wget.exe 放在同文件夹下tools/mingw64/bin/
  • 先探活,再在空的 site_tmp/ 抓取,成功后替换 site/
    uv run python mirror.py
  • 先探活,再把已有 site/ 复制到 site_tmp/ 增量抓取
    uv run python mirror.py --no-clean
  • 试跑探活(只检查不下载):
    uv run python mirror.py --spider
  • 自定义目标站点和输出目录,用于抓取其他博客:
    uv run python mirror.py --url https://example.com/ --output-dir my_site

🐍 Python 代码

"""
The script prefers the bundled wget on Windows (tools/mingw64/bin/wget.exe),
and uses the system wget on Linux/macOS, falling back to the bundled version
when system wget is unavailable. Output defaults to ./site and can be cleaned
before each run.
"""

from __future__ import annotations

import argparse
import hashlib
import os
import platform
import re
import shutil
import subprocess
import sys
from pathlib import Path
from typing import Iterable, List
from urllib.error import URLError
from urllib.parse import urlsplit
from urllib.request import urlopen


DEFAULT_URL = "https://blog.example.com/"
DEFAULT_OUTPUT_DIR = "site"
REJECT_REGEX = r"/(admin|login|register|action|feed)/"


def project_root() -> Path:
    """Return the directory containing this script."""
    return Path(__file__).resolve().parent


def resolve_output_dir(root: Path, output_dir: str) -> Path:
    """Resolve and validate the output directory inside the project root."""
    target = (root / output_dir).resolve()
    try:
        target.relative_to(root)
    except ValueError as exc:
        raise ValueError("Output directory must stay under the project root") from exc
    return target


def temp_output_dir(output_dir: Path) -> Path:
    """Derive a temporary output directory alongside the final output."""
    return output_dir.with_name(f"{output_dir.name}_tmp")


def bundled_wget_path(root: Path) -> Path:
    """Path to the repository-bundled wget executable."""
    return root / "tools" / "mingw64" / "bin" / "wget.exe"


def find_wget(root: Path) -> Path:
    """Pick the appropriate wget executable."""
    system_name = platform.system().lower()
    bundled = bundled_wget_path(root)
    system_wget = shutil.which("wget")

    if system_name == "windows":
        if bundled.exists():
            return bundled
        if system_wget:
            return Path(system_wget)
        raise FileNotFoundError(
            "wget not found. Expected bundled wget at tools/mingw64/bin/wget.exe "
            "or a system wget in PATH."
        )

    if system_wget:
        return Path(system_wget)
    if bundled.exists():
        return bundled
    raise FileNotFoundError(
        "wget not found. Install wget or place it at tools/mingw64/bin/wget.exe."
    )


def cleanup_directory(path: Path) -> None:
    """Remove a directory tree if it exists."""
    if path.exists():
        shutil.rmtree(path)


def prepare_temp_directory(temp_dir: Path, seed_from: Path | None) -> None:
    """Create a fresh temporary directory, optionally seeded from an existing tree."""
    cleanup_directory(temp_dir)
    temp_dir.parent.mkdir(parents=True, exist_ok=True)
    if seed_from and seed_from.exists():
        shutil.copytree(seed_from, temp_dir)
    else:
        temp_dir.mkdir(parents=True, exist_ok=True)


def replace_directory(src: Path, dst: Path) -> None:
    """Replace dst with src, keeping the previous dst until replacement succeeds."""
    backup = dst.with_name(f"{dst.name}_backup")
    if backup.exists():
        shutil.rmtree(backup)

    dst.parent.mkdir(parents=True, exist_ok=True)
    dst_existed = dst.exists()
    if dst_existed:
        dst.rename(backup)

    try:
        src.rename(dst)
    except OSError:
        if dst_existed and backup.exists():
            backup.rename(dst)
        raise

    if backup.exists():
        shutil.rmtree(backup)


def build_wget_command(
    wget_path: Path, output_dir: Path, url: str, spider: bool
) -> List[str]:
    """Construct the wget command for the mirror job."""
    command: List[str] = [
        str(wget_path),
        "--mirror",
        "--convert-links",
        "--adjust-extension",
        "--page-requisites",
        "--no-parent",
        "--restrict-file-names=windows",
        f"--reject-regex={REJECT_REGEX}",
        "-P",
        str(output_dir),
        "-nH",
    ]
    if spider:
        command.append("--spider")
    command.append(url)
    return command


def stream_process_output(command: Iterable[str]) -> int:
    """Run a process and stream stdout/stderr to the console."""
    with subprocess.Popen(
        list(command),
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        text=True,
        encoding="utf-8",
        errors="replace",
    ) as proc:
        if proc.stdout:
            for line in proc.stdout:
                print(line, end="")
        return_code = proc.wait()
    return return_code


def rewrite_links_to_local(output_dir: Path, base_url: str) -> None:
    """Post-process downloaded files to point base-domain assets to local copies."""
    parsed = urlsplit(base_url)
    host = parsed.netloc
    if not host:
        return
    prefixes = {f"{scheme}://{host}" for scheme in ("http", "https")}
    prefixes.add(f"//{host}")
    pattern = re.compile(
        r"(?P<prefix>" + "|".join(re.escape(p) for p in prefixes) + r")(?P<path>/[^\s\"'>)]+)"
    )
    for file_path in output_dir.rglob("*"):
        if file_path.suffix.lower() not in {".html", ".htm", ".css", ".js"}:
            continue
        try:
            original = file_path.read_text(encoding="utf-8", errors="ignore")
        except OSError:
            continue
        changed = False

        def _replace(match: re.Match[str]) -> str:
            nonlocal changed
            url_path = match.group("path")
            local_target = (output_dir / url_path.lstrip("/")).resolve()
            if local_target.exists():
                relative = Path(
                    os.path.relpath(local_target, start=file_path.parent.resolve())
                )
                changed = True
                return str(relative).replace("\\", "/")
            return match.group(0)

        rewritten = pattern.sub(_replace, original)
        if changed:
            try:
                file_path.write_text(rewritten, encoding="utf-8")
            except OSError:
                pass


def _hash_filename(url: str, default_ext: str = ".bin") -> str:
    parsed = urlsplit(url)
    ext = Path(parsed.path).suffix or default_ext
    digest = hashlib.sha1(url.encode("utf-8", "ignore")).hexdigest()
    return f"{digest}{ext}"


def download_external_images(output_dir: Path, base_url: str) -> None:
    """Download external img/src assets and rewrite HTML to local relative paths."""
    base_host = urlsplit(base_url).netloc
    external_dir = output_dir / "external_assets"
    external_dir.mkdir(parents=True, exist_ok=True)

    img_pattern = re.compile(
        r'(<img[^>]+src=["\'])(?P<src>https?:\/\/[^"\']+)(["\'])',
        flags=re.IGNORECASE,
    )
    replacements: dict[str, Path] = {}
    html_files = [
        p for p in output_dir.rglob("*") if p.suffix.lower() in {".html", ".htm"}
    ]

    for file_path in html_files:
        try:
            content = file_path.read_text(encoding="utf-8", errors="ignore")
        except OSError:
            continue

        changed = False

        def _handle_match(match: re.Match[str]) -> str:
            nonlocal changed
            src_url = match.group("src")
            host = urlsplit(src_url).netloc
            if not host or host == base_host:
                return match.group(0)

            if src_url not in replacements:
                filename = _hash_filename(src_url, default_ext=".img")
                dest_path = external_dir / filename
                if not dest_path.exists():
                    try:
                        with urlopen(src_url, timeout=20) as resp, open(
                            dest_path, "wb"
                        ) as out_f:
                            shutil.copyfileobj(resp, out_f)
                    except (URLError, OSError):
                        return match.group(0)
                replacements[src_url] = dest_path

            dest_path = replacements[src_url]
            relative = Path(
                os.path.relpath(dest_path.resolve(), start=file_path.parent.resolve())
            )
            changed = True
            new_src = str(relative).replace("\\", "/")
            return f"{match.group(1)}{new_src}{match.group(3)}"

        rewritten = img_pattern.sub(_handle_match, content)
        if changed:
            try:
                file_path.write_text(rewritten, encoding="utf-8")
            except OSError:
                pass


def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Mirror https://blog.sixhz.com/ into a local static site directory."
    )
    parser.add_argument(
        "--url",
        default=DEFAULT_URL,
        help="Root URL to mirror (default: %(default)s)",
    )
    parser.add_argument(
        "--output-dir",
        default=DEFAULT_OUTPUT_DIR,
        help="Directory (relative to project root) to store the mirrored site (default: %(default)s)",
    )
    clean_group = parser.add_mutually_exclusive_group()
    clean_group.add_argument(
        "--clean",
        dest="clean",
        action="store_true",
        help="Start from a fresh temporary directory instead of seeding from the existing output (default).",
    )
    clean_group.add_argument(
        "--no-clean",
        dest="clean",
        action="store_false",
        help="Seed the temporary download directory from the existing output before mirroring.",
    )
    parser.set_defaults(clean=True)
    parser.add_argument(
        "--spider",
        action="store_true",
        help="Only run wget spider mode to test links without downloading files.",
    )
    return parser.parse_args(argv)


def main(argv: list[str] | None = None) -> int:
    args = parse_args(argv)
    root = project_root()
    try:
        output_dir = resolve_output_dir(root, args.output_dir)
        temp_dir = temp_output_dir(output_dir)
        output_dir.parent.mkdir(parents=True, exist_ok=True)
        temp_dir.parent.mkdir(parents=True, exist_ok=True)
        wget_path = find_wget(root)
    except (FileNotFoundError, ValueError) as exc:
        print(exc, file=sys.stderr)
        return 1

    print(f"Using wget at: {wget_path}")
    print(f"Output directory: {output_dir}")
    print(f"Temporary directory: {temp_dir}")

    # Spider-only mode remains available for manual checks.
    if args.spider:
        cleanup_directory(temp_dir)
        spider_command = build_wget_command(wget_path, temp_dir, args.url, spider=True)
        print("Running spider command:")
        print(" ".join(spider_command))
        spider_code = stream_process_output(spider_command)
        cleanup_directory(temp_dir)
        if spider_code != 0:
            print(f"Spider check failed with code {spider_code}", file=sys.stderr)
        return spider_code

    # Pre-flight: spider the site before attempting a mirror.
    cleanup_directory(temp_dir)
    spider_command = build_wget_command(wget_path, temp_dir, args.url, spider=True)
    print("Running spider check before mirroring:")
    print(" ".join(spider_command))
    spider_code = stream_process_output(spider_command)
    if spider_code != 0:
        print(
            "Skipping mirroring because spider check failed; keeping existing output.",
            file=sys.stderr,
        )
        cleanup_directory(temp_dir)
        return spider_code

    try:
        seed_source = output_dir if not args.clean else None
        prepare_temp_directory(temp_dir, seed_from=seed_source)
    except OSError as exc:
        print(f"Failed to prepare temporary directory: {exc}", file=sys.stderr)
        cleanup_directory(temp_dir)
        return 1

    mirror_command = build_wget_command(wget_path, temp_dir, args.url, spider=False)
    print("Running mirror command:")
    print(" ".join(mirror_command))

    return_code = stream_process_output(mirror_command)
    if return_code != 0:
        print(f"wget exited with code {return_code}", file=sys.stderr)
        cleanup_directory(temp_dir)
        return return_code

    # Post-process links to ensure assets point to local copies for offline deploy.
    rewrite_links_to_local(temp_dir, args.url)
    download_external_images(temp_dir, args.url)

    try:
        replace_directory(temp_dir, output_dir)
    except OSError as exc:
        print(f"Failed to replace output directory: {exc}", file=sys.stderr)
        cleanup_directory(temp_dir)
        return 1

    return return_code


if __name__ == "__main__":
    sys.exit(main())

🧩 自动任务

可以参考run_mirror.sh,借助Github,将本项目部署在服务器上,以实现自动任务。

#!/bin/bash
set -e

# 确保能找到 uv 等命令
export PATH="/root/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

# 1. 到仓库目录
cd "$(dirname "$0")"

# 2. 确保是最新代码(可选)
git pull --rebase || true

# 3. 运行镜像脚本
uv run python mirror.py

# 4. 把新生成的内容加入 git
git add .

# 5. 提交(如果没有变更就跳过)
if ! git diff --cached --quiet; then
  git commit -m "chore: daily mirror $(date -Iseconds)"
  git push origin master
fi

📜 开源协议

本项目基于 GNU GPL v3,你可以自由地使用、分发、修改。

效果之强令人震撼,我国能把这样的东西做出来,确实是人才济济。未来会发展成什么样,真的不敢轻易想象,生怕自己的想象力已经不够用了。

不过,目前在操作体验上仍显得不够人性化,依旧依赖指令式语音操控,给人的感觉更像是一款技术过渡期的产品。希望最终我们真的能迎来属于自己的“贾维斯”。

控制端自监控

进过测试,控制端是可以做到自己监控自己的。二进制部署后,用作控制端的 NAT 小鸡的 64MB 内存也没有爆。

给板卡扎针 (Komari Agent)

目前,我的 Linux 板卡已经连上路由器,也就是常说的“已连上 Wi-Fi”了。

此时板卡处于一种典型的 “只能出不能入”(Outbound-only) 网络环境:允许主机主动连接外部网络 (Outbound),但不允许外部直接访问内部主机(Inbound 被阻断)。

在这种前提下,依然成功让 Komari Agent “扎针”并保持在线,这一点让我感到非常意外,非常有意思。

Komari Agent 通信机制

其详细原理可见 官方文档 - Komari Agent 信息上报与事件处理文档

在我的场景中,板卡是 Linux 系统,并且 可以正常出站访问公网。Komari Agent 启动后会:

  • 主动向 Komari 服务端发起 HTTPS / WebSocket 连接;
  • 启动时先通过 HTTP 上报一次基础信息(例如 CPU、内存、磁盘等静态或缓变数据);
  • 之后通过 WebSocket 以固定间隔持续推送实时监控数据(CPU 使用率、内存占用等);
  • 同时在这条 WebSocket 连接上接收来自服务端的远程命令等事件,在本地执行后再将结果回传。

整个过程中,所有连接都是由 “板卡 → 公网 → Komari 主控” 主动发起的:内网侧无需开放任何端口,路由器也不需要做端口映射,更不要求公网能够反向访问板卡 IP。

Agent 自动发现

其详细教程可见 官方文档 - Agent 自动发现

bash <(curl -sL https://raw.githubusercontent.com/komari-monitor/komari-agent/refs/heads/main/install.sh) -e https://example.com --auto-discovery <AD Key> --install-ghproxy https://ghfast.top/

Step 0. 开机

安装Debian系统,IP位置 Report.Check.Place

Step 1. 更新 apt

apt update -y && apt upgrade -y
apt install -y curl wget unzip

Step 2. 创建目录

mkdir -p /opt/komari
cd /opt/komari

Step 3. 下载 Komari 控制端

# https://github.com/komari-monitor/komari/blob/main/docs/README_zh.md
curl -fsSL https://raw.githubusercontent.com/komari-monitor/komari/main/install-komari.sh -o install-komari.sh
chmod +x install-komari.sh
sudo ./install-komari.sh
journalctl -u komari -f
# 修改密码(可选),默认用户名admin
cd /opt/komari
systemctl stop komari 2>/dev/null || true
./komari chpasswd -p 'TEEHRz7jap3TxpR2'
systemctl start komari 2>/dev/null || ./komari server -l 0.0.0.0:25774 &

Step 4. 反向代理

内部 80/443 端口不提供的话,可跳至第6步。

apt update
apt install -y nginx
# 替换 monitor.example.com 为正确的网址
cat >/etc/nginx/sites-available/komari.conf << 'EOF'
server {
    listen 80;
    listen [::]:80;
    server_name monitor.example.com;

    location / {
        proxy_pass http://127.0.0.1:25774/;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
EOF
# NAT 映射外部端口 → 内部 80
ln -s /etc/nginx/sites-available/komari.conf /etc/nginx/sites-enabled/
nginx -t && systemctl restart nginx

Step 5. 开启HTTPS

安装 certbot 工具
方式一:HTTP 验证(需要外部 80 端口)
方式二:TLS-SNI / HTTPS 验证(需要外部 443)
方式三:DNS TXT 验证(适合 NAT)

Step 6. 使用 Cloudflare Tunnel

安装并绑定 Tunnel,写 cloudflared 配置文件,最后注意要启动 Tunnel 并设置开机自启。

立创 · 泰山派 RK3566-Linux 开发板 2G+16G 版本,架构 aarch64,内核 4.19.232

过程

  1. 刷入官方编译的 Debian 10 固件 hdmi_20230915_update.img
  2. 串口 150000 看到 linaro-alip login: 提示后,用账号 linaro 和密码 linaro 成功登录系统。
  3. 切换为 root 用户 sudo -i
  4. 使用 nmcli device wifi list 查看可用 Wi-Fi 热点。
  5. 使用 sudo nmcli device wifi connect "<WIFI_SSID>" password "<WIFI_PASSWORD>" 连接目标 Wi-Fi。
  6. sudo ping -c 4 blog.sixhz.top 测试连通性和延迟。
  7. 获取 IP 地址,修改为静态 192.168.0.50,配置路由到板卡 22 端口,从此可以使用 SSH 工具远程板卡(局域网)。
  8. 修改 /etc/apt/sources.list 和相关配置文件,将软件源切换到可用的 Debian buster 归档仓库。
  9. 使用 sudo apt update 测试并确认 APT 软件源可以正常更新软件列表。
  10. 板卡为 ARM 架构,可以安装 1Panel 面板,但内核不支持 mqueue 文件系统,故不能使用 Docker。
  11. 挂载 128GB TF 卡,并配置开机自动挂载,查看 df -h
  12. 安装 Nginx 用于配置网站根目录,如 TF 卡挂载的位置。

后续

  1. 把 /tmp 转移到 TF 卡,并且让它拥有 20GB 可用空间,防止文件快递柜上传大文件爆掉。
  2. 想办法把 8GB 的 /userdata 分区利用上。

1Panel 面板

v1 版本安装 (Docker 附带就安装了)
https://1panel.cn/docs/v1/installation/online_installation/#2

Linux SSH 命令 如何安装 Docker?如何查看版本/开机自动启动/卸载
如何一条脚本,自动安装 Docker,配好 Docker Compose,生成一个 WordPress 示例

Docker Terminal UI 程序,直接在终端里跑:https://github.com/jesseduffield/lazydocker

(进阶)安装 ufw-docker 或 ufw-docker-automated ,解决容器绕过防火墙的问题。

Komari 探针控制端

Docker 一键安装 Komari(数据目录:/opt/komari-monitor,端口 25774)

sudo mkdir -p /opt/komari-monitor && \
sudo chown -R $USER:$USER /opt/komari-monitor && \
docker run -d \
  -p 25774:25774 \
  -v /opt/komari-monitor:/app/data \
  --name komari \
  --restart unless-stopped \
  ghcr.io/komari-monitor/komari:latest

SSH 命令 如何安装/配置 防火墙

RedHat/CentOS 使用的是 Firewall 防火墙。
Debian/Ubuntu 使用的是 UFW 防火墙。

SSH 命令 如何安装/配置 Fail2ban

注意添加 jail.local 文件,不修改原配置文件,Debian12 中 backend = systemd
开机自动启动/重启/查看状态/查看可用 jail 状态

[Definition]
logtarget = SYSTEMD-JOURNAL
allowipv6 = auto
  
[DEFAULT]
ignoreip = 127.0.0.1/8
backend = systemd
banaction = iptables-multiport
bantime = 86400
findtime = 86400
maxretry = 3
  
[sshd]
enabled = true
filter = sshd
port = ssh

git init
uv init --python 3.12
uv venv --python 3.12
.\.venv\Scripts\Activate.ps1
python -V
git add .
git commit -m "初始化 git + uv 项目,并添加虚拟环境配置"
uv add matplotlib
# Ensure matplotlib can render Chinese text and minus signs
plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei", "Arial Unicode MS"]
plt.rcParams["axes.unicode_minus"] = False

以下内容可以复制发给AI,以获得更详细的指导😊

bash 命令 DD 脚本 (可选)

https://github.com/bin456789/reinstall

# 运行示例
bash reinstall.sh debian 12 

bash 命令 初始化

apt update -y
apt upgrade -y 
apt install -y sudo curl wget nano vim
apt install neofetch
neofetch

bash 命令 校正系统时间 将时区改为上海 并查看当前时区

sudo timedatectl set-timezone Asia/Shanghai
timedatectl

bash 命令 开启 bbr 和 fq

如何开启BBR?注意用 /etc/sysctl.d/ 的文件来管理系统参数,不要动 sysctl.conf

# 证书环境
apt-get install ca-certificates wget -y && update-ca-certificates
# 脚本示例
wget -O tcpx.sh "https://github.com/ylx2016/Linux-NetSpeed/raw/master/tcpx.sh" && chmod +x tcpx.sh && ./tcpx.sh

bash 命令 如何添加 SWAP?并查看效果

# 脚本示例
wget -O swap.sh https://raw.githubusercontent.com/yuju520/Script/main/swap.sh && chmod +x swap.sh && clear && ./swap.sh
# 查看内存结果
free -m

bash 命令 修改 SSH 端口,默认为 22

如何用命令行将默认的 22 端口修改为 55555,并重启 sshd 服务

bash 命令 如何免密登录

解释如何在 VPS 设置密钥 实现 SSH 免密远程 Debian12 服务器
git bash 生成公/私钥,公钥上传到服务器(写入 authorized_keys)
如何成功连接后关闭禁止密码登录仅允许密钥登录

刚拿到烫手的机器,当然先要跑一跑各种测试了。

基础信息查看

# Debian / Ubuntu
apt update -y && apt upgrade -y && apt install neofetch && neofetch
# Alpine
sudo apk update && sudo apk upgrade && sudo apk add neofetch && neofetch

I/O 性能测试

curl -sL yabs.sh | bash

图标生成

添加位置

控制台 - 外观 - 编辑当前外观 - 编辑文件 header.php :

<!-- 添加网站图标 -->
<link rel="apple-touch-icon" sizes="180x180" href="https://academic.sixhz.com/images/favicon/apple-touch-icon.png" />
<link rel="icon" type="image/x-icon" href="https://academic.sixhz.com/images/favicon/favicon.ico" />
<link rel="icon" type="image/png" sizes="512x512" href="https://academic.sixhz.com/images/favicon/android-chrome-512x512.png" />
<link rel="icon" type="image/png" sizes="192x192" href="https://academic.sixhz.com/images/favicon/android-chrome-192x192.png" />
<link rel="icon" type="image/png" sizes="32x32" href="https://academic.sixhz.com/images/favicon/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="https://academic.sixhz.com/images/favicon/favicon-16x16.png" />
<link rel="manifest" href="https://academic.sixhz.com/images/favicon/site.webmanifest" />

图标预览

为什么记下这样一篇文章?这是计划的一部分。 :-)

从五月以来,把三体给读完了,就想把自己读后一些小想法记下来。

读三体的起因

在学校的时间里,其实很少会有时间去读书。就算有一些空闲的时间,真的很难会想到会拿这点儿时间去读书,毕竟现代人平时里娱乐的方式太多。

然而,在今年五一归家途中,要在坐高铁要好久,车上开手机流量也太不好使,就点开了之前下载的电子书,结果打开后就一发不可收了。

关于《三体》,从初中就听闻过它的大名,在去年暑假的闲暇时间里,已经把三体第一部读完。不过,第一本的具体内容也记不太清了,还依稀记得三体游戏、智子监控什么的。继续去读第二部,事实上并不影响接下来的阅读体验。在接下来的一个月里,断断续续读完了原著。随后,我还找到一些其他的相关作品来看,下面是一些我的简单感受。

三体及其相关作品

三体原著

原作多牛逼不用多说了。虽然书中有一些不符合科学角度的设定,但是它世界观和宏大的想象力实在厉害,一部比一部视角开阔,读后实在有一种豁然开朗的感觉。

球形闪电

《球形闪电》三体前传: 出现在三体中的有丁仪和球形闪电武器,不影响阅读原著。作者想象量子学的宏观现象,比如宏电子有足球那么大,还能被激发形成球形闪电;被球形闪电打击消失的生物会处于量子态;观察者的概念…都是让我印象深刻的!想象力赞!

观想宇宙

同人小说:《三体 X·观想宇宙》,作者宝树。开始下载下来搁置了,字数没多少,后来一股气看完了。想象力上是可以的,内容文笔稍逊色。作为读完原作后的解馋是十分 OK 的,也有我喜欢的歌者片段。“同人逼死官方。”原著没续作了。

我的三体

原创动画:真实展现技术爆炸!目前一共三部,现在只看了罗辑传 , 后两部已刷完。剧情还原度好,动画赞!音乐赞!创新赞!

水滴

原创动画短片: 有些艺术,确实有那种宇宙宏大的感觉,推荐一看。

超新星纪元

《超新星纪元》与三体不相关,刘慈欣作品里顺便看的,设想还是很大胆的。不过,不太合我的口味。

烧火工

作为一个童话故事,挺好的。里面描写鲸鱼的情节是在说日本么?

后记

自己本来写这篇之前似乎心里有很多想说的,但鉴于自己表达能力不强,一坐在键盘前就什么不知道要说啥了了。只能把心中最直接的一些小想法记下来了,以后再想到什么再补充吧~

最后,就贴一个我最喜欢的关于歌者的片段作为结束吧~

歌者·时间童话

我看到了我的爱恋
我飞到她的身边
我捧出给她的礼物
那是一小块凝固的时间
时间上有美丽的条纹
摸起来像浅海的泥一样柔软

她把时间涂满全身
然后拉起我飞向存在的边缘
这是灵态的飞行
我们眼中的星星像幽灵
星星眼中的我们也像幽灵

相关链接

  1. 萌娘百科 - 三体衍生作品
  2. 小姬.刘慈欣写给我的童话