[Python 세 가지 패키징 방법] 프로젝트 패키징을 구현하기 위해 PyInstaller 및 setupTools 및 복사 환경 사용

[Python 세 가지 패키징 방법] 프로젝트 패키징을 구현하기 위해 PyInstaller 및 setupTools 및 복사 환경 사용

1. setupTools와 PyInstaller 패키징의 차이점

pyinstaller포장 과 setuptools차이점은 무엇입니까 ?ChatGPT

pyinstallersetuptools둘 다 Python에서 이지만 주요 목적과 작업 방식이 약간 다릅니다.

  • pyinstaller코드를 실행 파일로 패키징Python 할 수 있는 도구로 , 코드 해석기 없이 코드를 실행할 수 있습니다 . 이를 통해 Python이 설치되지 않은 사용자에게 Python 애플리케이션을 배포하거나 Python 환경이 없는 시스템에 배치 할 수 있습니다 .PythonPython

  • setuptoolsPyPI 또는 다른 패키지 관리자에 게시할 수 있는 설치 가능한 패키지로 코드를 패키징하는 데 도움이 되는 Python패키지 빌드 . 패키지 메타데이터, 종속성 및 기타 설정을 정의하기 위한 많은 옵션과 패키지 패키징을 위한 명령줄 도구도 있습니다.Pythonsetuptools

  • pyinstaller따라서 setuptools둘 다 Python 코드를 독립형 배포판으로 패키징할 수 있지만 주요 목적은 약간 다릅니다.

    • Python응용 프로그램을 단일 실행 파일로 패키징하려는 경우 선택 사항입니다 pyinstaller.

    • Python코드를 설치 가능한 패키지 로 패키징 하고 PyPI다른 경우 선택 사항입니다 setuptools.

2. setupTools를 사용하여 로컬 프로젝트 패키징

참고

0) 프로젝트 준비 및 생성 requirements.txt및 패키지 파일 작성

가) 프로젝트 준비

Python 프로젝트의 디렉터리 구조가 다음과 같다고 가정합니다.

여기에 이미지 설명 삽입

여기서 pdf_handler.py코드는 다음과 같습니다.

import pdfplumber
from PyPDF2 import PdfReader, PdfWriter
import os

import sys

pdf_path = "resume.pdf"  #相对位置,不能使用绝对位置

#提取PDF文字
def extract_pdf_text():
    # 提取pdf指定页的文字
    with pdfplumber.open(pdf_path) as pdf:
        page01 = pdf.pages[0]  # 指定页码
        text = page01.extract_text()  # 提取文本
        print(f"第一页pdf的文本内容为:{
      
      text}",end='\n')

    #提取所有页pdf文字
    with pdfplumber.open(pdf_path) as pdf:
        print(f"所有页pdf的文本内容如下:")
        for page in pdf.pages:  #遍历所有页
            text = page.extract_text()  # 提取当前页的文本
            print(text)

#提取PDF表格
def extract_pdf_table():
    with pdfplumber.open(pdf_path) as pdf:
        print(f"所有页pdf的表格内容如下:")
        for page in pdf.pages:  # 遍历所有页
            table = page.extract_table()  # 提取当前页的文本
            print(table)

#分割PDF
def split_pdf():
    #如果使用PdfFileReader会抛出异常:PyPDF2.errors.DeprecationError: PdfFileReader is deprecated and was removed in PyPDF2 3.0.0. Use PdfReader instead.
    file_reader = PdfReader(pdf_path)  #实例化pdf reader对象
    # getNumPages() 获取总页数会报错:PyPDF2.errors.DeprecationError: reader.getNumPages is deprecated and was removed in PyPDF2 3.0.0. Use len(reader.pages) instead.
    for page in range(len(file_reader.pages)):
        file_writer = PdfWriter()  #实例化pdf writer对象
        # 将遍历的每一页对象添加到pdf writer对象中,使用file_reader.getPage(page)会报错:PyPDF2.errors.DeprecationError: reader.getPage(pageNumber) is deprecated and was removed in PyPDF2 3.0.0. Use reader.pages[page_number] instead.
        file_writer.add_page(file_reader.pages[page])  #PyPDF2.errors.DeprecationError: addPage is deprecated and was removed in PyPDF2 3.0.0. Use add_page instead.
        with open(f'{
      
      os.path.join(saveDir,str(page)+".pdf")}', 'wb') as out:
            file_writer.write(out)

그중 main.py주요 기능 항목인 코드는 다음과 같습니다.

from pdf_task.pdf_handler import extract_pdf_text,extract_pdf_table,split_pdf
import os

if __name__ == '__main__':
    print("Hello world")
    extract_pdf_text()
    extract_pdf_table()
    split_pdf()
    os.system("pause")

