Python打包exe文件方法

“乔治是世界上最后一只平塔岛象龟,而我是世界上最后一个源家后裔,最后一只平塔岛象龟应该为了种族不灭努力地繁殖后代,最后一个源家后裔应该重振家族在黑道中的威望,但是乔治只是想回自己的水坑里去打滚,而我只是想去天体海滩上卖防晒油。”源稚生盯着恺撒的眼睛,“我就是这种人,其实蛇岐八家的黑道事业和秘党的使命对我来说都不重要,我的人生理想就是去卖防晒油。我跟你叔叔不是一种人。”

Python作为解释型语言,发布即公开源码,虽然是提倡开源但是有些时候就是忍不住想打包成exe,不仅仅是为了对代码进行加密,而是为了跨平台。防止有些没有安装py环境的电脑无法运行软件。

目录对python代码打包成exe的方式有4种,py2exe,pyinstaller,cx_Freeze,nuitka,分别对以上4种方法介绍,欢迎纠错和提供新的办法。

py2exe

介绍

py2exe是一个将python脚本转换成windows上的可独立执行的可执行程序(*.exe)的工具,这样,你就可以不用装python而在windows系统上运行这个可执行程序。

优点:

  1. 可以把python打包成exe

缺点:

  1. 打包好的 exe只能在相同的系统下运行,比如你在XP系统用py2exe打包好的exe只能在XP上运行,在win7上肯定有问题,而且也是存在在别的XP机子上也可能不能运行,因为一些别的机子可能会缺少dll文件什么的,作者我当时之所以抛弃py2exe 也是因为这个原因;

  2. 打包的文件也需要打包同exe下的dll,和一些库的压缩包,不然exe根本跑不起来

  3. 打包时候报始终报RuntimeError: BeginUpdateResource错误,就需要拷贝dll文件。C:\WINDOWS\system32\python27.dll复制到dist目录

  4. 总之毛病一大堆,基本上已经舍弃了

使用

  1. 下载安装 下载地址

  2. 安装好后,准备进行打包

  3. 准备一个文件x.py,这个x.py就是想要打包成exe。

  4. 然后这x.py同目录下新建一个setup.py

内容如下:

#coding=utf-8
from distutils.core import setup
import py2exe
includes = ["encodings", "encodings.*"]
#要包含的其它库文件
options = {"py2exe":
  {
    "compressed": 1, #压缩
    "optimize": 2,
    "ascii": 1,
    "includes": includes,
    "bundle_files": 1 #所有文件打包成一个exe文件
  }
}
setup (
  options = options,
  zipfile=None,  #不生成library.zip文件
  console=[{"script": "x.py", "icon_resources": [(1, "Q.ico")] }]#源文件,程序图标
)
  1. 然后这cmd窗口下运行:

    python setup.py py2exe

py2exe使用方法 (含一些调试技巧,如压缩email 类)

pyinstaller

介绍

PyInstaller除了win32平台外还支持Linux,Unix平台.py2exe的用法在前面的博文里已经有了,现在看看PyInstaller,pyinstaller是目前应用最多的python打包工具,也是我最常用的。

PyInstaller本身并不是Python模块,所以安装时随便放在哪儿都行。

优点:

  1. 可将python文件转成可执行文件
  2. 跨平台
  3. 输出的可以是单一目录,也可以是一个单独的打好包的可执行文件。py2exe貌似输出目录。
  4. 智能支持python的第三方模块如PyQt,外部数据文件等
  5. 支持EGG格式文件
  6. 可执行文件可以用UPX压缩,二进制压缩方式
  7. 支持控制台和视窗两种方式。
  8. 可以选择exe文件的图标 (Windows only)
  9. 支持 COM server (Windows only)

缺点:

  1. import导入的问题

pyinstaller是很智能的,只要指定了入口py文件,那么它就会根据代码自动查找需要导入的包。但是隐式导入的话,平常运行是没有问题的,举例:

# test1.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker


DB_CONNECT_STRING = 'mysql+pymysql://root:123456@localhost/study'
engine = create_engine(DB_CONNECT_STRING, echo = False)
DB_Session = sessionmaker(bind = engine)
session = DB_Session()

print('this is my test')

运行这个ORM库的初始化引擎,是没有问题的,在console得到结果:

this is my test

那么我们开始打包,使用最简单的pyinstaller test1.py。打包完成后,在当前目录下有个dist文件夹,进入dist下的test1文件夹,然后打开cmd,运行这个exe,我们就会发现:

提示: no model named 'pymysql'

这是怎么回事呢?那么问题来了,sqlalchemy这个库在初始化的时候是不需要显示导入引擎库的,它自己有一个create_engine()的函数来初始化,这个字符串是使用者根据规则来自己填写的。其实解决的方法很简单,我们只要在显式导入pymysql这个库即可。现在我们导入这个库:

import pymysql

重新打包一遍(重新打包的时候记得删除掉spec文件,否则会有缓存,或者是加上–clean选项清除掉),再次运行,现在就没有这个问题了。

  1. 多进程打包的问题

