应用场景:单位的电脑是局域网不能连外网,想要用python还必要通过离线的方式安装各种各样的包,把各种离线文件带过去还需要一个一个install,而且如果依赖关系没有弄清楚还不能一次到位。虽然能用freeze导出requierment的方式批量安装,但是实际操作上会有中途失败、而且有不想要的包的情况。对此自己想着开发一款自己想要什么包就下载什么样的离线文件,同时用一个批处理就能事项一次性install所有依赖包的程序。所以就编写了本代码。
实现原理:根据录入情况离线下载包文件,再通过pip show命令逐个包检查依赖关系,并按依赖深度从深到浅的顺序形成列表,再将列表的包名和离线文件的包名进行匹配,把离线文件的顺序按照依赖关系由深到浅写入批处理文件install.bat。
可以进一步改进的地方:就是递归调用pip show的时候会有阻塞感,但是不懂如何在递归中应用多线程,有大佬的可以提供建议。离线文件名和包名可能会有特殊情况发生会有暴雷的潜在危机。
代码如下:
[Python] 纯文本查看 复制代码 import os
import re
import subprocess
#根据你要下载的包名(packageName),利用pip show命令,递归查找依赖关系,并形成(包名,层次)的列表packageList
def GetDepPackage(packageName,packageList,deepth):
#执行pip show 命令
result = subprocess.run("pip show "+packageName, capture_output=True, text=True).stdout
#根据输出结果截取出requires字段,并将依赖包存到packages列表
beginString="Requires:"
beginPos=result.find(beginString)+len(beginString)
endString="\n"
requeiresString=result[beginPos:result.find(endString,beginPos)]
packages = requeiresString.replace(' ', '').split(',')
#递归查找依赖关系,形成(包名,层次)的列表packageList
#统一包名的下划线和横杠的设置,避免包名和文件名存在不一致的情况
if packages[0]=='':
packageName=packageName.replace("_","-")
packageList.append((packageName,deepth))
else:
for perPackage in packages:
GetDepPackage(perPackage,packageList,deepth+1)
packageName=packageName.replace("_", "-")
packageList.append((packageName,deepth))
#构建install.bat文件
def BuildBatFileFromList(packageList,packageFileList,downPath):
#设置install.bat文件名
batFileName=os.path.join(downPath,"install.bat")
#根据依赖关系的包列表packageList,匹配出依赖先后顺序的离线文件,按依赖先后为顺序写入bat批处理
for perPackageList in packageList:
for index,perFileName in enumerate(packageFileList):
perRepalcedFileName=perFileName.replace("_","-")
packageName,deepth=perPackageList
pattern = r"-\d+."
matched = re.search(pattern, perRepalcedFileName)
perRepalcedFileName=perRepalcedFileName[:matched.start()]
if perRepalcedFileName==packageName:
with open(batFileName,"a") as batFile:
batFile.write("pip install "+perFileName+"\n")
packageFileList.pop(index)
break
#如果存在离线包文件没能和包名匹配的上,那么将这些离线文件放置到bat批处理的头部。
for index,perFileName in enumerate(packageFileList):
with open(batFileName, "r+") as batFile:
content=batFile.read()
content="pip install " + perFileName + "\n"+content
batFile.seek(0)
batFile.write(content)
print(batFileName,"已生成:")
#输出install.bat的内容
with open(batFileName, "r") as batFile:
content = batFile.read()
print(content)
if __name__ == '__main__':
while True:
globalPackageName=input("请输入你要下载的包名:")
#创建离线安装包的文件夹和下载离线文件
downPath = os.path.join(u"D:\\测试\\", globalPackageName)
downPathIsExistsed=False
if os.path.exists(downPath):
downPathIsExistsed = True
command = "pip download " + globalPackageName + " -i https://pypi.tuna.tsinghua.edu.cn/simple/ -d " + downPath
print("开始执行下载"+globalPackageName+"命令,需要一定的下载时间:\n"+command)
result = subprocess.run(command, capture_output=True, text=True,encoding="utf-8").stdout
print(result)
if result.find("Successfully")==-1:
print("下载"+globalPackageName+"出现错误")
if downPathIsExistsed==False:
os.remove(downPath)
continue
#根据指定的包,创建依赖包列表
packageList = []
GetDepPackage(globalPackageName,packageList,0)
#对依赖包列表根据被依赖关系进行前后排序
packageList.sort(key=lambda x: x[1], reverse=True)
print(packageList)
#根据依赖包列表按依赖关系顺序匹配离线文件,并构造批处理文件
packageFileList = os.listdir(downPath)
BuildBatFileFromList(packageList,packageFileList,downPath) |