여기에 두 개의 동일한 모듈을 만들어 패키지에 이 두 모듈을 포함시키는 pdf_task것을 시뮬레이트합니다 .pypi_test

b) 생성requirements.txt

그런 다음 프로젝트의 루트 디렉터리에서 pypi_test()를 사용하여 전체 패키지에 대한 타사 종속성 파일을 생성pipreqs 합니다 . ( 파이썬에서 requirements.txt를 생성하는 두 가지 방법 참조 )requirements.txt

pip install pipreqs -i https://pypi.tuna.tsinghua.edu.cn/simple

#生成requirements.txt命令如下
pipreqs . --encoding=utf8 --force  #如果requirement.txt已存在,则

requirement.txt생성된 콘텐츠는 다음과 같습니다.

pdfplumber==0.8.0
PyPDF2==3.0.1
setuptools==67.3.3

c) 패키지 파일 작성

그런 다음 해당 패키지 파일을 만들고 작성합니다 .

  • README.md: 이 항목에 대한 구체적인 설명(맞춤형)

  • LICENSEhttps://choosealicense.com/: 프로젝트 소유권 및 사용 제약(커스텀, 참조 ) 등의 문제에 대한 설명

  • pyproject.toml: setuptools지정해야 하는 최소 버전, 스크립트 내용은 다음과 같습니다.

    [build-system]
    requires = ["setuptools>=42"]
    build-backend = "setuptools.build_meta"
    
  • setup.pysetuptools: 패키징 스크립트 사용 내용은 다음과 같은 부분이 포함되어 있습니다 참조 Python 프로젝트 코드 작성 후 패키징 및 릴리스는 어떻게 하나요?

    name: 你定义的包名,可以用字母、数字、下划线,需要确保唯一性。
    version: 项目的版本号。
    author:(作者)的名称。
    author_email:(作者) 的邮箱。
    description: 项目的简要描述。
    long_description_content_type:长描述内容的使用的标记类型,一般为 markdown 或者 rst。
    url: 你这个项目的主页地址,也可以直接链接到你这个项目的Github 地址上面去。
    include_package_data: 是否添加 py 以外的文件。
    package_data: 需要添加 Python 的额外文件列表。
    packages: 直接用 setuptool 找到你项目所有相关的包列表。这里直接使用find_packages()自动寻找
    classifiers: 附加说明,比如这里写的就是使用于 Python3 版本,使用的是 MIT 协议,独立于 OS。
    python_requires: python 版本要求。
    

    제가 작성한 스크립트는 setup.py다음과 같습니다.

    #参考 https://juejin.cn/post/7053009657371033630, https://zhuanlan.zhihu.com/p/527321393
    #!/usr/bin/env python
    from os import path
    from setuptools import setup, find_packages
    
    here = path.abspath(path.dirname(__file__))
    with open(path.join(here, 'requirements.txt'), 'r', encoding='utf-8') as f:
        all_reqs = f.read().split('\n')
    
    with open(path.join(here, 'README.md'), 'r', encoding='utf-8') as f:
        long_description = f.read()
    
    install_requires = [x.strip() for x in all_reqs if 'git+' not in x]
    
    setup(
        name='pypi_test',  # 必填,项目的名字,用户根据这个名字安装,pip install SpiderKeeper-new
        version='1.0.0',  # 必填,项目的版本,建议遵循语义化版本规范
        author='wangxiaoxi',  # 项目的作者
        description='pypi测试',  # 项目的一个简短描述
        long_description=long_description,  # 项目的详细说明,通常读取 README.md 文件的内容
        long_description_content_type='text/markdown',  # 描述的格式,可选的值: text/plain, text/x-rst, and text/markdown
        author_email='[email protected]',  # 作者的有效邮箱地址
        url='https://github.com/test',  # 项目的源码地址
        license='MIT',
        include_package_data=True,
        package_data= {
          
          
            'src' : ["resources/"]
        },
        packages=find_packages(),  # 必填,指定打包的目录,默认是当前目录,如果是其他目录比如 src, 可以使用 find_packages(where='src')
        install_requires=install_requires,  # 指定你的项目依赖的 python 包,这里直接读取 requirements.txt
        # 分类器通过对项目进行分类,帮助用户找到项目
        classifiers=[
            'License :: OSI Approved :: MIT License',
            'Operating System :: OS Independent',
            'Programming Language :: Python :: 3',
        ],
        python_requires=">=3.8"
    )
    

위의 파일을 모두 작성한 후 디렉토리 구조는 다음과 같습니다.

여기에 이미지 설명 삽입

