分类: WordPress

  • WordPress全站静态化

    对于想要一个简单的个人博客,又对静态站点较为繁琐的发布流程感到厌烦的人来讲,上手简单、功能完备的WordPress也许是一个不错的选择。

    然而,搭建一个动态站点需要PHP和数据库环境,这对于内存和性能捉襟见肘的廉价云主机而言并不现实。何况在公网环境下,动态站点所面临的安全风险更是显著大于只有静态文件的网站。

    那么,既然个人博客的一切操作都只由自己完成,为什么不能让WordPress运行在本地,定期生成静态站点交由远程主机对外访问呢?本文就是要探讨一种将WordPress全站静态化的实现方式。

    而且,不花钱。


    材料准备

    • 一台具备公网访问能力的廉价主机
    • 一台搭建了WordPress站点的电脑(本文示例安装Linux系统)

    正式开始

    首先,我们要解决如何生成静态站点的问题。

    Simply Static可以帮助我们便捷地生成静态文件。安装并启用该插件,在插件页面-SETTINGS-General调整相关配置,然后点击Gernerate即可。

    我个人的习惯是将/wp-content/uploads/目录加入Additional Files and Directories,以便显示一些媒体文件和额外脚本。

    需要注意的是,Simply Static插件存储临时文件和静态站点的目录在/uploads下,如果将uploads目录加入额外文件,会导致更新静态站点时额外包含我们不希望出现的文件。下文进行自动化实现时将会采取额外操作以避免此问题。

    在生成完成后,理论上/www/wwwroot/wp/wp-content/uploads/simply-static/temp-files下就会出现静态站点的内容。其中的目录里是文件形式的网站内容,同时还会生成前者的压缩形式。进行同步时按需进行选择。

    接下来要搞定自动同步。我们没必要要求自己的静态站点实时更新,所以只需定期生成并同步即可。然而,免费版的Simply Static并不支持自动生成,我们需要想办法让「用户点击Generate按钮」的操作自动化。

    正常情况下,当我们点击Generate按钮后,浏览器会向https://your.site/wp-json/simplystatic/v1/start-export?_locale=user

    发送一个POST请求,以此触发服务器的生成操作。

    这样的POST请求当然需要鉴权,通过调试工具可知,该请求的headers部分应当如下所示。

    其中的关键部分是CookieX-WP-Nonce字段。我们接下来的目标便是自动化获取以上信息。

    顺带一提,以下的代码大部分都是找AI搓的。

    首先模拟登录。

    Python
    session = requests.Session()
    login_url = "https://www.hacinsl.top/wp-login.php"
    
    login_data = {
        "log": "username",
        "pwd": "password",
        "wp-submit": "登录"
    }
    session.post(login_url, data=login_data)

    从中提取cookie并转换为JSON样式。

    Python
    def process_cookies(cookie_text):
        pattern = r'<Cookie (\w+)=([^ ]+) for'
        matches = re.findall(pattern, cookie_text)
        
        cookies = [f"{name}={value}" for name, value in matches]
        result = "; ".join(cookies)
        
        return result
    auth_cookies = process_cookies(str(session.cookies)[20:-1])

    模拟访问管理页面并解析JSON以获取WP-Nonce

    Python
    admin_url = "https://www.hacinsl.top/wp-admin/"
    admin_page = session.get(admin_url)
    
    match = re.search(r'var wpApiSettings\s*=\s*({.*?});', admin_page.text, re.DOTALL)
    if match:
        json_str = match.group(1)
        print("找到JSON部分:", json_str)
        
        try:
            data = json.loads(json_str)
            nonce = data.get('nonce')
            print(f"nonce的值是:{nonce} ")
        except json.JSONDecodeError:
            print("JSON解析失败")
    else:
        print("没有找到以'var wpApiSettings ='开头的行")

    构建请求headersbody

    Python
    headers = {
        "Host":"www.hacinsl.top",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0",
        "Referer": "https://www.hacinsl.top/wp-admin/admin.php?page=simply-static-generate",
        "Content-Type": "application/json",
        "Cookie": auth_cookies,
        "Connection": "keep-alive",
        "X-WP-Nonce":nonce
    }
    
    body = {
        "blog_id":"1",
        "type":"update"
        }

    发送POST请求并输出状态码。

    Python
    response = requests.post("https://www.hacinsl.top/wp-json/simplystatic/v1/start-export?_locale=user",headers=headers, json=body)
    
    print(response)

    等待服务器生成完毕。

    Python
    time.sleep(300)

    以上操作的完整代码如下:

    Python
    import requests
    import json
    import re
    import time
    
    
    def process_cookies(cookie_text):
        pattern = r'<Cookie (\w+)=([^ ]+) for'
        matches = re.findall(pattern, cookie_text)
        
        cookies = [f"{name}={value}" for name, value in matches]
        result = "; ".join(cookies)
        
        return result
    
    session = requests.Session()
    login_url = "https://www.hacinsl.top/wp-login.php"
    admin_url = "https://www.hacinsl.top/wp-admin/"
    
    login_data = {
        "log": "username",
        "pwd": "password",
        "wp-submit": "登录"
    }
    session.post(login_url, data=login_data)
    
    auth_cookies = process_cookies(str(session.cookies)[20:-1])
    print(session.cookies)
    print(auth_cookies)
    
    
    admin_page = session.get(admin_url)
    
    match = re.search(r'var wpApiSettings\s*=\s*({.*?});', admin_page.text, re.DOTALL)
    if match:
        json_str = match.group(1)
        print("找到JSON部分:", json_str)
        
        try:
            data = json.loads(json_str)
            nonce = data.get('nonce')
            print(f"nonce的值是:{nonce} ")
        except json.JSONDecodeError:
            print("JSON解析失败")
    else:
        print("没有找到以'var wpApiSettings ='开头的行")
    
    headers = {
        "Host":"www.hacinsl.top",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0",
        "Referer": "https://www.hacinsl.top/wp-admin/admin.php?page=simply-static-generate",
        "Content-Type": "application/json",
        "Cookie": auth_cookies,
        "Connection": "keep-alive",
        "X-WP-Nonce":nonce
    }
    
    body = {
        "blog_id":"1",
        "type":"update"
        }
    
    response = requests.post("https://www.hacinsl.top/wp-json/simplystatic/v1/start-export?_locale=user",headers=headers, json=body)
    
    print(response)
    
    time.sleep(300)

    接下来搞定文件同步的部分,使用shell脚本。

    首先删除上次生成遗留的文件。

    Bash
    rm -R /www/wwwroot/wp/wp-content/uploads/simply-static/

    运行刚刚编写的Python程序,需要服务器安装Python环境。

    Bash
    /usr/bin/python3 /opt/wp_static.py

    使用rsync同步本地和远程主机的内容。使用--checksum选项以根据校验值判断文件异同,使用--delete选项以及时删除远程主机上存在的已不再使用的文件,通过密钥登录远程主机以避免输入密码。

    Bash
    /usr/bin/rsync -avz --checksum --delete -e "ssh -i /home/username/.ssh/id_rsa" /www/wwwroot/wp/wp-content/uploads/simply-static/temp-files/simply-static-1-*/ hacinsl@misaka.hacinsl.top:/www/wwwroot/wp/

    以上内容的完整代码如下:

    Bash
    #!/bin/bash
    rm -R /www/wwwroot/wp/wp-content/uploads/simply-static/
    /usr/bin/python3 /opt/wp_static.py
    /usr/bin/rsync -avz --checksum --delete -e "ssh -i /home/hacinsl/.ssh/id_rsa" /www/wwwroot/wp/wp-content/uploads/simply-static/temp-files/simply-static-1-*/ hacinsl@misaka.hacinsl.top:/www/wwwroot/wp/
    echo "Done."

    接下来设置一个定时任务,以root身份每半小时运行一次上述脚本,即可完成远程静态站点的自动更新。