python pyinstaller 打包项目

一、pyinstaller 打包项目

1.1 安装pyinstaller

直接安装:pip install pyinstaller
或者源码安装:https://pypi.org/project/PyInstaller/#files

依次执行:
  tar zxvf PyInstaller-3.6.tar.gz PyInstaller-3.6  
  cd PyInstaller-3.6
  sudo python3 setup.py build
  sudo python3 setup.py install

更多可以参考:http://www.pyinstaller.org
参考手册:https://pyinstaller.readthedocs.io/en/v3.6/
关于打包瘦身:https://www.jianshu.com/p/a4339550d7c1

1.2 简单打包命令

【官方使用文档】

pyinstaller [-F/-D] [-w/-c] [-i xxx.ico] xxx.py/xxx.spec

  • xxx.py/xxx.spec:需要打包的程序main文件或者spec文件。spec文件在使用py文件进行打包时会在相同路径下自动生成,spec中的内容也是根据命令行中输入的内容来生成的,也可以使用命令pyi-makespec [options] xxx.py来生成一个纯粹的spec文件,而不会去执行打包的操作。
  • -F/–onefile:将整个程序打包为一个exe文件,需要注意的是,与-D模式生成的exe程序相比,在启动速度上会慢一点,原因是它需要先解压exe文件并生成一个唯一的临时环境来运行程序,关闭环境时也会自动删除这个临时环境,-D模式的程序本身就是解压好的,运行完也不需要执行删除操作,当程序比较大时,这个差别就很明显了。
  • -D/–onedir:默认选项,与F/–onefile参数作用相反,将程序打包为一个文件夹,文件夹中包含启动程序的exe文件和其他依赖的资源文件和DLL文件等。
  • -w:表示程序运行后隐藏命令行窗口,当你不需要使用命令行窗口作为程序的I/O时,比如GUI程序,可以使用这个参数选项。
  • -c:默认选项,与 -w 相反,提供一个命令行窗口进行 I/O。
  • -i/–icon:指定exe程序图标。

1.3 命令行指定参数选项方式打包:

  1. build文件夹:运行后会在同路径下生成一个build文件夹,这个文件夹的作用相当于PyInstaller的工作空间,PyInstaller运行相关的文件和日志都在这个文件夹中,打包完成后可以直接删除。
  2. dist文件夹:运行完成后会在同路径下生成一个dist文件夹,这个文件夹下有一个跟程序同名的文件夹,打包好的exe程序就在这个文件夹下。
  3. 多py文件:如果命令行中指定的py文件不止一个,比如“pyinstaller xxx1.py xxx2.py”,pyinstaller会依次分析并执行,并把第一个py名称作为spec和dist文件下的文件夹和程序的名称。
  4. 其他常用参数选项
    –specpath DIR :指定生成spec文件的路径,默认为当前路径。
    -n NAME/–name NAME:指定spec文件和程序的名称,默认为传入的py脚本或spec文件名称。
    -h/–help :显示PyInstaller帮助信息。
    -v/–version:显示PyInstaller版本信息。
    –distpath DIR:指定生成dist的目录,默认为“./dist”。
    –workpath WORKPATH:指定pyinstaller的工作目录,即build文件夹,默认为“./build”。
    -y/–noconfirm:替换输出目录时不询问,默认输出目录是“SPECPATH/dist/SPECNAME”。
    –upx-dir UPX_DIR:指定UPX程序的路径,默认为“程序执行路径”,即双击某个文件时,系统自动查找对应程序的路径。UPX为一个压缩程序,需要自行下载,可以将exe压缩为zip格式的文件,并且压缩效率非常高,如果打包后的exe程序非常大的话,为了避免客户下载时文件太大的问题,可以使用这个UPX工具。
    –noupx:不需要UPX(即便可用)。
    -a/–ascii:不支持Unicode,默认为支持(如果可用的话)。
    –clean:在pyinstaller开始执行之前清除缓存并删除临时文件(一般存储在C:\Users\Administrator\AppData\Roaming\pyinstaller)。
    –log-level LEVEL:指定打印的日志等级,默认为INFO,日志等级有:TRACE,DEBUG,INFO,WARN,ERROR,CRITICAL。如果在打包时遇到了问题,为了方便定位问题,可以使用这个参数来查看特定级别的日志信息。

