streamlit+wordcloud使用pyinstaller打包遇到的一些坑

说明

相比常规的python程序打包,streamlit应用打包需要额外加一层壳,常规app.py应用运行直接使用

shell 复制代码
python app.py

就可以运行程序了,但streamlit应用是需要通过streamlit命令来运行

shell 复制代码
streamlit app.py

所以使用常规的pyinstaller app.py打包是不可行滴。
注:这里就默认大家都把python环境和streamlit环境都安装好了

正确打包姿势

此次操作工作目录结构如下

1、同workdir下创建一个hooks目录并创建一个hook_app.py(命名可以自定义,只是在打包的时候能找到就行)文件,hook_app.py文件内容如下:

python 复制代码
from PyInstaller.utils.hooks import copy_metadata

datas = copy_metadata("streamlit")

2、与app.py文件同级创建一个run_app.py文件,具体内容如下

python 复制代码
import streamlit.web.cli as stcli
import os, sys
 
 
def resolve_path(path):
    resolved_path = os.path.abspath(os.path.join(os.getcwd(), path))
    return resolved_path
 
 
if __name__ == "__main__":
    sys.argv = [
        "streamlit",
        "run",
        resolve_path("app.py"),
        "--global.developmentMode=false",
    ]
    sys.exit(stcli.main())

3、首次打包

直接通过命令进行首次打包

shell 复制代码
pyinstaller --onefile --additional-hooks-dir=./hooks run_app.py --clean

执行完后会在run_app.py同级下生成run_app.spec文件,具体内容如下

python 复制代码
# -*- mode: python ; coding: utf-8 -*-


a = Analysis(
    ['run_app.py'],
    pathex=[],
    binaries=[],
    datas=[('static/*', './static')],
    hiddenimports=['wordcloud'],
    hookspath=['./hooks'],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    noarchive=False,
)
pyz = PYZ(a.pure)

exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.datas,
    [],
    name='run_app',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    upx_exclude=[],
    runtime_tmpdir=None,
    console=True,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
)

到这里首次打包是成功的,但此时生成的exe文件还用不了,别问为啥,问就是有问题,咱继续接着说。

3、二次打包

根据首次打包生成的run_app.spec文件我进行调整,需要将一些环境以及一些所需的静态文件一同打包进去,修改后内容如下所示

python 复制代码
# -*- mode: python ; coding: utf-8 -*-
from PyInstaller.utils.hooks import copy_metadata

datas=[("./site-packages/altair/vegalite/v5/schema/vega-lite-schema.json","./altair/vegalite/v4/schema/"),
        ("./site-packages/streamlit//static","./streamlit/static"),
        ("./site-packages/streamlit/runtime","./streamlit/runtime"),
        ("./site-packages/wordcloud","./wordcloud"),
        ('./static/*', './static')
    ]
datas+=copy_metadata('streamlit')

a = Analysis(
    ['run_app.py'],
    pathex=[],
    binaries=[],
    datas=datas,
    hiddenimports=['wordcloud'],
    hookspath=['./hooks'],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    noarchive=False,
)
pyz = PYZ(a.pure)

exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.datas,
    [],
    name='run_app',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    upx_exclude=[],
    runtime_tmpdir=None,
    console=True,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
)

粗略说明下:这里只是对datas里的内容进行额外处理了,将本地或是指定py环境下的altair、streamlit、wordcloud相关依赖环境打包到exe中

内容修改好后咱直接使用以下命令进行二次打包

shell 复制代码
pyinstaller run_app.spec --clean

等打包完毕后dist目录下就会生成一个run_app.exe 文件了,这时候不要高兴太早,直接运行也是会有问题滴,这里我们需要把app.py文件复制到生成的run_app.exe所在目录下,如图所示:

这时候咱再运行run_app.exe,如果你看到了以结果,那么恭喜你,你已经成功了,如果报错了那就请耐心的看看错误信息仔细排查下,或是看看我接下来说的一问题看你是否踩坑了

遇见问题

1、程序启动报错提示streamlit模块找不到

