“乔治是世界上最后一只平塔岛象龟,而我是世界上最后一个源家后裔,最后一只平塔岛象龟应该为了种族不灭努力地繁殖后代,最后一个源家后裔应该重振家族在黑道中的威望,但是乔治只是想回自己的水坑里去打滚,而我只是想去天体海滩上卖防晒油。”源稚生盯着恺撒的眼睛,“我就是这种人,其实蛇岐八家的黑道事业和秘党的使命对我来说都不重要,我的人生理想就是去卖防晒油。我跟你叔叔不是一种人。”
Python作为解释型语言,发布即公开源码,虽然是提倡开源但是有些时候就是忍不住想打包成exe,不仅仅是为了对代码进行加密,而是为了跨平台。防止有些没有安装py环境的电脑无法运行软件。
目录对python代码打包成exe的方式有4种,py2exe,pyinstaller,cx_Freeze,nuitka,分别对以上4种方法介绍,欢迎纠错和提供新的办法。
py2exe
介绍
py2exe是一个将python脚本转换成windows上的可独立执行的可执行程序(*.exe)的工具,这样,你就可以不用装python而在windows系统上运行这个可执行程序。
优点:
- 可以把python打包成exe
缺点:
打包好的 exe只能在相同的系统下运行,比如你在XP系统用py2exe打包好的exe只能在XP上运行,在win7上肯定有问题,而且也是存在在别的XP机子上也可能不能运行,因为一些别的机子可能会缺少dll文件什么的,作者我当时之所以抛弃py2exe 也是因为这个原因;
打包的文件也需要打包同exe下的dll,和一些库的压缩包,不然exe根本跑不起来
打包时候报始终报RuntimeError: BeginUpdateResource错误,就需要拷贝dll文件。C:\WINDOWS\system32\python27.dll复制到dist目录
总之毛病一大堆,基本上已经舍弃了
使用
下载安装 下载地址
安装好后,准备进行打包
准备一个文件x.py,这个x.py就是想要打包成exe。
然后这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")] }]#源文件,程序图标
)
然后这cmd窗口下运行:
python setup.py py2exe
py2exe使用方法 (含一些调试技巧,如压缩email 类)
pyinstaller
介绍
PyInstaller除了win32平台外还支持Linux,Unix平台.py2exe的用法在前面的博文里已经有了,现在看看PyInstaller,pyinstaller是目前应用最多的python打包工具,也是我最常用的。
PyInstaller本身并不是Python模块,所以安装时随便放在哪儿都行。
优点:
- 可将python文件转成可执行文件
- 跨平台
- 输出的可以是单一目录,也可以是一个单独的打好包的可执行文件。py2exe貌似输出目录。
- 智能支持python的第三方模块如PyQt,外部数据文件等
- 支持EGG格式文件
- 可执行文件可以用UPX压缩,二进制压缩方式
- 支持控制台和视窗两种方式。
- 可以选择exe文件的图标 (Windows only)
- 支持 COM server (Windows only)
缺点:
- 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选项清除掉),再次运行,现在就没有这个问题了。
- 多进程打包的问题
官方的CPython存在一个GIL锁,这个锁的存在有很多优点,很多库都是线程安全的,单线程执行的效率也高。在python早期的一个版本中取消掉了GIL,代之以高粒度的锁来实现多线程,但是实际应用中单个线程的效率大大降低。故后来又将GIL这个锁还原回去,所以至今的python2也好还是python3中都会有这个锁。但是这个锁有很大一个问题,那就是效率问题,它导致了python仅仅只能利用一个core来进行数据的计算。所以后面为了弥补这个GIL带来的问题,专家们设计了multiprocessing库,gevent库等。前一个是多进程库,为了解决python用于数据密集型处理的情况;后一个用于异步IO处理的情况,基本原理就是在CPU时钟之间来回切换,简单的例子就是爬虫程序爬取网页的时候。假如有10个url,我们都要去GET它,实际上网络之间的延迟是大大高于计算机内部的,那么这个时间内计算机就切换到下一个。
有时候运用多进程是必须的,这个替代不了,哪怕它占用资源很多。
使用
- 首先安装
一般情况下:
pip install pyinstaller
但是会爆错误,比如命令行下出现
UnicodeDecodeError: 'gbk' codec can't decode pyinstaller等等
这个时候下载pyinstall按转包文件解压后,放在没有中文的目录下,输入
python setup.py install
即可完成
- 首先写一个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
使用
安装方法:
- 打开cmd,输入pip install cx-freeze 后,会发现在python的Scripts文件夹下生成了三个文件
- 然后在cmd中,切换到Scripts路径下,输入 python cxfreeze-postinstall,会发现Scripts文件夹下又多了.bat文件,此时已经安装成功
- 继续在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='https://sxadmin.github.io',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
虽然加密脚本可以无缝替换原来的脚本,但是打包的时候还是存在一个问题:
加密之后所有的依赖包无法自动获取
解决这个问题的基本思路是
- 使用没有加密的脚本找到所有的依赖文件
- 使用加密脚本替换原来的脚本
- 添加加密脚本需要的运行辅助文件到安装包
- 替换主脚本,因为主脚本会被编译成为可执行文件
运行 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)
从这个例子中,可以学习到
- 如何加密一个 Python 包 mypkg,它所在的路径是 examples/testpkg
- 如何设置加密包的运行期限
- 如何使用外部脚本 main.py 来导入和使用加密后 mypkg 包中的函数
- 如何发布加密后的包给用户
使用如下命令:
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 来管理和加密脚本
从这个例子中,可以学习到
- 如何使用 Project 管理加密脚本
- 如何绑定加密脚本到硬盘、网卡等
- 如何跨平台发布加密脚本
- 如何为不同客户定制授权认证文件
这是一个更接近真实场景的例子,加密后的脚本 queens.py 会以不同的授权方 式发布给不同的客户:
- John: 运行在 64位 Ubuntu 上面,2019年5月5号过期,之后就无法在使用
- Lily: 运行在一台 64位 Win10 上面,这台机器的硬盘序列号必须是 100304PBN2081SF3NJ5T
- 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 现在只能演示功能。通常情况下,有两种方式会进入演示模块
- Pyarmor WebApp 官方在线版
- 直接在文件夹打开 /opt/pyarmor/webapp/index-zh.html
使用方法
下面通过四个例子来演示 WebApp 的主要功能:
- 第一个例子最简单,运行一个加密的脚本
- 第二个稍微复杂一些,演示如何导入加密的模块和包(Package)
- 第三个例子主要是演示如何绑定加密脚本到指定机器
- 最后一个例子演示如何设置加密脚本的有效期
运行加密脚本
在这个例子中,将会加密 /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等打包环境下载后保存在一个压缩包里面,方便下载使用