1) 소스 및 바이너리 설치 가능 패키지( )로 패키징 whl및 참조

  1. 먼저 setuptools및 의 버전을 업그레이드하십시오 wheel.

    pip install --upgrade setuptools wheel
    
  2. 그런 다음 루트 디렉터리( pypi_test)에서 다음 명령을 사용하여 소스 코드를 패키징합니다.

    python setup.py sdist
    

    이때 루트 디렉토리 아래에dist 패키지된 소스 코드 압축 패키지가 들어 있는 폴더가 생성 되며 pypi_test-1.0.0.tar.gz, 압축 해제 후 루트 디렉토리 아래 python소스 코드가 됩니다 .

  3. 그런 다음 루트 디렉터리( pypi_test)에서 다음 명령을 사용하여 소스 코드를 패키징합니다.

    python setup.py bdist_wheel
    

    이때 해당 dist폴더에는 해당 프로젝트의 바이너리 설치 가능 파일이 생성되며 pypi_test-1.0.0-py3-none-any.whl정적 리소스를 제외한 전체 프로젝트의 두 모듈의 소스 코드를 포함 하는 폴더 build가 생성됩니다libpython .

  4. 소스 코드를 패키징하고 바이너리 설치 가능 소프트웨어 패키지를 동시에 패키징하려면 다음 명령을 직접 사용할 수 있습니다.

    python setup.py sdist bdist_wheel
    

    패키지된 디렉토리 구조는 다음과 같습니다.

    여기에 이미지 설명 삽입

이 패키지를 참조하려면 직접 pip install .\pypi_test-1.0.0-py3-none-any.whl,

여기에 이미지 설명 삽입

그런 다음 새 프로젝트에서 하나를 만들어 이 프로젝트 python_test의 두 모듈을 직접 참조합니다 .pdf_taskpdf_task2

from pdf_task.pdf_handler import extract_pdf_text

extract_pdf_text()  
# 或许会报找不到静态资源的错误:FileNotFoundError: [Errno 2] No such file or directory: 'D:\\programSoftware\\python\\anaconda\\envs\\spider_env\\lib\\site-packages\\pdf_task\\resume.pdf',
# 可以将resume.pdf放到"site-packages/pdf_task/'中即可解决

이 패키지를 사용하지 않으려면 pip uninstall pypi_test로 제거할 수 있습니다. 여기서 는 에 구성된 프로젝트 이름 pypi_test입니다setup.py .

2) PyPI에 whl 업로드

먼저 https://pypi.org/account/register/pypi 에서 계정을 등록합니다 .

그런 다음 다음을 설치하십시오 twine.

pip install twine 

마지막으로 pypi_test디렉터리 에서 setupTools패키징된 dist파일을 pypi다음 위치에 업로드합니다.