检查下有没有hook_app.py 文件,以及内容里是否有定义copy_metadata("streamlit"),如果没有就请加上。加上后重复我以上打包步骤再试试,如果还是提示没有就检查下首次打包后生成的run_app.spec文件有没有正常修改为我提供的实例那样,如果改了就再确定下datas定义里设置的路径是否可用

2、程序启动后wordclould模块找不到

检查下首次打包生成的run_app.spec文件修改有没有遗漏下图所示的设置

这里需要把wordcloud模块引入

3、程序启动后词云生成失败,导致页面词云图片为空

这里需要引入一些字体文件,打包好后把字体静态文件放置到与exe程序同级目录下,如下图所示

放好了重启exe程序即可验证,效果如下图

附上一键打包脚本(选择性使用):

python 复制代码
import os
import shutil
import subprocess



WORK_DIR = os.getcwd()

def package():
    build_path = os.path.join(WORK_DIR,'build')
    dist_path = os.path.join(WORK_DIR,'dist')
    if os.path.exists(build_path):
        shutil.rmtree(build_path)
    if os.path.exists(dist_path):
        shutil.rmtree(dist_path)
    try:
        # 构建 PyInstaller 命令
        cmd = [
            'pyinstaller',
            '--onefile',
            '--hidden-import=wordcloud',
            '--add-data', 'static/*;./static',
            '--additional-hooks-dir=./hooks',
            'run_app.py',
            '--clean'
        ]
        print("首次打包cmd:{}".format(cmd))
        result = subprocess.run(cmd, capture_output=True, text=True)
        print("首次打包结果:{}".format(result))
        if result.returncode == 0:
            os.remove(os.path.join(WORK_DIR,'run_app.spec'))
            source_spec = os.path.join(WORK_DIR,'package_app_spec.txt')
            dest_spec = os.path.join(WORK_DIR,'run_app.spec')
            with open(dest_spec,'w') as f:
                pass
            shutil.copy(source_spec, dest_spec)
            cmd_final = [
                'pyinstaller',
                'run_app.spec',
                '--clean'
            ]
            final_result = subprocess.run(cmd_final, capture_output=True, text=True)
            if final_result.returncode == 0:
                app_path = os.path.join(WORK_DIR,'app.py')
                dest_app_path = os.path.join(dist_path,'app.py')
                with open(dest_app_path,'w') as f:
                    pass
                shutil.copy2(app_path,dest_app_path)
                shutil.copytree(os.path.join(WORK_DIR,'static'),  os.path.join(dist_path,'static'))
                print("打包成功")
            else:
                print("最后打包失败:{}".format(final_result))
        else:
            print("首次打包失败:{}".format(result))
    except Exception as e:
        print(e)


if __name__ == "__main__":
    package()

如果按照以上步骤依旧解决不了您的问题,滴滴我哈!

相关推荐
汪洪墩8 分钟前
【Mars3d】设置backgroundImage、map.scene.skyBox、backgroundImage来回切换
开发语言·javascript·python·ecmascript·webgl·cesium
程序员shen1616111 小时前
抖音短视频saas矩阵源码系统开发所需掌握的技术
java·前端·数据库·python·算法
人人人人一样一样2 小时前
作业Python
python
四口鲸鱼爱吃盐2 小时前
Pytorch | 利用VMI-FGSM针对CIFAR10上的ResNet分类器进行对抗攻击
人工智能·pytorch·python
四口鲸鱼爱吃盐2 小时前
Pytorch | 利用PI-FGSM针对CIFAR10上的ResNet分类器进行对抗攻击
人工智能·pytorch·python
小陈phd2 小时前
深度学习之超分辨率算法——SRCNN
python·深度学习·tensorflow·卷积
CodeClimb2 小时前
【华为OD-E卷-简单的自动曝光 100分(python、java、c++、js、c)】
java·python·华为od
数据小小爬虫3 小时前
如何利用Python爬虫获取商品历史价格信息
开发语言·爬虫·python
NiNg_1_2343 小时前
Python的sklearn中的RandomForestRegressor使用详解
开发语言·python·sklearn