.pyc
文件,这个文件是可以被反编译回近似的源代码的。通过使用 Cython 将 Python 源代码编译成 C 语言,然后再生成本地二进制文件(.pyd)。然后正常使用 cx_Freeze 打包,这样做可以极大地提高代码的保护级别,防止被轻易逆向。
需要安装 Cython 和 Numpy,在终端或命令行中运行:
pip install Cython numpy
Cython 将 Python 代码转换成 C 代码,但最终需要一个 C 编译器来将 C 代码编译成机器码。这是最关键的一步。
对于 Windows 用户:
对于 macOS 用户:
打开终端并运行 xcode-select --install
。这会安装苹果的命令行开发者工具,其中包含了 Clang 编译器。
对于 Linux 用户 (例如 Ubuntu/Debian):
打开终端并运行 sudo apt update && sudo apt install build-essential
。
如果项目目录结构如下:
/my_project
|-- main.py # 你的主程序文件
|-- /src # 你的其他模块目录
| |-- func1.py
| |-- func2.py
|-- setup.py # cx_Freeze 的配置文件
根据以上目录结构,下面是一个配置文件示例:
from cx_Freeze import setup, Executable
import sys, os, io
# =============================================================================
# Cython 自动化编译集成
# =============================================================================
try:
from Cython.Build import cythonize
from setuptools import Extension
import numpy
except ImportError:
print("\n[错误] 缺少必要的库。请先安装 Cython 和 Numpy:")
print("pip install Cython numpy")
sys.exit(1)
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') # 修复非中文系统打包时报错
# 增加递归调用深度限制
sys.setrecursionlimit(1500)
# 定义相关路径
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
ENTRY_POINT = "main.py"
# 检查是否为打包命令
build_commands = {"build", "bdist_msi", "bdist_dmg", "bdist_mac"}
is_building = any(cmd in sys.argv for cmd in build_commands)
# --- Cython 编译配置 ---
# 此函数会自动查找 src 目录下的所有 .py 文件并准备将它们编译
def find_extensions_to_compile(dir_path="src"):
"""Find all .py files to be compiled by Cython."""
extensions = []
# 添加 numpy 的头文件路径,这对于编译依赖 numpy/scipy 的代码至关重要
numpy_include = numpy.get_include()
for root, dirs, files in os.walk(dir_path):
for file in files:
# 我们只编译 .py 文件,但跳过 __init__.py 文件
if file.endswith(".py") and file != "__init__.py":
path = os.path.join(root, file)
# 将文件路径转换为模块路径,例如 "src/library/function.py" -> "src.library.function"
module_path = path.replace(os.sep, '.')[:-3]
extensions.append(
Extension(
name=module_path,
sources=[path],
include_dirs=[numpy_include] # 包含 numpy 头文件
)
)
print(f"--- 找到 {len(extensions)} 个模块准备通过 Cython 编译...")
return extensions
# 仅在执行打包命令时才准备编译列表
extensions = []
if is_building:
# 1. 编译 src 目录下的所有模块
extensions.extend(find_extensions_to_compile("src"))
# 2. 明确地将 main.py 添加到编译列表
print(f"--- 添加入口文件 '{ENTRY_POINT}' 到编译列表...")
main_extension = Extension(
name="main", # 编译后的模块名
sources=[ENTRY_POINT],
include_dirs=[numpy.get_include()]
)
extensions.append(main_extension)
# =============================================================================
# cx_Freeze 配置
# =============================================================================
# 安装依赖
build_exe_options = {
"packages": [
],
"excludes": ["email"] + [ext.name for ext in extensions], # 排除 Cython 编译的模块
"include_files": [
],
"includes": [],
# 性能优化选项
"optimize": 2, # 使用Python优化
"include_msvcr": False, # 不包含MSVC运行库
}
# 基础设置
base = "Win32GUI" if sys.platform == "win32" else None
directory_table = [
# ...
]
shortcut_table = [
(
# ...
),
(
# ...
),
]
msi_data = {"Directory": directory_table, "Shortcut": shortcut_table}
bdist_msi_options = {
# ...
}
executables = [
Executable(
"main.py", # 入口文件 依然调用 py 程序,cx_Freeze 会自动识别并使用加密后的文件
# ...
)
]
# =============================================================================
# 清理函数
# =============================================================================
def cleanup_generated_files():
"""查找并删除由 Cython 生成的所有 .c 文件。"""
print("\n--- 正在运行清理程序:删除生成的 C 文件... ---")
for root, dirs, files in os.walk(ROOT_DIR):
# 避免进入不相关的目录
if 'myenv' in root or '.git' in root or 'build' in root or 'dist' in root:
continue
for file in files:
if file.endswith('.c'):
file_path = os.path.join(root, file)
try:
os.remove(file_path)
print(f"--- 已删除: {file_path}")
except OSError as e:
print(f"--- 删除失败 {file_path}: {e}")
# =============================================================================
# 执行打包
# =============================================================================
try:
setup(
# ...
# 关键步骤:将找到的 .py 文件交给 Cythonize 进行编译
ext_modules=cythonize(
extensions,
compiler_directives={'language_level': "3"}, # 使用 Python 3 语法
quiet=True # 减少不必要的编译输出
) if is_building else [],
# ...
)
finally:
# 只有在执行打包命令时才运行清理
if is_building:
cleanup_generated_files()
运行打包命令打包即可,如:
python setup.py bdist_msi
安装完成后,进入安装路径的 Lib/site-packages 文件夹,会看到加密后的 .pyd
程序文件。.pyd
文件是 Windows 上的二进制动态链接库,本质上和 .dll 文件一样。如果加密失败:会在这里看到 .pyc
文件或者甚至原始的 .py
文件。
Python 3.3 及以上版本自带 venv 模块,可以直接使用。
在你的项目目录下,运行以下命令来创建一个虚拟环境,这将在项目目录下创建一个名为 myenv 的文件夹,其中包含虚拟环境的所有文件:
python -m venv myenv
Windows:
myenv\Scripts\activate
macOS 和 Linux:
source myenv/bin/activate
在虚拟环境中,你可以使用 pip 来安装项目所需的库:
pip install requests
安装的库将只会影响当前虚拟环境,而不会影响系统的 Python 环境或其他项目。
为了确保你的项目依赖可以在其他环境中重现,你可以使用以下命令将当前环境的依赖写入 requirements.txt 文件:
pip freeze > requirements.txt
requirements.txt 文件将包含所有当前环境中的安装包及其版本信息。
在新的环境中,你可以使用 requirements.txt 文件来安装所需的所有依赖:
pip install -r requirements.txt
当你完成工作时,可以通过以下命令退出虚拟环境:
deactivate
使用虚拟环境进行Python开发可以有效地隔离项目依赖,避免版本冲突。通过创建和激活虚拟环境、安装依赖、冻结依赖并在新环境中重新安装依赖,可以确保你的项目在不同环境中具有一致的运行表现。
]]>python 官网:https://www.python.org/
当前最新版是 3.8.5,在这个页面找到地址:https://www.python.org/downloads/release/python-385/
下载 tgz 压缩包到本地并解压:
cd /tmp
wget https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tgz
tar xvf Python-3.8.5.tgz
编译需要安装一些依赖:
apt install libffi-dev libgdbm-dev libsqlite3-dev libssl-dev zlib1g-dev
python 源码使用标准 GNU 编译系统,详细说明参考:https://blog.niekun.net/archives/883.html
将 python 安装到 /opt
目录,先创建文件夹:
mkdir /opt/python3.8.5
然后配置 configure:
cd /tmp/Python-3.8.5
./configure \
--prefix=/opt/python3.8.5 \
--enable-optimizations \
没有错误提示的话就开始编译和安装:
make
make install
安装完成后测试执行:
/opt/python3.8.5/bin/python3 --version
返回版本信息则安装完成。
下面将可执行文件加入系统路径,创建软连接:
ln -s /opt/python3.8.5/bin/python3 /usr/bin/python
测试运行:
python --version
源码编译安装的 python 不自带 pip,需要自己安装,可以使用 get-pip.py 脚本来安装。
官网:https://pip.pypa.io/en/stable/installing/
下载脚本到本地:
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
使用刚才安装的 python 执行脚本:
/opt/python3.8.5/bin/python3 get-pip.py
pip 的安装路径是 /opt/python3.8.5/bin/
,测试命令:
/opt/python3.8.5/bin/pip3 --version
返回版本信息则安装完成。
添加软连接到系统路径:
ln -s /opt/python3.8.5/bin/pip3 /usr/bin/pip
测试命令:
pip --version
subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False)
执行cmd命令并返回结果到字符串。
用法:
import subprocess
output = check_output(["cat", "/etc/hostname"]).strip()
print(output)
以上脚本会执行 cat /etc/hostname
命令然后将结果赋值给 output
变量。strip()
可以将 string 的前后空格去掉。
>>> 'foo'.encode('utf_8')
b'foo'
To transform a byte string to a unicode string:
>>> b'foo'.decode('utf_8')
'foo'
To convert a string to bytes.
data = "" #string
data = "".encode() #bytes
data = b"" #bytes
To convert bytes to a String.
data = b"" #bytes
data = b"".decode() #string
data = str(b"") #string
import winreg
REG_PATH = r"Control Panel\Mouse"
def set_reg(name, value):
try:
winreg.CreateKey(winreg.HKEY_CURRENT_USER, REG_PATH)
registry_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, REG_PATH, 0,
winreg.KEY_WRITE)
winreg.SetValueEx(registry_key, name, 0, winreg.REG_SZ, value)
winreg.CloseKey(registry_key)
return True
except WindowsError:
return False
def get_reg(name):
try:
registry_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, REG_PATH, 0,
winreg.KEY_READ)
value, regtype = winreg.QueryValueEx(registry_key, name)
winreg.CloseKey(registry_key)
return value
except WindowsError:
return None
#Example MouseSensitivity
#Read value
print (get_reg('MouseSensitivity'))
#Set Value 1/20 (will just write the value to reg, the changed mouse val requires a win re-log to apply*)
set_reg('MouseSensitivity', str(10))
#*For instant apply of SystemParameters like the mouse speed on-write, you can use win32gui/SPI
#http://docs.activestate.com/activepython/3.4/pywin32/win32gui__SystemParametersInfo_meth.html
]]>Bad Request: BUTTON_DATA_INVALID
反复检查代码没有发现语法错误,查找之后了解到 InlineKeyboardButton 响应后返回的 Callback Data 有大小限制,最大64位:
的确我想返回的内容长度的确超过了大小限制,优化源码后问题解决了。
参考链接:
https://core.telegram.org/bots/api#inlinekeyboardbutton
https://stackoverflow.com/questions/46389040/inlinekeyboardbutton-are-limited-in-inlinekeyboardmarkup-telegram-bot-c-sharp
可以用来运行终端命令:
import os
os.system('date')
-----------------------
The current date is: 10/17/2019 Thu
用来将匹配的文件放入数组:
import glob
import os
CWD = os.getcwd()#当前目录路径
for name in glob.glob(CWD+'/*'):
print(name)
以上输出当前目录下所有文件的文件名。
for name in glob.glob(CWD+'/file?.txt'):
print(name)
以上输出 filea.txt, fileb.txt filec.txt 等文件名。
可以使用类似正则表达式的方式匹配文件名:
glob.glob(CWD+'/*[12].*')
从python3.5开始,支持使用一下方法进行递归搜索目录内文件及文件夹:
for name in glob.glob(CWD+'/**/*', recursive=True):
print(name)
以上会输出目录内文件及子文件夹内文件。
字符串分割:
按空格分割,注意两个部分之间的空格可以是1个或多个,不影响分割效果:
txt = "welcome to the jungle"
x = txt.split()
将 txt 字符串按空格来分成4个部分,x 是数组。
分割成设定的个数:
txt = "welcome to the jungle"
x = txt.split(' ', 1)
输出结果:x = ['welcome', 'to the jungle']
按特定字符分割:
txt = "apple#banana#cherry#orange"
x = txt.split("#", 1)
输出结果:x = ['apple', 'banana#cherry#orange']
用于 iterator 的顺序提取。
mylist = iter(["apple", "banana", "cherry"])
x = next(mylist)
print(x)
y = next(mylist)
print(x)
z = next(mylist)
print(x)
输出结果:x = 'apple' y = 'banana' z = 'cherry'
用于字符串内的赋值:
print ("{}, A computer science portal for geeks".format("GeeksforGeeks"))
输出:GeeksforGeeks, A computer science portal for geeks
多个输入参数:
print ("Hi ! My name is {} and I am {} years old"
.format("User", 19))
带索引的多参数输入:
print("Every {3} should know the use of {2} {1} programming and {0}"
.format("programmer", "Open", "Source", "Operating Systems"))
输出结果:Every Operating Systems should know the use of Source Open programming and programmer
删除字符串前和后的空格:
txt = " banana "
x = txt.strip()
输出:x = 'banana'
删除字符串前和后的自定义字符:
txt = ",,,,,rrttgg.....banana....rrr"
x = txt.strip(",.grt")
输出: x = 'banana'
]]>