[Day 29] 从零开始学Python - 打包安装PyInstaller:谁把谁的灵魂,装进谁的身体

注:本文同步刊载在Medium,若习惯Medium的话亦可去那边看呦!

因为按照惯例,第三十天主要会讲比较偏向结论性质的东西,
包含接下来可能的学习方向以及建议,
所以让我们今天用PyInstaller来做技术方面的最後一章,
恭喜各位读者,这系列如果都有扎实的跟上的话,
应该能对Python有一些基本的认识。

PyInstaller是一个用来打包安装Python档案的函式库,
一般状况下,使用pip可以轻松将其安装:

pip install pyinstaller

为什麽我们会需要PyInstaller呢?
一般状况下有几个可能:

  1. 你不想直接让别人看到程序码(注:但会破解的还是做得到XD)
  2. 你在开发过程中用了一些函式库,
    这些函式库并非内建的,从而别人要用你的.py档执行的话,
    会需要进行其它的pip install或准备工作。
    这样很麻烦很不方便,
    你希望拿到的人最好可以滑鼠点两下就可以执行才对XD

这时候使用PyInstaller就可以达成这样的目的,
除了可以给定简单的加密外(防君子的那种),
它还可以将整个程序连同用到的函式库一起打包成执行档!

我们这边使用前面的tkinter的范例,
读者应该会用到的档案就是fromzero.py和unicorn.ico,
还没做过的同学,请参照Day 22的范例及Day 23的修改部分。
https://ithelp.ithome.com.tw/upload/images/20201008/20119871RQGzzgQdt0.jpg
为了测试,
我们再手动加上一个其实我们没有在这个范例使用的numpy,
请在fromzero.py中额外加入这行:
(因为它是额外pip安装的,藉此我们可以观察一下差异)

import numpy as np

PyInstaller的使用方式是直接在命令提示字元下指令,
使用pyinstaller -h可以查看help提示:

pyinstaller -h
...密密麻麻的一大堆XD

我们简单介绍一下几个常用的参数部分:
(前面和後面是相同的效果,只是使用缩写)
-h, --help:显示help提示说明各参数用法
-F, --onefile:打包成单一一个执行档
-D, --onedir (预设):打包成一个资料夹,内含一个执行档
-y, --noconfirm
--clean:清空前面打包时产生的暂存档案
-n NAME, --name NAME:将NAME做为app名字并命名到执行档
(预设会是主程序原先的主档名)
--add-data <SRC;DEST or SRC:DEST>
将非二进位档案加到打包中,
SRC对应原先的档案,DEST对应打包後放的相对资料夹位置
-p DIR, --paths DIR:如果有额外需要import的函式库时,
告诉pyinstaller可以去DIR这个位置搜寻
--key KEY:用key来加密Python的bytecode
-w, --windowed, --noconsole:在Windows执行时隐藏命令提示字元的视窗

那麽,我们先试试看最基本的打包:

C:\Users\Desolve\utils>pyinstaller --noconfirm fromzero.py
...底下会开始打包

在预设的状况下,会产生几个目录:
pycache(主程序编译的bytecode档),
build(编译过程中产生的档案),
dist(最终执行所需要的执行档及其他资料)

我们切换到dist\fromzero的资料夹以後,
应该可以看到fromzero.exe,以及其它的一些档案及资料夹,
当中就包含了tk和numpy,
显然pyinstaller自动帮我们评估将函式库给包进来了!
那麽,在命令提示字元打fromzero.exe,
或者在资料夹中连点两下就可以执行了......咦?

C:\Users\Desolve\utils\dist\fromzero>fromzero.exe
Traceback (most recent call last):
  File "fromzero.py", line 41, in <module>
  File "tkinter\__init__.py", line 2071, in wm_iconbitmap
_tkinter.TclError: bitmap "unicorn.ico" not defined
[12564] Failed to execute script fromzero