官方的CPython存在一个GIL锁,这个锁的存在有很多优点,很多库都是线程安全的,单线程执行的效率也高。在python早期的一个版本中取消掉了GIL,代之以高粒度的锁来实现多线程,但是实际应用中单个线程的效率大大降低。故后来又将GIL这个锁还原回去,所以至今的python2也好还是python3中都会有这个锁。但是这个锁有很大一个问题,那就是效率问题,它导致了python仅仅只能利用一个core来进行数据的计算。所以后面为了弥补这个GIL带来的问题,专家们设计了multiprocessing库,gevent库等。前一个是多进程库,为了解决python用于数据密集型处理的情况;后一个用于异步IO处理的情况,基本原理就是在CPU时钟之间来回切换,简单的例子就是爬虫程序爬取网页的时候。假如有10个url,我们都要去GET它,实际上网络之间的延迟是大大高于计算机内部的,那么这个时间内计算机就切换到下一个。

有时候运用多进程是必须的,这个替代不了,哪怕它占用资源很多。

使用

  1. 首先安装

一般情况下:

pip install pyinstaller

但是会爆错误,比如命令行下出现

UnicodeDecodeError: 'gbk' codec can't decode pyinstaller等等

这个时候下载pyinstall按转包文件解压后,放在没有中文的目录下,输入

python setup.py install

即可完成

  1. 首先写一个main.py的文件,到网上下载一个图标,保存为logo.ico

使用方法(例子)在:

pyinstaller -D -p F:\Python27\Lib -i logo.ico mian.py

-D:打包成多个文件
-p:指定python安装包路径
-i:指定图标,到网上下载一个图标,保存为logi.ico文件,
mian.py:要打包的文件

运行完命令行即可生成exe文件

注意main.py与logo.ico必须放在同一个目录下

-D与-F一一对应,-F是打包成一个单独的文件。

最后一排加上–noconsole,就是无窗口运行。

多进程打包

如果py程序是多进程的话,使用pyinstaller打包会出现错误,这个时候只要加上一行代码

在:
if name==’mian‘:

#新增
multiprocessing.freeze_support()  

word文件打包

如果要打包那种操作word的文件代码,用pyinstaller工具把使用到python-docx库的脚本打包成exe可执行文件后,双击运行生成的exe文件,报错:

docx.opc.exceptions.PackageNotFoundError: Package not found at 'C:\Users\ADMINI~1.PC-\AppData\Local\Temp\_MEI49~1\docx\templates\default.docx'

经过在stackoverflow上搜索,发现有人遇到过类似的问题(问题链接:cx_freeze and docx - problems when freezing),经过尝试,该问题的第二个回答可以解决这个问题:

I had the same problem and managed to get around it by doing the following. First, I located the default.docx file in the site-packages. Then, I copied it in the same directory as my .py file. I also start the .docx file with Document() which has a docx=... flag, to which I assigned the value: os.path.join(os.getcwd(), 'default.docx') and now it looks like doc = Document(docx=os.path.join(os.getcwd(), 'default.docx')). The final step was to include the file in the freezing process. Et voilà! So far I have no problem.

大概的解决步骤是这样的:

找到python-docx包安装路径下的一个名为default.docx的文件,我是通过everything这个强大的搜索工具全局搜索找到这个文件的,它在我本地所在的路径是:

E:\code\env\.env\Lib\site-packages\docx\templates

把找到的default.docx文件复制到我的py脚本文件所在的目录下。
修改脚本中创建Document对象的方式:
从原来的创建方式:

document = Document()

修改为:

import os
document = Document(docx=os.path.join(os.getcwd(), 'default.docx'))

再次用pyinstaller工具打包脚本为exe文件

把default.docx文件复制到与生成的exe文件相同的路径下,再次运行exe文件,顺利运行通过,没有再出现之前的报错,问题得到解决。

其他事项

有的时候你打包图标的时候会报错,会显示logo error之类的,这是因为图标里面的格式出错了,需要用图片编辑软件重新对图片内部大小进行设置,这个百度一下吧~

如果要加密混淆代码(其实没有用,一样可以反编译出你的源代码,但是聊胜于无吧)

加上一行 –key=asfweakjhfksafkasdkdbas随便几个字符串

好像要安装依赖包,根据提示安装即可

cx_Freeze

介绍

也是一种打包软件,介于py2exe和pyinstaller之间。

有点

python中比较常用的python转exe方法有三种,分别是cx_freeze,py2exe,PyInstaller。py2exe恐怕是三者里面知名度最高的一个,但是同时相对来说它的打包质量恐怕也是最差的一个。pyinstaller打包很好,但是操作工序较为复杂,推荐cx_freeze,可以通过pip install cx-freeze 安装

缺点

  • 只能指定一个要打包的模块,也就是启动模块
  • 发布后,可执行文件执行路径不能有中文(最好也不要有空格)
  • 启动执行的文件中不要有下面这种判断,否则可执行文件执行会没有任何效果

    if __name__=='__mian__':
    
  • 将exe放到其他机子上运行,弹出“找不到msvcr100.dll“ ,手动将”msvcr100.dll“拷贝到dist目录下即可

  • 要去掉exe里的后面黑色控制台窗口就在前面的命令改成:
    cxfreeze C:\Users\restartRemote.py (需打包文件路径) –target-dir D:\pyproject (存放exe的目标文件夹路径)–base-name=win32gui

使用

安装方法:

  1. 打开cmd,输入pip install cx-freeze 后,会发现在python的Scripts文件夹下生成了三个文件
  2. 然后在cmd中,切换到Scripts路径下,输入 python cxfreeze-postinstall,会发现Scripts文件夹下又多了.bat文件,此时已经安装成功
  3. 继续在cmd下尝试输入 cxfeeeze -h 验证下,显示成功。

