下载离线包及依赖包并按依赖关系生成安装批处理
应用场景:单位的电脑是局域网不能连外网,想要用python还必要通过离线的方式安装各种各样的包,把各种离线文件带过去还需要一个一个install,而且如果依赖关系没有弄清楚还不能一次到位。虽然能用freeze导出requierment的方式批量安装,但是实际操作上会有中途失败、而且有不想要的包的情况。对此自己想着开发一款自己想要什么包就下载什么样的离线文件,同时用一个批处理就能事项一次性install所有依赖包的程序。所以就编写了本代码。实现原理:根据录入情况离线下载包文件,再通过pip show命令逐个包检查依赖关系,并按依赖深度从深到浅的顺序形成列表,再将列表的包名和离线文件的包名进行匹配,把离线文件的顺序按照依赖关系由深到浅写入批处理文件install.bat。
可以进一步改进的地方:就是递归调用pip show的时候会有阻塞感,但是不懂如何在递归中应用多线程,有大佬的可以提供建议。离线文件名和包名可能会有特殊情况发生会有暴雷的潜在危机。
代码如下:
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
packages = requeiresString.replace(' ', '').split(',')
#递归查找依赖关系,形成(包名,层次)的列表packageList
#统一包名的下划线和横杠的设置,避免包名和文件名存在不一致的情况
if packages=='':
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, reverse=True)
print(packageList)
#根据依赖包列表按依赖关系顺序匹配离线文件,并构造批处理文件
packageFileList = os.listdir(downPath)
BuildBatFileFromList(packageList,packageFileList,downPath) 本帖最后由 罗萨 于 2023-10-6 21:59 编辑
ccber 发表于 2023-10-6 18:46
不能。内部网管策略比较严,同时,内网文件夹贡献是不是也建立在有离线文件的基础上?
没有啊,相当于把东西存在内网一台电脑上,要的时候,从那台电脑拖过来就行了
这个内网共享有啥管的,再不行,内网找台电脑,做个 iis, 开网站就能直接内网下载
那你这软件,还不是相当于下载
你这都内网插U盘设备了,共享有啥问题 lookfeiji 发表于 2023-10-6 12:16
递归加多线程。。。。不得绕晕啊,好像蛮有用的,学习一下
因为我这个程序中的递归要用到pip show命令是需要联网的,会有较大的阻塞。如果能有多线程可能会快一些,但是会有你说的绕晕的。 试试venv? 贝木尼舟 发表于 2023-10-6 10:59
试试venv?
先在venu安装包,再freeze,接着再下载离线包么? 不错!!!!!!!!!!!! 递归加多线程。。。。不得绕晕啊,好像蛮有用的,学习一下 ccber 发表于 2023-10-6 11:06
先在venu安装包,再freeze,接着再下载离线包么?
venv里面的包都是项目本身要用的包,直接打包整个文件夹就行,不用在安装了 不能在内网某台电脑做内网文件夹共享吗? 我之前遇到的问题大概相同,不过我当时用了无线网卡临时给连网解决了。:Dweeqw xaibin 发表于 2023-10-6 16:00
我之前遇到的问题大概相同,不过我当时用了无线网卡临时给连网解决了。
{:1_927:}我们内部有网管软件不能接无线网卡。
页:
[1]
2