twine upload dist/* 

가능한 문제들:

  • 오류 1 : 업로드된 dist폴더에 whl다음 이외의 폴더가 포함되어 있습니다.

    (spider_env) PS D:\桌面\pypi_test> twine upload dist/*
    Uploading distributions to https://upload.pypi.org/legacy/
    ERROR    InvalidDistribution: Unknown distribution format: 'pypi_test-1.0.0'
    

    해결 방법 : dist폴더에서 중복 폴더를 삭제합니다.

  • 오류 2 : 프로젝트 이름이 다른 사람에 의해 사용되었습니다. HTTPError: 403 클라이언트 오류: 사용자가 프로젝트에 업로드하도록 허용됨 · GitHub를 참조하십시오.

    ERROR    HTTPError: 403 Forbidden from https://upload.pypi.org/legacy/
             The user 'wangxiaoxi' isn't allowed to upload to project 'pypi_test'. See https://pypi.org/help/#project-name for more
             information. 
    

    해결 방법 : 원본 파일을 삭제하고 프로젝트 이름을 whl수정하여 다시 패키징한 후 다시 업로드합니다. setup.py여기서 프로젝트 이름을 wangwangwang_pypi_test.

    여기에 이미지 설명 삽입

업로드에 성공하면 콘솔의 링크를 통해 액세스할 수 있습니다.

여기에 이미지 설명 삽입

3) 가능한 문제

모듈 이름은 python키워드가 될 수 없습니다(예: main). 그렇지 않으면 패키징이 성공하더라도 직접 참조할 수 없습니다 .

3. PyInstaller를 사용하여 로컬 프로젝트 패키징

참고

1) PyInstaller는 주요 기능을 실행 파일로 패키징합니다.

pyinstaller패키징 명령 의 매개변수는 다음과 같습니다.

--distpath <path>: 打包到哪个目录下
-w: 指定生成 GUI 软件,也就是运行时不打开控制台
-c: 运行时打开控制台
-i <Icon File>: 指定打包后可执行文件的图标
--clean: 在构建之前清理PyInstaller缓存并删除临时文件

위의 프로젝트 예제는 여기에서도 여전히 사용되지만 setuptools패키징과 달리 단일 모듈의 항목 기능 만 여기에 패키징됩니다pdf_taskmain.py ( 루트 디렉터리 main.py있음).GUI 데스크탑이 포함되지 않으므로 콘솔 출력이 유지되고 패키징 명령은 명령은 다음과 같습니다.pdf_task

(spider_env) PS D:\桌面\pypi_test> pyinstaller --distpath D:/pypi_package/Release/ --clean pdf_task/main.py

pypi_test패키징 후 현재 디렉터리( ) 아래에 폴더 build생성되고 , 경로 아래에 릴리스 버전 의 패키지 파일 distpath( )이 생성됩니다 .Release

여기에 이미지 설명 삽입

여기에 이미지 설명 삽입

2) 패키지 및 정적 파일 가져오기 문제

참고 :

  • 패키징할 때 모듈의 루트 디렉터리 (여기서는 ) 아래에 main.py두어야 하며 파일을 패키지의 다른 모듈로 가져올 때 루트 디렉터리 이름을 추가해야 합니다 .pdf_taskpythonimport

    예를 들어, 다음과 같은 Release실행 파일을 실행하면 No module named 'pdf_handler'오류가 발생합니다.

    from pdf_handler import extract_pdf_text,extract_pdf_table,split_pdf
    import os
    
    if __name__ == '__main__':
        print("Hello world")
        extract_pdf_text()
        extract_pdf_table()
        split_pdf()
        os.system("pause")
    

    pdf_handler찾을 수 없는 오류가 보고되지 않도록 다음 가져오기 방법으로 수정해야 합니다 ( 전제는 pdf_task빈 패키지를 포함해야 하는 Python 패키지라는__init__.py 것입니다 . 그렇지 않으면 오류가 보고됨).

    from pdf_task.pdf_handler import extract_pdf_text,extract_pdf_table,split_pdf
    import os
    
    if __name__ == '__main__':
        print("Hello world")
        extract_pdf_text()
        extract_pdf_table()
        split_pdf()
        os.system("pause")
    
  • Release위 폴더를 직접 실행하면 main.exe아래와 같은 오류가 발생합니다.

    참고 : 콘솔이 깜박이면 GUI 인터페이스와 함께 명령을 사용하여 pyinstaller --distpath D:/pypi_package/Release/ -w --clean pdf_task/main.py인터페이스에서 오류 메시지를 볼 수 있습니다.

    오류 메시지를 찾은 다음 누락된 정적 파일 (구성 파일 또는 정적 코드 라이브러리) 을 도입하려는 경우 에서 사용할 수 있습니다pythonraise Exception() .

    Traceback (most recent call last):
     File "pdf_task\main.py", line 6, in <module>
     File "pdf_task\pdf_handler.py", line 17, in extract_pdf_text
     File "pdfplumber\pdf.py", line 71, in open
    FileNotFoundError: [Errno 2] No such file or directory: 'D:\\桌面\\pypi_test\\src\\Release\\main\\pdf_task\\resume.pdf'
    

    해결책은 모듈의 정적 파일을 그대로 복사 하는 것입니다 Release/main( 전체를 복사 하고 불필요한 파일은 pdf_task모두 삭제py ).

    여기에 이미지 설명 삽입

    이렇게 하면 폴더를 다시 실행 Release해도 main.exe문제가 없습니다 .

3) 기타 문제

4. 환경 복사 및 실행 스크립트 작성

여전히 위의 pypi_test모듈을 pdf_task예로 들어, 동일한 운영 체제에서 압축된 패키지의 압축을 푼 후 다른 사람이 직접 메서드를 호출하도록 하려면 환경을 프로젝트의 루트 디렉터리에 직접 복사하고 시작 스크립트를 작성할 pdf_task/main.py수 있습니다 .pythonmain.py

  • python가상 환경 spider_env의 모든 파일을 루트 디렉터리에 직접 복사합니다 py38.

    여기에 이미지 설명 삽입

  • 시작 스크립트를 작성하고 실행하여 main.py;

    py38\python.exe pdf_task\main.py
    
    pause
    

프로젝트 구조는 다음과 같습니다.

여기에 이미지 설명 삽입

setuptools패키징 과 달리 환경을 pyInstaller직접 복사하는 장점은 원래 프로젝트 구조의 무결성을 보장할 수 있다는 점이지만 단점은 다른 시스템으로의 이식성이 상대적으로 떨어진다는 것입니다 .Python

Guess you like

Origin blog.csdn.net/qq_33934427/article/details/129152909