显然我们的独角兽并没有被包进去,
由於前面我们读取档案时,是在和.py相同的资料夹,
所以我们可以将unicorn.ico复制到dist\fromzero的资料夹
就可以正常执行了。
当然这显然不是很理想,所以让我们回到上一步,
处理一下图片的部分。
我们先用以下的方式,将unicorn.ico转成二进位制,
再存到一个.py档做为变数img:
(注:参考自CSDN blog)
(如果是一般档案,则可以使用--add-data='SRC;DEST'的方式即可)

>>> import base64 # base64可以将binary档案转成unicode格式
>>> icon = open('unicorn.ico', 'rb') # 使用binary的格式读入icon
>>> b64str = base64.b64encode(icon.read()) # 转成unicode格式
>>> icon.close()
>>> write = 'img = %s' % b64str # 放到名为img的变数
>>> f = open('icon.py', 'w+') # 写到icon.py中
>>> f.write(write)
6097
>>> f.close()

此时资料夹中会多出一个icon.py,
里面就是img="......"的格式。
接着我们要修改我们的fromzero.py,
从读取原本的icon,改成从icon.py取得变数:

from icon import img # 从icon.py中取得img变数
import base64 # 同样需要base64函式库
# 主视窗生成
win = tk.Tk()
win.title('从零开始学Python:第二件X折?')
win.geometry('800x220')
win.resizable(False, False)
# 加上icon
ico = open('unicorn.ico', 'wb+')
ico.write(base64.b64decode(img)) # 写一个icon出来
ico.close()
win.iconbitmap('unicorn.ico') # 将icon嵌上视窗
os.remove('unicorn.ico') # 把刚刚用完的档案删掉

我们在重新下一次指令:

C:\Users\Desolve\utils>pyinstaller fromzero.py -F -y -w

这时候应该同样可以在dist资料夹看到fromzero.exe,
且由於我们下了-w,所以执行时後面不会出现命令提示字元了!

如果要进行简单加密的话,
pyinstaller预设是使用tinyaes:

pip install tinyaes

安装後,打包时额外加上--key="(16个字元的字串)"即可,
少掉的字元会补0。
例如:

C:\Users\Desolve\utils>pyinstaller fromzero.py -F -y -w --key="XDDD3096"
61 INFO: PyInstaller: 4.0
62 INFO: Python: 3.8.5
62 INFO: Platform: Windows-7-6.1.7601-SP1
64 INFO: wrote C:\Users\Desolve\utils\fromzero.spec
66 INFO: UPX is not available.
68 INFO: Extending PYTHONPATH with paths
['C:\\Users\\Desolve\\utils', 'C:\\Users\\Desolve\\utils']
77 INFO: Will encrypt Python bytecode with key: 00000000XDDD3096
77 INFO: checking Analysis
...(以下省略)

如果你觉得这样子还不够放心,
可以再透过obfuscator或PyArmor之类的软件,
将主程序进行混淆以後再打包,
效果会更好呦XD!

最後要留意一点,
如果想要编译出能在32位元的电脑运行的程序,
则需要使用32位元版本的Python才可以。

那麽,我们就明天见罗!


<<:  HERE API Example - Context menu

>>:  Day30_渗透 patator

【Day10】模组化及引用模组

模组 在一个 .V 档案里面,可以有很多个 module,但是 Top Module 只会有一个,所...

Day 28 -『破釜沉舟的转职路 - 去年说要成为软件工程师的我,今年 38 岁了,我成为工程师了吗?』-- 第一天上班就想好离职的那一天是什麽时候?

不进则退 当要踏进软件这个领域,尤其是前端,我从去年要开始从头学习时,就认清了一个事实,那就是软件...

Day 28 Chatbot integration- 汇率预测小工具

Chatbot integration- 汇率预测小工具 丑话先说在前头,模型虽然可以达到一定程度准...

Django + MariaDB 在 Amazon Linux 2-Day 05

Django + MariaDB 在 Amazon Linux 2-Day 05 今天的目的是要让 ...

Day 7 - Using Global.asax File for Short URL Routing with ASP.NET Web Forms C# 使用全域应用程序类别产生短网址路由功能

=x= 🌵 网址显示方式管理。 短网址功能介绍 : 📌 如果仔细观察网址,可以发现平常浏览的网页并不...