提示:直接通过 pip install cx-freeze 安装,仍然出现错误时(找不到文件),最好去官网:网址手动下载相应版本的cx-freeze文件,放在Scripts文件夹下,手动双击安装,然后再运行cxfreeze-postinstall。(例如:我的python版本是3.4.3,下载cx_Freeze-4.3.3.win-amd64-py3.4的文件。注意,版本不对无法安装!)

在cxfreeze的安装目录下即\Python34\Scripts,添加一个文件cxfreeze.bat(可用记事本)cxfreeze.bat内容如下:

@ echo off
C:\Python34\python.exe C:\Python34\Scripts\cxfreeze %*

在该目录下输入czfreeze -h也可以查看安装情况(用pip install cxfreeze就可直接安装改第三方库)

打包方法

打包文件时要在Scripts文件夹路径下操作,文件的路径最好是绝对路径。例如:将 G 盘的 hello.py 打包到 G 盘的 123文件夹下:cxfreeze g:\hello.py –taget-dir g:\123 或者 cxfreeze g:\hello.py –taget-dir g:\123 –base-name=Win32GUI。加上–base-name=Win32GUI可以在运行exe时去掉cmd黑框。

具体操作:

首先在命令提示符中使用cd命令转换到所需要转换的python文件的目录中,因为转换后的dist文件夹会出现在命令提示符的当前目录中

然后输入:

cxfreeze --init-script=C:\Users\tangc\Desktop\alien_invasion\alien_invasion.py alien_invasion.py

--init-script=后接绝对路径

如果你的程序中有图片等资源,是需要根据程序中的相对路径加入到dist文件中,也要在.exe同目录中加入images的文件夹(图片资源)

如果原.py程序使用了图片等素材,并且添加图片是相对路径(同一个文件夹下可以不使用绝对路径),打包文件后,需要将图片等素材拷贝一份放在打包后的文件夹下,否则运行.exe程序会找不到图片素材。

如果原.py程序使用图片添加的是绝对路径,在本地电脑上运行时可能不会发现错误。但是你将打包后的文件发给另一台机器,运行.exe程序会报错,大概意思是“根据你写的绝对路径找不到图片等素材”,即使你在文件夹下拷贝了素材,但是绝对路径也是错误的,所以,最好使用上一种方法。

nuitka

Nuitka直接将python转换成C++代码 ,再编译C++代码产生可执行文件,完全不存在反向解析的问题,非常安全,而且由于可执行文件由C++编译而来,运行速度也会获得提升。

当有一些大型的代码写完后,为了防止反编译源代码,我会使用nuitka进行打包,但是他存在一个很明显的缺点,那就是在另一台电脑上无法运行…并且实际编译时需要近20分钟,编译的时候cpu持续100%

不过如果只是自己的电脑上运行的话,还是挺好使的

官方文档

注意nuitka对python的某些语法有专门要注意的地方

下载地址

也可以通过pip下载,安装完nuitka后

再下载地址的中选择相对的版本号,下载py解释器,这里你可以理解成使用niutka改造后的cpython解释器

然后安装下载相对于版本号的msi文件即可

下载完后会提示安装编译c++代码的软件,使用的是gcc,下载地址是mingw 64

选择操作系统

我直接下载的shimingw-w64,你可以点击这里直接下载win 10 win64

下载后安装

选项如下

等待安装即可

安装好有会有一个MinGW64的文件夹,比如我要打包一个back.py的文件,把这个py文件放在MinGW64目录下,下面是我实验出来的一些经验打包命令行

python -m nuitka --recurse-all --standalone cc.py

python -m nuitka --standalone back.py

# 上面两种是可以用的打包方式
python -m nuitka --recurse-all --recurse-plugins=F:\Python27\Lib --remove-output --plugin-enable=enum_compat back.py
# 带图标并且有窗口
python -m nuitka --recurse-all --recurse-plugins=F:\Python27\Lib --remove-output --icon=logo.ico --plugin-enable=enum_compat back.py
# 不带窗口不带图标
python -m nuitka --recurse-all --recurse-plugins=F:\Python27\Lib --remove-output --plugin-enable=enum_compat --windows-disable-console back.py

# 不删除辅助文件夹
python -m nuitka --recurse-all --recurse-plugins=F:\Python27\Lib --icon=logo.ico --plugin-enable=enum_compat back.py

# 其他可运行


python -m nuitka --recurse-all --recurse-plugins=F:\Python27\Lib --standalone --remove-output --icon=logo.ico --plugin-enable=enum_compat back.py

上面那几条是我去年的时候总结的,使用的是python2,在使用py3更新后,图标需要指定位置

C:\Program Files\mingw-w64\x86_64-8.1.0-win32-seh-rt_v6-rev0>python3 -m nuitka --recurse-all --windows-icon=logo.ico --standalone main.py

具体的变动使用

python3 -m nuitka -h

即可查看使用说明

在编译的时候大概要挺长时间,并且cpu持续暴涨,之前使用锐龙5 1400编译一个程序大概是15分钟,换上9900k后大概一分钟左右就好了。

速度确实快了一些

虚拟环境打包

本机的电脑中python包比较杂乱,而且2和3版本混淆在一起,使用pyinstaller打包的时候想用3的环境但是又默认使用2,而且电脑上的pyinstaller安装其他插件都是2版本的,这种情况下就很糟心,并且有些包是完全不需要不依赖的,这个时候就显得很臃肿,所以使用pipenv安装py3的相关环境,在flask学习过程中,提起到pipenv,虚拟包管理软件进行软件打包处理,具体介绍pipenv 虚拟环境

前提:python目录下的script目录添加到环境变量