1.4 数据绑定和搜索相关的参数选项:

–add-data SRC;DEST:指定需要添加非二进制文件路径或者文件夹路径,比如图片和pdf文件等,这个选项可以使用多次。这个命令其实就是将需要的文件或者文件夹拷贝到指定的路径下,在-D模式下,可以看情况在程序打包完成后自己手动拷贝过去。
–add-binary SRC;DEST:指定需要添加的二进制文件路径,比如DLL文件、动态链接库或者共享文件对象等,这个选项可以使用多次。同-add-data命令一样,是一个拷贝数据的功能。
-p DIR/–paths DIR:指定import语句的查找路径(与PYTHONPATH一样),多个路径之间可以使用分号“;”连接,或者多次使用这个选项来进行指定。
–hidden-import MODULENAME/–hiddenimport MODULENAME:指定脚本中需要隐式导入的模块,比如在__import__、imp.find_module()、exec、eval等语句中导入的模块,这些模块PyInstaller是找不到的,需要手动指定导入,这个选项可以使用多次。
–additional-hooks-dir HOOKSPATH:指定额外hook文件(可以是py文件)的查找路径,这些文件的作用是在PyInstaller运行时改变一些Python或者其他库原有的函数或者变量的执行逻辑(并不会改变这些库本身的代码),以便能顺利的打包完成,这个选项可以使用多次。
–runtime-hook RUNTIME_HOOKS:指定自定义的运行时hook文件路径(可以是py文件),在打好包的exe程序中,在运行这个exe程序时,指定的hook文件会在所有代码和模块之前运行,包括main文件,以满足一些运行环境的特殊要求,这个选项可以使用多次。
–exclude-module EXCLUDES:指定可以被忽略的可选的模块或包,因为某些模块只是PyInstaller根据自身的逻辑去查找的,这些模块对于exe程序本身并没有用到,但是在日志中还是会提示“module not found”,这种日志可以不用管,或者使用这个参数选项来指定不用导入,这个选项可以使用多次。
–key KEY:指定用于Python字节码加密的key,key是一个16个字符的字符串。

1.5 程序的运行模式

  • -D模式程序的运行
    运行程序的时候,其实开始运行的是一个pyinstaller生成的引导加载程序bootloader,bootloader是根据不同的操作系统生成的,运行bootloader时,会创建一个临时的Python环境以便运行Python程序,所以使用exe程序时不用安装Python也能运行这个程序。
  • -F模式程序的运行
    运行程序的时候,也会有一个BootLoader,但是会根据操作系统创建一个名为_MEIxxxxxx的文件夹,用作这个程序的临时运行环境(不只是Python环境),这个xxxxxx是一个随机的数字。-F模式程序启动的时候因为需要解压并拷贝依赖和资源文件到临时运行环境_MEIxxxxxx,所以启动速度是比-D模式程序要慢的,运行结束后会删除临时运行环境的文件夹。在Linux和相关系统中,可能有“no-execution”选项,但是对于-F模式程序是不兼容的。由于_MEIxxxxxx是唯一的,所以可以同时运行多个程序,多个程序时互不干涉的。如果程序崩溃了,或者强行结束了(比如在Windows的任务管理器中杀死了进程),_MEIxxxxxx文件夹是不会被删除的,所以频繁崩溃或者结束进程会导致有多个_MEIxxxxxx文件夹,会非常占用磁盘空间,可以使用–runtime-temdir指定_MEIxxxxxx的存放位置。-F模式程序如果在运行时遇到了权限问题,可以使用-D模式程序替代。
  • shell脚本/批处理脚本
    使用命令行打包时,可能需要指定的参数选项很多,这时候可以将需要执行的全部命令信息,包括这些参数选项的指定,都放在一个shell脚本或者批处理文件中来执行。

1.6 运行时信息:

__file__: 所有基于模块的使用到__file__属性的代码,在源码运行时表示的是当前脚本的绝对路径,但是打包后就是当前模块的模块名(即文件名xxx.py)。
sys.frozen:源码运行时没有这个属性,打包后的程序添加了这个属性,值为True。
sys._MEIPASS: 源码运行时没有这个属性,打包后的程序添加了这个属性,表示程序运行的绝对路径。对于-D模式程序,表示的是这个exe程序所在文件夹的绝对路径,对于-F模式程序表示的是_MEIxxxxxx的文件夹绝对路径,_MEIxxxxxx为exe解压后创建的临时运行环境的文件夹名称,对于exe程序每一次运行来说,它是唯一的。
sys.excutable:代码运行时表示运行的解释器绝对路径,如C:\Python36\python.exe,在打包的程序中就是exe程序文件的绝对路径,这个是用来定位用户运行该程序的真实位置。
sys.argv[0]:一般来说就是运行程序的绝对路径,但是在不同平台或者不同的方式启`动程序时,会有所不同,比如通过符号链接运行的程序sys.argv[0]就是符号名称,而不是真实的程序路径。

1.6.1 数据文件的修改

–add-data这种通过拷贝形式的数据文件,在-D模式下如果在运行时修改了,那么对应的数据文件是真的被修改了,但是在-F模式下,由于每次运行会单独创建一个临时运行环境,修改的内容也是临时运行环境中的内容,并且运行完后会自动删除临时运行环境,所以这种数据文件是无法直接更改exe中的数据文件的,也就意味着每次运行程序,数据文件都会是exe程序中原来的那一份,修改的内容会随着临时运行环境的关闭而删除了,不会同步到exe程序中的。

1.6.2 发生错误时
  • 当发生“module not found”警告时,其实很多找不到对应模块的消息都不用管,只是PyInstaller根据自身的逻辑去查找的,因为它们并不是跟你的最终程序有关的。
  • 当发生导入失败时,这才是真正的错误,需要去关注和解决的。
  • 当打包成功,且中间没有发生任何警告提示,但是运行程序时提示某个模块找不到,可能就是“–hidden-import=”的问题了,当使用__import__、imp.find_module()、exec、eval或者Python/C API时,pyinstaller不会自动去导入这些里面涉及的导包,所以这些包就需要使用“–hidden-import=”来指定具体需要导入的包了。
  • –runtime-hook=xxx.py中指定的py会依次在打包的主程序main脚本之前运行,可以用来改变一些函数或者变量,以满足特殊场景的要求。

1.7 spec配置文件方式打包

  1. 生成spec文件:pyi-makespec [options] xxx.py [other scripts…],生成spec文件时可以什么都不指定,然后在生成的spec文件中单独配置,默认为-D模式下的spec文件,也可以指定生成-F模式的spec文件。当然也可以在第一次就将参数选项指定好,以后就只维护spec文件。
  2. 参数选项:生成spec文件的参数选项和命令行模式下执行PyInstaller打包是完全一样的。
  3. spec文件类型:spec文件其实就是一个py文件,在编辑时可以直接将它当成一个py文件来使用。
  4. spec文件优势:一般情况而言,直接使用PyInstaller命令行直接打包即可,但是以下情况使用spec文件的话会方便一些:
    1. 程序需要绑定一些数据文件,可以在spec文件中单独用一个列表变量来指定,可读性和可维护性会高很多。
    2. 需要include一些PyInstaller不知道的动态链接库,如:.dll/.so文件,同样可以在spec文件中单独用一个列表变量来指定。
    3. 需要往可执行文件中添加一些运行时选项,如hook文件。
1.7.1 关于spec文件:
如果有需要,可以通过PyCharm、eclipse等工具打开安装目录中的PyInstaller文件夹来查看源码信息
  • a:Analysis类的实例,要求传入各种脚本用于分析程序的导入和依赖。a中内容主要包括以下四部分:scripts,即可以在命令行中输入的Python脚本;pure,程序代码文件中的纯Python模块,包括程序的代码文件本身;binaries,程序代码文件中需要的非Python模块,包括–add-binary参数指定的内容;datas,非二进制文件,包括–add-data参数指定的内容。
  • pyz:PYZ的实例,是一个.pyz文件,包含了所有pure中的所有Python模块。
  • exe:EXE类的实例,这个类是用来处理Analysis和PYZ的结果的,也是用来生成最后的exe可执行程序。
  • coll:COLLECT类的实例,用于创建输出目录。在-F模式下,是没有COLLECT实例的,并且所有的脚本、模块和二进制文件都包含在了最终生成的exe文件中。
  1. Analysis参数scripts:也是第一个参数,它是一个脚本列表,可以传入多个py脚本,效果与命令行中指定多py文件相同,即py文件不止一个时,比如“pyinstaller xxx1.py xxx2.py”,pyinstaller会依次分析并执行,并把第一个py名称作为spec和dist文件下的文件夹和程序的名称。

  2. Analysis参数pathex:同命令“-p DIR/–paths DIR”,其实默认就有一个spec的目录,如果使用命令添加的话,会首先添加命令中指定的目录,再添加默认的路径。

  3. Analysis参数datas:即添加数据文件,命令是–add-data,spec文件中是Analysis的datas=[]参数,datas是一个元素为元组的列表,每个元组有两个元素,都必须是字符串类型,元组的第一个元素为数据文件或文件夹,元组的第二个元素为运行时这些文件或文件夹的位置。例如:datas=[(‘src/README.txt’, ‘.’), ],也可以在命令行中这样写:pyinstaller --add-data ‘src/README.txt;.’ myscript.py,表示打包时将文件src/README.txt添加(copy)到相对于spec文件的根目录下,指定文件时是相对于spec来进行寻找的,而不是要打包的exe程序路径。也可以使用通配符:datas= [ (’/mygame/sfx/*.mp3’, ‘sfx’ ) ],表示将/mygame/sfx/目录下的所有.mp3文件都copy到sfx文件夹中。也可以添加整个文件夹:datas= [ (’/mygame/data’, ‘data’ ) ],表示将/mygame/data文件夹下所有的文件都copy到data文件夹下。

  4. Analysis参数binaries:添加二进制文件,效果同命令–add-binary,也是一个列表,定义方式与datas参数一样。
    Analysis参数hiddenimports:同命令“–hidden-import MODULENAME/–hiddenimport MODULENAME”。
    Analysis参数hookspath:同命令“–additional-hooks-dir HOOKSPATH”。
    Analysis参数runtime_hooks:同命令“–runtime-hook RUNTIME_HOOKS”。
    Analysis参数excludes:同命令“–exclude-module EXCLUDES”。
    exe参数console:设置是否显示命令行窗口,和命令-w/-c作用一样。
    exe参数icon:设置程序图标,和命令-i/–icon作用一样。某些情况,直接执行“pyinstaller xxx.py”时生成的spec中没有这个参数,需要手动添加,参数值就是图片路径的字符串。
    PyInstaller全局变量:这些全局变量可以在spec文件使用。

    • DISTPATH:相对于dist文件夹的相对路径,如果–distpath参数选项被指定了,则使用被指定的参数值。
    • HOMEPATH:pyinstaller查找的绝对路径,一般是Python解释器的site-packages文件夹的绝对路径。
    • SPEC:在命令行中指定的spec文件路径。
    • SPECPATH:os.path.split(SPEC)的第一个值。
    • specnm:spec文件的文件名,不含文件类型后缀。
    • workpath:相对于build文件夹的相对路径,如果workpath=参数选项被指定了,这使用被指定的值。
    • WARNFILE:在build文件夹中警告文件的全路径,一般是warn-myscript.txt
  5. 指定了相同的参数:当命令行和spec中指定了相同的参数选项,那么命令行的参数选项会被忽略。

1.8 pyinstaller 打包精简技巧

由于 我在linux使用了Anaconda3 (比直接使用python省事),但是Anacona自带了许多第三方库,打包时可能会将其带入打包文件中,为了精简,我们可以将这些包在打包时移除掉,但是首先你得知道那些包是干什么的,能不能移除掉,否则程序就无法启动了。

Anaconda3 自带第三方包(打包可执行文件时不常用的):

matplotlib: Matplotlib是一个Python 2D绘图库,您只需几行代码就可以生成图表,直方图,功率谱,条形图,误差图,散点图等
pyQt: PyQt5是Qt v5的Python版本,功能强大复杂,提供QT Designer设计UI (GPL V3协议,开源,商用收费)
notebook : python交互式笔记本
jupyter: python交互式笔记本
docutils: 将纯文本转换为一些常用格式的工具: 包括:HTML、XML和LaTeX等
nbformat:jupyter notebook中支持json文件格式,markdown等相关的格式转化与可视化。
nbconvert 允许您将Jupyter.ipynb笔记本文档文件转换为另一种静态格式,包括HTML、LaTeX、PDF、Markdown、restructedtext等
sphinx : 是种令人可以轻松撰写出优美文档的工具: 输出格式:HTML, LaTex, epub, Texinfo, manual pages, plain text。支持语法高亮

zmq: ZMQ是一套嵌入式的网络链接库,是一个基于内存的消息队列,工作起来更像是一个并发式的框架。
tcl: 在Tkinter图形界面编程中如何使用Tcl语言与Python代码交互。
lxml: html解析库。在爬虫领域:对于不好使用正则一部分搜索。推荐lxml解析html的比较多,优点:稳定,高效。
bokeh: (Bokeh.js) 是一个Python交互式可视化库,支持现代化 Web 浏览器。
jedi: 用于Python自动补齐和静态分析的开源库。
pytz: 常用于时区的转换(北京时间->纽约时间),常常配合datetime一起使用。
Crypto: crypto模块的目的是为了提供通用的加密和哈希算法。

例如:

a = Analysis(['xxx.py'],
             pathex=['/home/xxx/xxx'],
             binaries=[("/home/xxx/xx/lib/xx.so.0",".")],
             datas=[],
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=['matplotlib','PyQt','notebook','jupyter','sphinx','docutils','nbformat'],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)

二、编译问题及解决

pyinstaller 打包报错:

  1. RecursionError: maximum recursion depth exceeded
    在 filename.spec 文件头添加下面语句:
    import sys
    sys.setrecursionlimit(5000)
    再次执行:pyinstaller filename.spec

  2. UnicodeDecodeError: 'utf-8' codec can't decode byte 0xce in position 110: invalid continuation byte
    Unicode解码错误:“UTF-8”编解码器无法解码位置130中的字节0xCE:无效的继续字节
    1.首先尝试将中文解释都去掉
    2.控制台中输入 chcp 65001,然后执行打包
    来源:https://blog.csdn.net/qq_41185868/article/details/80599390
    3.[17260] Error loading Python lib '/home/wangsp/ocr_server_multi_file2/dist/ocr_server/libpython3.6m.so': dlopen: /home/wangsp/ocr_server_multi_file2/dist/ocr_server/libpython3.6m.so: cannot open shared object file: No such file or directory
    重新编译或重命名

  3. 报错 ERROR: Tcl/Tk improperly installed on this system

    打包时报错
    93126 INFO: Loading module hook "hook-_tkinter.py"...
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/home/xxx/anaconda3/lib/python3.6/tkinter/__init__.py", line 36, in <module>
        import _tkinter # If this fails your Python may not be configured for Tk
    ImportError: libX11.so.6: cannot open shared object file: No such file or directory
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
    ImportError: libX11.so.6: cannot open shared object file: No such file or directory
    93201 ERROR: Tcl/Tk improperly installed on this system.
    
    运行时时报错
    [90854] Failed to execute script pyi_rth__tkinter
    Traceback (most recent call last):
      File "site-packages/PyInstaller/loader/rthooks/pyi_rth__tkinter.py", line 30, in <module>
    FileNotFoundError: Tcl data directory "/home/xxx/ocr_server_files/dist/ocr_server/tcl" not found.
    

    方法:suod yum -y install tk-devel
    参考:https://blog.csdn.net/qq_43173257/article/details/89189842(使用了这个方法)
    参考:https://blog.csdn.net/weixin_30756499/article/details/98635721
    参考:https://www.cnblogs.com/liyongjian5179/p/10683301.html

  4. 报错内容:pkg_resources.DistributionNotFound: The ‘greenlet>=0.4.14’ distribution was not found and is required by the application
    处理方法
    https://blog.csdn.net/weekdawn/article/details/81039377
    https://github.com/pyinstaller/pyinstaller/issues/1713

    [user@localhost test]$ touch hook-ctypes.macholib.py
    [user@localhost test]$ cat hook-ctypes.macholib.py
    from PyInstaller.utils.hooks import copy_metadata
    datas = copy_metadata('greenlet')
    
  5. 打包paddle程序时出现程序反复不断的启动直到内存耗尽。参考【4067】,【4110】,参考文章【paddleOCR】
    查看文件paddle\dataset\image.py修改后如下:

    # if six.PY3:
    #     import subprocess
    #     import sys
    #     import_cv2_proc = subprocess.Popen(
    #         [sys.executable, "-c", "import cv2"],
    #         stdout=subprocess.PIPE,
    #         stderr=subprocess.PIPE)
    #     out, err = import_cv2_proc.communicate()
    #     retcode = import_cv2_proc.poll()
    #     if retcode != 0:
    #         cv2 = None
    #     else:
    #         import cv2
    # else:
    #     try:
    #         import cv2
    #     except ImportError:
    #         cv2 = None
    try:
        import cv2
    except ImportError:
        cv2 = None
    

    关于隐藏导入:https://zhuanlan.zhihu.com/p/35338321

  6. ImportError: OpenCV loader: missing configuration file: ['config.py']. Check OpenCV installation.
    导入opencv时报错:
    处理分发:指定opencv打包路径:
    pyinstaller demo.py --paths="/home/user/anaconda3/envs/py36_tf121/lib/python3.6/site-packages/cv2"

三. cx_Freeze打包项目

cx_Freeze是一个跨平台的将python转换成可执行程序的项目。
项目主页:http://cx-freeze.sourceforge.net/
可以下载rpm包安装。
安装完以后只需要运行:cxfreezeyourprogram.py
就会在当前目录生成一个dist的目录,里面包含了可执行程序以及依赖模块的库文件。
项目主页上说可以通过distutils的build_exe命令生成单一的可执行程序,不过貌似不行…所以这个相对于pyinstaller可能还是稍有不便。

更多可以参考:http://cx-freeze.sourceforge.net/cx_Freeze.html

一个Python应用程序,其中添加了Cython模块。需要使用cx_Freeze构建的可执行版本。
该可执行文件会引发ImportError并试图导入.pyx模块。
我进行了修改setup.py,以查看是否可以先编译.pyx以便cx_Freeze可以成功打包,但是报错:No module named fx

from cx_Freeze import setup, Executable
from Cython.Build import cythonize


setup(name='projectname',
      version='0.0',
      description=' ',
      options={"build_exe": {"packages":["pygame","fx"]},'build_ext': {'compiler': 'mingw32'}},
      ext_modules=cythonize("fx.pyx"),
      executables=[Executable('main.py',targetName="myproject.exe",base = "Win32GUI")],
      requires=['pygcurse','pyperclip','rsa','dill','numpy']
      )
	  
	  

解决的办法是有两个单独的调用setup();一个fx.pyx用Cython 构建,然后一个用cx_Freeze打包exe。这是修改后的setup.py:
from cx_Freeze import Executable
from cx_Freeze import setup as cx_setup
from distutils.core import setup
from Cython.Build import cythonize

setup(options={'build_ext': {'compiler': 'mingw32'}},
      ext_modules=cythonize("fx.pyx"))

cx_setup(name='myproject',
      version='0.0',
      description='',
      options={"build_exe": {"packages":["pygame","fx"]}},
      executables=[Executable('main.py',targetName="myproject.exe",base = "Win32GUI")],
      requires=['pygcurse','pyperclip','rsa','dill','numpy']
      )

先执行:python setup-cython.py build_ext --inplace
后执行:python setup-cython.py build
  • 5
    点赞
  • 34
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:1024 设计师:我叫白小胖 返回首页
评论

打赏作者

SongpingWang

你的鼓励是我创作的最大动力!

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值