首先到py3的script目录下,安装pipenv(如果之前安装了pipenv也不要紧)

pyton3 pip3.6.exe install pipenv

然后创建一段代码,用来打包,随便写一段代码好了

# coding:utf-8
import requests
import os
headers = {'User-Agent':'I AM LANGZI'}
r = requests.get(url='http://www.langzi.fun',headers=headers)
print('状态码:'+str(r.status_code))
print('-**-*-*-*-*-*-*-*-**-*')
print(r.content)
os.system('pause')

这段代码随便取名为a.py

然后创建一个文件夹,为了方便记住,就取名python2exe文件夹,这个文件夹专门用来将py代码打包成exe,把上面的a.py保存到python2exe文件夹下面。

打开cmd,输入pipenv,这个时候我的电脑报错了,提示

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xce in position 4: in....

因为windows默认gbk编码,这个时候要修改源码将utf-8改成gbk就能解决,如果你有其他好的方法欢迎交流。

修改方法

在的C:\python3\Lib\site-packages\pipenv\vendor\shellingham的nt.py中78行,修改成

info = {'executable': str(pe.szExeFile.decode('gbk'))}

然后输入命令检测pipenv是否能成功运行

pipenv check

此时报错:

  File "c:\python3\lib\site-packages\pipenv\vendor\pythonfinder\_vendor\pep514tools\_registry.py", line 75, in __getattr
__
    raise AttributeError(attr)
AttributeError: version

当前目录下会生成pipfile文件,但是内容为空

通过查看pipenv官方文档,需要指定一下虚拟环境的配置信息,编辑pipfile,修改内容为

[[source]]
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
verify_ssl = true
name = "pypi"

[dev-packages]

[packages]
requests = "*"

[requires]
python_version = "3.6"
# 这里指定用生成的3.6版本

然后继续输入

pipenv check

返回结果:

C:\python2exe>pipenv check
Checking PEP 508 requirements…
Passed!
Checking installed package safety…
All good!

到此为止pipenv安装成功

这个时候这个文件夹里面就生成了python3.6的独立虚拟环境,这个时候激活虚拟环境

pipenv shell

然后查看一下当前的虚拟环境信息

pipenv --where # 显示虚拟环境所在目录
pipenv --venv # 显示虚拟环境信息
pipenv --py # 显示python虚拟环境解释器的信息

因为是新建的虚拟环境,所以里面没有任何外部包,就需要先安装,举个例子先安装requests这个包,再python2exe目录下启动cmd,输入

pipenv install requests # 还能指定版本,比如pipenv install requests==20.00

然后查看刚刚安装的包和依赖

pipenv graph

如果要删除就

pipenv uninstall requests # 想要删除所有的外部包就输入 pipenv uninstall --all

要在虚拟环境里安装好你py文件中调用的库,不然打包出来也没法正常运行。

最后安装pyinstaller

pipenv install pyinstaller

这个时候我的电脑报错:

[pipenv.exceptions.InstallError]: ['Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple', 'Collecting pyinstall
er==3.4 (from -r C:\\Users\\浪子\\AppData\\Local\\Temp\\pipenv-u8g559jn-requirements\\pipenv-209rryi9-requirement.txt (l
ine 1))', '  Using cached https://pypi.tuna.tsinghua.edu.cn/packages/03/32/0e0de593f129bf1d1e77eed562496d154ef4460fd5cec
fd78612ef39a0cc/PyInstaller-3.4.tar.gz', '  Installing build dependencies: started', "  Installing build dependencies: f
inished with status 'done'", '  Getting requirements to build wheel: started', "  Getting requirements to build wheel: f
inished with status 'error'", '  Complete output from command c:\\users\\浪子\\.virtualenvs\\python2exe-lqrqjwd-\\script
s\\python3.exe c:\\users\\浪子\\.virtualenvs\\python2exe-lqrqjwd-\\lib\\site-packages\\pip\\_vendor\\pep517\\_in_process
.py get_requires_for_build_wheel C:\\Users\\浪子\\AppData\\Local\\Temp\\tmprp_w934y:', '  Failed to import the site modu
le', '  Traceback (most recent call last):', '    File "c:\\users\\浪子\\.virtualenvs\\python2exe-lqrqjwd-\\lib\\site.py
", line 791, in <module>', '      main()', '    File "c:\\users\\浪子\\.virtualenvs\\python2exe-lqrqjwd-\\lib\\site.py",
 line 781, in main', '      execsitecustomize()', '    File "c:\\users\\浪子\\.virtualenvs\\python2exe-lqrqjwd-\\lib\\si
te.py", line 553, in execsitecustomize', '      import sitecustomize', '    File "C:\\Users\\浪子\\AppData\\Local\\Temp\
\pip-build-env-qfpa92l1\\site\\sitecustomize.py", line 7', "  SyntaxError: (unicode error) 'utf-8' codec can't decode by
te 0xc0 in position 0: invalid start byte", '  ', '  ----------------------------------------']
[pipenv.exceptions.InstallError]: ['Command "c:\\users\\浪子\\.virtualenvs\\python2exe-lqrqjwd-\\scripts\\python3.exe c:
\\users\\浪子\\.virtualenvs\\python2exe-lqrqjwd-\\lib\\site-packages\\pip\\_vendor\\pep517\\_in_process.py get_requires_
for_build_wheel C:\\Users\\浪子\\AppData\\Local\\Temp\\tmprp_w934y" failed with error code 1 in C:\\Users\\浪子\\AppData
\\Local\\Temp\\pip-install-ugo2mgu4\\pyinstaller']
ERROR: ERROR: Package installation failed...
     ================================ 0/1 - 00:00:05

解决方法是下载pyinstaller的按转包,使用虚拟环境的解释器安装到虚拟环境,下载地址

保存到python2exe目录下

解压后,回到cmd窗口

cd PyInstaller-3.4

然后安装

python install.py install

这个时候即可安装成功

安装好了后启动打包文件即可

pyinstaller -F a.py

这个时候会报错

2260 WARNING: lib not found: api-ms-win-crt-stdio-l1-1-0.dll dependency of c:\users\langzi\.virtualenvs\python2exe-lqrqjwd-\scripts\python3.exe
2424 WARNING: lib not found: api-ms-win-crt-math-l1-1-0.dll dependency of c:\users\langzi\.virtualenvs\python2exe-lqrqjwd-\scripts\python3.exe
2588 WARNING: lib not found: api-ms-win-crt-runtime-l1-1-0.dll dependency of c:\users\langzi\.virtualenvs\python2exe-lqrqjwd-\scripts\python3.exe
2753 WARNING: lib not found: api-ms-win-crt-heap-l1-1-0.dll dependency of c:\users\langzi\.virtualenvs\python2exe-lqrqjwd-\scripts\python3.exe
2917 WARNING: lib not found: api-ms-win-crt-locale-l1-1-0.dll dependency of c:\users\langzi\.virtualenvs\python2exe-lqrqjwd-\scripts\python3.exe
3153 WARNING: lib not found: api-ms-win-crt-stdio-l1-1-0.dll dependency of c:\users\langzi\.virtualenvs\python2exe-lqrqjwd-\scripts\python36.dll
3317 WARNING: lib not found: api-ms-win-crt-math-l1-1-0.dll dependency of c:\users\langzi\.virtualenvs\python2exe-lqrqjwd-\scripts\python36.dll
3481 WARNING: lib not found: api-ms-win-crt-conio-l1-1-0.dll dependency of c:\users\langzi\.virtualenvs\python2exe-lqrqjwd-\scripts\python36.dll
3647 WARNING: lib not found: api-ms-win-crt-runtime-l1-1-0.dll dependency of c:\users\langzi\.virtualenvs\python2exe-lqrqjwd-\scripts\python36.dll
3810 WARNING: lib not found: api-ms-win-crt-time-l1-1-0.dll dependency of c:\users\langzi\.virtualenvs\python2exe-lqrqjwd-\scripts\python36.dll
3975 WARNING: lib not found: api-ms-win-crt-heap-l1-1-0.dll dependency of c:\users\langzi\.virtualenvs\python2exe-lqrqjwd-\scripts\python36.dll
4138 WARNING: lib not found: api-ms-win-crt-convert-l1-1-0.dll dependency of c:\users\langzi\.virtualenvs\python2exe-lqrqjwd-\scripts\python36.dll
4304 WARNING: lib not found: api-ms-win-crt-filesystem-l1-1-0.dll dependency of c:\users\langzi\.virtualenvs\python2exe-lqrqjwd-\scripts\python36.dll
4473 WARNING: lib not found: api-ms-win-crt-process-l1-1-0.dll dependency of c:\users\langzi\.virtualenvs\python2exe-lqrqjwd-\scripts\python36.dll
4639 WARNING: lib not found: api-ms-win-crt-locale-l1-1-0.dll dependency of c:\users\langzi\.virtualenvs\python2exe-lqrqjwd-\scripts\python36.dll
4806 WARNING: lib not found: api-ms-win-crt-environment-l1-1-0.dll dependency of c:\users\langzi\.virtualenvs\python2exe-lqrqjwd-\scripts\python36.dll
4975 WARNING: lib not found: api-ms-win-crt-string-l1-1-0.dll dependency of c:\users\langzi\.virtualenvs\python2exe-lqrqjwd-\scripts\python36.dll
5143 WARNING: lib not found: api-ms-win-crt-stdio-l1-1-0.dll dependency of C:\Windows\system32\vcruntime140.dll
5306 WARNING: lib not found: api-ms-win-crt-runtime-l1-1-0.dll dependency of C:\Windows\system32\vcruntime140.dll
5471 WARNING: lib not found: api-ms-win-crt-heap-l1-1-0.dll dependency of C:\Windows\system32\vcruntime140.dll
5635 WARNING: lib not found: api-ms-win-crt-convert-l1-1-0.dll dependency of C:\Windows\system32\vcruntime140.dll
5801 WARNING: lib not found: api-ms-win-crt-string-l1-1-0.dll dependency of C:\Windows\system32\vcruntime140.dll

这个时候需要安装vc++库下载地址

然后修改生成下spec的文件pathex片段

在其中的pathex字段中,添加api-ms-win-crt-*-1-0.dll的文件路径。通常如果安装了VC++运行库的话,其地址为“C:\Program Files\Windows Kits\10\Redist\ucrt\DLLs\x86”(32位操作系统)或“C:\Program Files (x86)\Windows Kits\10\Redist\ucrt\DLLs\x86”(64位操作系统)。此处选用的是VC++的32位版本库文件。

比如:

# -*- mode: python -*-

block_cipher = None


a = Analysis(['a.py'],
             pathex=['C:\\python2exe','C:\Program Files (x86)\Windows Kits\10\Redist\ucrt\DLLs\x86'],
             binaries=[],
             datas=[],
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,
          [],
          name='a',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          runtime_tmpdir=None,
          console=True )

然后输入:

pyinstaller a.spec

就能生成exe文件了。

代码混淆

在线版

你可以自己写混淆器,虽然说是防君子不防小人,但是…..罢了不提了…..

我使用的是一个在线的代码混淆器,猜测代码会上传到那个服务器上面,所以用或不用看你的需求了

地址

注意:代码规范符合python3

Pyarmor

Pyarmor 是一个导入和运行加密 Python 脚本的命令行工具。他支持命令行操纵加密与图形化界面操作。

下载安装

当然首先要安装

pip3 install pyarmor

命令行操作

使用命令行高级命令

单个文件加密打包

首先加密脚本

使用 obfuscate 命令,加密 src 目录下面的所有 .py 文件

pyarmor obfuscate src/hello.py

运行加密脚本,会在目录下生成一个dist文件夹,文件夹里面就是加密后的脚本代码

像运行正常脚本一样运行加密后的脚本 hello.py

cd ./dist/
python hello.py
打包加密脚本

使用 pack 命令,把脚本加密之后直接生成可以单独运行的可执行文件

pyarmor pack src/hello.py

生成不同许可文件

首先使用命令 hdinfo 查看目标机器的硬件设备信息

然后使用命令 licenses, 创建新的许可文件

接着使用新生成的许可文件替换默认许可文件 dist/license.lic

最后再次运行加密脚本 queens.py,测试一下新的许可证

pyarmor hdinfo
pyarmor licenses \
        --expired "2018-12-31" \
        --bind-disk "100304PBN2081SF3NJ5T" \
        --bind-mac "70:f1:a1:23:f0:94" \
        --bind-ipv4 "202.10.2.52" \
        customer-jondy
cp licenses/customer-jondy/license.lic ./dist/
cd ./dist/
python hello.py
封装成exe

虽然加密脚本可以无缝替换原来的脚本,但是打包的时候还是存在一个问题:

加密之后所有的依赖包无法自动获取

解决这个问题的基本思路是

  1. 使用没有加密的脚本找到所有的依赖文件
  2. 使用加密脚本替换原来的脚本
  3. 添加加密脚本需要的运行辅助文件到安装包
  4. 替换主脚本,因为主脚本会被编译成为可执行文件

运行 pyarmor pack 命令进行打包的时候, PyArmor 会进行如下的工作

第一步是加密所有的脚本,保存到 dist/obf:

pyarmor obfuscate --output dist/obf hello.py

第二步是生成 .spec 文件,这是 PyInstaller 需要的,把加密脚本需要的 运行辅助文件也添加到里面:

pyinstaller --add-data dist/obf/license.lic
            --add-data dist/obf/pytransform.key
            --add-data dist/obf/_pytransform.*
            hello.py dist/obf/hello.py

第三步是修改 hello.spec, 在 Analysis 之后插入下面的语句,主要作用是 打包的时候使用加密后的脚本,而不是原来的脚本:

a.scripts[-1] = 'hello', 'dist/obf/hello.py', 'PYSOURCE'
for i in range(len(a.pure)):
    if a.pure[i][1].startswith(a.pathex[0]):
        x = a.pure[i][1].replace(a.pathex[0], os.path.abspath('dist/obf'))
        if os.path.exists(x):
            a.pure[i] = a.pure[i][0], x, a.pure[i][2]

最后运行这个修改过的文件,生成最终的安装包:

pyinstaller hello.spec

检查一下安装包中的脚本是否已经加密:

# It works
dist/hello/hello.exe

rm dist/hello/license.lic

# It should not work
dist/hello/hello.exe

加密包(Package)

从这个例子中,可以学习到

  1. 如何加密一个 Python 包 mypkg,它所在的路径是 examples/testpkg
  2. 如何设置加密包的运行期限
  3. 如何使用外部脚本 main.py 来导入和使用加密后 mypkg 包中的函数
  4. 如何发布加密后的包给用户

使用如下命令:

cd /path/to/pyarmor

# 使用 obfuscate 去加密包,加密后的脚本存放在 `dist/mypkg`
pyarmor obfuscate --output=dist/mypkg examples/testpkg/mypkg/__init__.py

# 使用命令 licenses 生成一个有效期到 2019-01-01 的授权文件
pyarmor licenses --expired 2019-01-01 mypkg2018

# 使用新的授权文件覆盖默认的授权文件
cp licenses/mypkg2018/license.lic dist/mypkg

# 使用第三方脚本 `main.py` 导入加密库
cd dist
cp ../examples/testpkg/main.py ./
python main.py

# 打包整个路径 `mypkg`,发布给客户
zip -r mypkg-obf.zip mypkg
zip 命令是linux下的打包命令,win下直接压缩包即可

使用 Project 来管理和加密脚本

从这个例子中,可以学习到

  1. 如何使用 Project 管理加密脚本
  2. 如何绑定加密脚本到硬盘、网卡等
  3. 如何跨平台发布加密脚本
  4. 如何为不同客户定制授权认证文件

这是一个更接近真实场景的例子,加密后的脚本 queens.py 会以不同的授权方 式发布给不同的客户:

  1. John: 运行在 64位 Ubuntu 上面,2019年5月5号过期,之后就无法在使用
  2. Lily: 运行在一台 64位 Win10 上面,这台机器的硬盘序列号必须是 100304PBN2081SF3NJ5T
  3. Tom: 运行在一台嵌入式设备 Raspberry Pi 上面,网卡Mac地址必须是 70:f1:a1:23:f0:94,并且2019年5月5号过期

使用命令如下:

cd /path/to/pyarmor

# 使用命令 init 创建一个工程
pyarmor init --src=examples/simple --entry=queens.py projects/simple

# 切换到新创建的工程
cd projects/simple

# 这儿自动生成有一个脚本 `pyarmor`,在 Windows 下面名字是 `pyarmor.bat`
# 使用命令 `build` 加密工程中所有的 `.py` 文件,加密脚本存放在 `dist` 下面
pyarmor build

# 生成不同的授权文件
#
# 为 John 生成的限时许可,新的许可文件存放在 "licenses/john/license.lic"
pyarmor licenses --expired 2019-03-05 john

# 为 Lily 生成的硬盘许可,新的许可文件存放在 "licenses/lily/license.lic"
pyarmor licenses --bind-disk '100304PBN2081SF3NJ5T' lily

# 为 Tom 生成的限时和网卡绑定许可,新的许可文件存放在 "licenses/tom/license.lic"
pyarmor licenses --bind-mac '70:f1:a1:23:f0:94' --expired 2019-03-05 tom

# 创建给 John 的发布包
#
mkdir -p customers/john

# 复制所有的加密脚本到新目录
cp -a dist/ customers/john

# 替换默认的许可文件
cp licenses/john/license.lic customers/john/dist

# 替换平台相关的动态链接库,从网站上下载适用 64位 Linux 的版本
rm -f customer/john/dist/_pytransform.*
wget http://pyarmor.dashingsoft.com/downloads/platforms/linux_x86_64/_pytransform.so -O customer/john/dist/_pytransform.so

# 打包在路径 `customer/john/dist` 的所有文件,发布给 John

# 对于 Lily 和 Tom 来说,基本操作都是一样,除了动态链接库需要根据不同的平台分别下载和替换
#
wget http://pyarmor.dashingsoft.com/downloads/platforms/win_amd64/_pytransform.dll
wget http://pyarmor.dashingsoft.com/downloads/platforms/raspberrypi/_pytransform.so

打包加密脚本

从这个例子中,可以学习到

如何使用命令 pack 来打包加密的脚本

PyArmor 需要使用第三方的打包工具,推荐工具是 PyInstaller, 首先安装

pip install pyinstaller

接着就可以运行命令 pack 打包加密脚本

cd /path/to/pyarmor
pyarmor pack examples/py2exe/hello.py

运行一下打包好的可执行文件

dist/hello/hello

确认脚本已经加密

rm dist/hello/license.lic
dist/hello/hello

使用其他工具需要先写一个安装脚本setup.py, 这儿有一个 py2exe 的实 例 examples/py2exe/setup.py。这个脚本用来打 包主脚本hello.py 和另外一个模块 queens.py。

首先安装 py2exe,并确定没有加密之前能够正常打包

pip install py2exe

cd /path/to/pyarmor

cd example/py2exe
python setup.py py2exe

# 输出文件在这里
ls dist/

之后运行命令 pack 来打包加密脚本

pyarmor pack --type py2exe hello.py

检查一下输出路径 dist,发现多个几个文件,这些是运行加密脚本需要的辅助 文件,另外 library.zip 也被修改了,里面的 queens.pyc 被替换成为了加 密后的脚本

对于其他打包工具 cx_Freeze, py2app, 基本使用方法和 py2exe 很类似。

图形化操作

下载

可以通过

pip3 install pyarmor

或者你也可以单独下载:下载地址

解压下载文件到任意目录,例如 /opt。本教程后面的章节都是以 /opt 作为 WebApp 的安装路径,运行里面的例子需要把它替换成为实际的安装路径。

启动

可以通过命令行

pyarmor-webui

还可以通过如下方式进行设置后启动

打开文件夹 /opt/pyarmor/webapp,双击 start-server.bat for Windows 或者 start-server.sh for Linux

也可以通过命令行方式启动,假定 python 的安装路径为 D:/tools/Python27

cd /opt/pyarmor/webapp
D:/tools/Python27/python server.py

这条命令会打开一个控制台,显示服务器的行为动态,同时本机默认的浏览器会自动打开一个网页,这就是 WebApp。

单击 项目, 进入 Pyarmor 的世界

我们下面的操作主要就是在这个网页里面进行的,另外还需要打开一个命令窗口执行相关的 Python 命令。

演示模式

如果在项目页面的右上角有个 演示版本 的按钮,那就是说,WebApp 现在只能演示功能。通常情况下,有两种方式会进入演示模块

  1. Pyarmor WebApp 官方在线版
  2. 直接在文件夹打开 /opt/pyarmor/webapp/index-zh.html

使用方法

下面通过四个例子来演示 WebApp 的主要功能:

  1. 第一个例子最简单,运行一个加密的脚本
  2. 第二个稍微复杂一些,演示如何导入加密的模块和包(Package)
  3. 第三个例子主要是演示如何绑定加密脚本到指定机器
  4. 最后一个例子演示如何设置加密脚本的有效期

运行加密脚本

在这个例子中,将会加密 /opt/pyarmor/src/examples/queens.py,然后运行加密后的脚本。

案例 1

进行单个加密

  • 按照下图输入 标题, 源路径, 启动脚本, 输出路径
  • 点击按钮 加密

加密成功之后在目录 /opt/pyarmor/webapp/build 下面会有加密脚本 queens.py 以及相关的辅助文件。

运行

在命令窗口执行里面的命令

# 进入输出目录
cd /opt/pyarmor/webapp/build

# 执行包裹脚本,并且传入命令行参数: "6"
python queens.py 6

查看加密后的 queens.py,它的内容如下

import pyimcore
__pyarmor__(__name__, b'xxxxx')

最后点击按钮 保存,保存项目信息,后面的例子中还会用到这个项目。

案例 2

导入加密模块

第二个例子稍微复杂一些,使用一个真实的包 /opt/pyarmor/src/examples/pybench 。这是 一个用来测试 Python 实现性能的小工具包,用在这里最合适不过了。

在这个例子中,将会加密 pybench 中的所有 “.py” 文件,然后运行脚本 pybench.py,它可以像正常方式一样,使用导入后的加密模块。

加密

  • 点击按钮 新建,创建一个新的项目
  • 按照下图内容输入 标题, 源路径, 输出路径, MANIFEST.in

关于 MANIFEST.in 的格式,参考这里

这个模板完全参照 Python Distutils 里面相应的文件,它用来列出项目中需要加密的文件。例如

include *.py
recursive-include examples *.py

上面的例子一看就能明白: 所有在项目 源路径 下面的 py 文件,递归所有在项目子目录 examples 下面的 py 文件。

模板支持的命令如下表所示(从 Python 帮助文件拷贝而来)

              Command                             Description
---------------------------------------------------------------------------------------------------------------------------
include pat1 pat2 ...                include all files matching any of the listed patterns
exclude pat1 pat2 ...                exclude all files matching any of the listed patterns
recursive-include dir pat1 pat2 ...  include all files under dir matching any of the listed patterns
recursive-exclude dir pat1 pat2 ...  exclude all files under dir matching any of the listed patterns
global-include pat1 pat2 ...         include all files anywhere in the source tree matching — & any of the listed patterns
global-exclude pat1 pat2 ...         exclude all files anywhere in the source tree matching — & any of the listed patterns
prune dir                            exclude all files under dir
graft dir                            include all files under dir

需要注意的是在 Pyarmor WebApp 中,MANIFEST.in 里面的路径都是相对于项目 源路径,并且不支持绝对路径。

  • 点击按钮 保存,保存修改后的项目
  • 点击按钮 加密

所有加密后的脚本存放在 输出路径 /opt/pyarmor/webapp/build/pybench 下面

运行

运行加密脚本 pybench.py

cd /opt/pyarmor/webapp/build/pybench
python pybench.py

查看加密脚本 pybench.py

import pyimcore
__pyarmor__(__name__, b'xxx')
案例 3

绑定加密脚本到指定机器

这个例子演示如何绑定加密脚本到当前机器,在之前首先要得到本机的硬盘序列号。Pyarmor 提供了一个命令可以直接获取本机的硬盘序列号,打开一个命令行窗口,执行下面的命令

cd /opt/pyarmor/src
python pyarmor.py hdinfo
Harddisk's serial number is '100304PBN2081SF3NJ5T'

生成许可证

我们继续使用第一个例子中的项目 Queen,加密后的脚本已经存放在 /opt/pyarmor/webapp/build,可以直接在这个例子里面使用。

  • 点击按钮 打开,弹出项目列表对话框
  • 在列出的项目中选中 project-1:Queen,然后点击对话框下方按钮 打开

  • 点击左侧 许可证
  • 选中 绑定加密脚本到指定机器,在下面的文本框输入 100304PBN2081SF3NJ5T

  • 点击下面的按钮 生成

如上图所示,新的许可文件会显示在 所有许可方式 的列表中,上图中例子是 Bind to 100304PBN2081SF3NJ5T (projects\project-1\license-0.lic)

小括号里面的内容是许可证的文件名称,加上安装路径,全路径的文件名称是 /opt/pyarmor/webapp/projects/project-1/license-0.lic

使用新的许可证

使用新的许可证只需要使用新的许可证替换默认许可证 /opt/pyarmor/webapp/build/license.lic 即可

cp /opt/pyarmor/webapp/projects/project-1/license-0.lic /opt/pyarmor/webapp/build/license.lic

打开命令行窗口,再次运行

cd /opt/pyarmor/webapp/build
python queens.py 6

在本机可以正常运行,但是在其他机子上,就会出错。

设置加密脚本的有效期

这个例子演示如何设置加密脚本的有效期,我们继续上面的操作。

  • 点击左侧 许可证
  • 选中 设置加密脚本的有效期,在下面的文本框输入 2017-12-25

  • 点击下面的按钮 生成

和上面的例子一样,新的许可文件会显示在 所有许可方式 的列表中,用新的许可文件替换旧的即可。

打开命令窗口,再次运行

cd /opt/pyarmor/webapp/build
python queens.py 6

这个加密脚本只能在本机运行到2071年的圣诞节。在圣诞节之后,就会出错。

所有环境打包下载

为了方便迁移环境,所以将上文中的pyinstaller等打包环境下载后保存在一个压缩包里面,方便下载使用

下载链接

参考链接 1

参考链接 2

参考链接 3

坚持原创技术分享,您的支持将鼓励我继续创作!
------ 本文结束 ------

版权声明

LangZi_Blog's by Jy Xie is licensed under a Creative Commons BY-NC-ND 4.0 International License
由浪子LangZi创作并维护的Langzi_Blog's博客采用创作共用保留署名-非商业-禁止演绎4.0国际许可证
本文首发于Langzi_Blog's 博客( http://langzi.fun ),版权所有,侵权必究。

0%