Pythonでインストール済みパッケージ一覧を取得する方法【完全ガイド】

 

Pythonで開発をしていると、現在インストールされているパッケージを確認したい場面がよくあります。「どのパッケージがインストールされているか分からない」「バージョンを確認したい」「requirements.txtを作成したい」といった場合に役立つ方法を、具体的なコマンド例とともに詳しく解説します。

目次

  1. パッケージ一覧を取得する理由
  2. pip listコマンドの基本的な使い方
  3. pip freezeコマンドの活用法
  4. パッケージ情報の詳細表示
  5. Pythonスクリプトでパッケージ一覧を取得
  6. 仮想環境でのパッケージ管理
  7. パッケージの検索とフィルタリング
  8. 出力形式のカスタマイズ
  9. パッケージの依存関係確認
  10. トラブルシューティング

パッケージ一覧を取得する理由

なぜパッケージ一覧が必要なのか

  • 環境の確認: 現在の開発環境の状態を把握
  • requirements.txt作成: プロジェクトの依存関係を記録
  • 環境の複製: 他の環境で同じパッケージ構成を再現
  • デバッグ: パッケージの競合や問題の特定
  • セキュリティ監査: 古いパッケージや脆弱性のチェック
  • 環境の整理: 不要なパッケージの特定と削除

よくある使用場面

# 新しいプロジェクトの開始時
pip list

# 本番環境へのデプロイ前
pip freeze > requirements.txt

# 開発環境の共有時
pip list --format=freeze

# パッケージの競合調査時
pip show package_name

pip listコマンドの基本的な使い方

基本的なコマンド

# インストール済みパッケージの一覧表示
pip list

# Python 3を明示的に指定
pip3 list

# 特定の仮想環境で実行
source venv/bin/activate
pip list

基本的な出力例:

Package    Version
---------- -------
certifi    2023.7.22
numpy      1.24.3
pandas     2.0.3
pip        23.3.1
requests   2.31.0
setuptools 68.2.2

よく使用されるオプション

# アウトデートなパッケージのみ表示
pip list --outdated

# ローカルでインストールしたパッケージのみ表示
pip list --local

# ユーザーディレクトリにインストールしたパッケージのみ
pip list --user

# 最新でないパッケージの詳細情報
pip list --outdated --verbose

# 特定の形式で出力
pip list --format=columns
pip list --format=freeze
pip list --format=json

出力形式の比較

# デフォルト形式(columns)
pip list
# Package    Version
# ---------- -------
# requests   2.31.0
# pandas     2.0.3

# freeze形式
pip list --format=freeze
# requests==2.31.0
# pandas==2.0.3

# JSON形式
pip list --format=json
# [{"name": "requests", "version": "2.31.0"}, {"name": "pandas", "version": "2.0.3"}]

pip freezeコマンドの活用法

pip freezeの基本使用法

# requirements.txt形式で出力
pip freeze

# ファイルに保存
pip freeze > requirements.txt

# 特定のパッケージのみ
pip freeze | grep requests

# アルファベット順にソート
pip freeze | sort

pip listとpip freezeの違い

項目pip listpip freeze
出力形式表形式(デフォルト)パッケージ==バージョン
用途確認・表示requirements.txt生成
依存関係すべて表示トップレベルのみ(オプション)
カスタマイズ豊富なオプションシンプル

実用的な使用例

# 現在の環境をrequirements.txtに保存
pip freeze > requirements.txt

# 既存のrequirements.txtと比較
pip freeze > current_packages.txt
diff requirements.txt current_packages.txt

# 特定のパッケージのバージョンを確認
pip freeze | grep -i django
# Django==4.2.7

# パッケージ数をカウント
pip freeze | wc -l
# 42

# 特定の文字列を含むパッケージを検索
pip freeze | grep -E "(test|dev)"
# pytest==7.4.3
# pytest-django==4.7.0

パッケージ情報の詳細表示

pip showコマンドの使用法

# 特定のパッケージの詳細情報
pip show requests

# 複数パッケージの情報を一度に表示
pip show requests pandas numpy

# 依存関係も含めて表示
pip show --verbose requests

# ファイル一覧も表示
pip show --files requests

出力例:

Name: requests
Version: 2.31.0
Summary: Python HTTP for Humans.
Home-page: https://requests.readthedocs.io
Author: Kenneth Reitz
Author-email: me@kennethreitz.org
License: Apache 2.0
Location: /usr/local/lib/python3.11/site-packages
Requires: certifi, charset-normalizer, idna, urllib3
Required-by: 

パッケージ情報の活用方法

# インストール場所の確認
pip show requests | grep Location

# 依存関係の確認
pip show requests | grep Requires

# ライセンス情報の確認
pip show requests | grep License

# 複数パッケージのライセンス一覧
for package in $(pip freeze | cut -d= -f1); do
    echo -n "$package: "
    pip show $package | grep License | cut -d: -f2
done

Pythonスクリプトでパッケージ一覧を取得

pkgライブラリを使用した方法

import pkg_resources

def get_installed_packages():
    """インストール済みパッケージの一覧を取得"""
    installed_packages = []
    
    for dist in pkg_resources.working_set:
        installed_packages.append({
            'name': dist.project_name,
            'version': dist.version,
            'location': dist.location
        })
    
    return sorted(installed_packages, key=lambda x: x['name'])

# 使用例
packages = get_installed_packages()
for package in packages:
    print(f"{package['name']}=={package['version']}")

importlib.metadataを使用した方法(Python 3.8+)

from importlib import metadata
import sys

def get_package_info():
    """パッケージ情報を詳細に取得"""
    packages = []
    
    for dist in metadata.distributions():
        try:
            package_info = {
                'name': dist.metadata['Name'],
                'version': dist.version,
                'summary': dist.metadata.get('Summary', ''),
                'author': dist.metadata.get('Author', ''),
                'license': dist.metadata.get('License', ''),
                'requires': dist.metadata.get('Requires-Dist', [])
            }
            packages.append(package_info)
        except Exception as e:
            print(f"Error processing package: {e}", file=sys.stderr)
    
    return sorted(packages, key=lambda x: x['name'])

# 使用例
packages = get_package_info()
for package in packages[:5]:  # 最初の5個のみ表示
    print(f"Name: {package['name']}")
    print(f"Version: {package['version']}")
    print(f"Summary: {package['summary']}")
    print("-" * 40)

subprocessを使用してpipコマンドを実行

import subprocess
import json

def get_packages_via_pip():
    """pipコマンドを使ってパッケージ一覧を取得"""
    try:
        # pip list --format=json を実行
        result = subprocess.run(
            ['pip', 'list', '--format=json'],
            capture_output=True,
            text=True,
            check=True
        )
        
        packages = json.loads(result.stdout)
        return packages
    
    except subprocess.CalledProcessError as e:
        print(f"Error executing pip command: {e}")
        return []
    except json.JSONDecodeError as e:
        print(f"Error parsing JSON output: {e}")
        return []

# 使用例
packages = get_packages_via_pip()
print(f"Total packages installed: {len(packages)}")

for package in packages[:10]:  # 最初の10個のみ表示
    print(f"{package['name']} ({package['version']})")

カスタム関数の作成

import pkg_resources
from datetime import datetime
import os
import sys

class PackageManager:
    """パッケージ管理のためのユーティリティクラス"""
    
    @staticmethod
    def get_all_packages():
        """すべてのパッケージ情報を取得"""
        packages = []
        for dist in pkg_resources.working_set:
            packages.append({
                'name': dist.project_name,
                'version': dist.version,
                'location': dist.location,
                'requires': [str(req) for req in dist.requires()]
            })
        return sorted(packages, key=lambda x: x['name'])
    
    @staticmethod
    def export_to_requirements(filename='requirements.txt'):
        """requirements.txtファイルを生成"""
        packages = PackageManager.get_all_packages()
        with open(filename, 'w') as f:
            f.write(f"# Generated on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
            f.write(f"# Python {sys.version.split()[0]}\n\n")
            
            for package in packages:
                f.write(f"{package['name']}=={package['version']}\n")
        
        print(f"Requirements exported to {filename}")
    
    @staticmethod
    def find_package(search_term):
        """パッケージを検索"""
        packages = PackageManager.get_all_packages()
        results = []
        
        for package in packages:
            if search_term.lower() in package['name'].lower():
                results.append(package)
        
        return results
    
    @staticmethod
    def get_package_size_estimate():
        """パッケージのサイズを推定"""
        packages = PackageManager.get_all_packages()
        size_info = []
        
        for package in packages:
            location = package['location']
            if os.path.exists(location):
                # ディレクトリサイズの計算(簡易版)
                total_size = 0
                package_dir = os.path.join(location, package['name'].replace('-', '_'))
                if os.path.exists(package_dir):
                    for dirpath, dirnames, filenames in os.walk(package_dir):
                        for filename in filenames:
                            filepath = os.path.join(dirpath, filename)
                            try:
                                total_size += os.path.getsize(filepath)
                            except (OSError, IOError):
                                pass
                
                size_info.append({
                    'name': package['name'],
                    'version': package['version'],
                    'size_mb': round(total_size / (1024 * 1024), 2)
                })
        
        return sorted(size_info, key=lambda x: x['size_mb'], reverse=True)

# 使用例
if __name__ == "__main__":
    # すべてのパッケージを表示
    packages = PackageManager.get_all_packages()
    print(f"Total packages: {len(packages)}\n")
    
    # 最初の5個を表示
    for package in packages[:5]:
        print(f"Name: {package['name']}")
        print(f"Version: {package['version']}")
        print(f"Location: {package['location']}")
        print(f"Requires: {', '.join(package['requires'])}")
        print("-" * 50)
    
    # requirements.txtを生成
    PackageManager.export_to_requirements()
    
    # 特定のパッケージを検索
    django_packages = PackageManager.find_package('django')
    print(f"\nDjango related packages: {len(django_packages)}")
    for package in django_packages:
        print(f"  {package['name']} ({package['version']})")

仮想環境でのパッケージ管理

venv環境でのパッケージ確認

# 仮想環境を作成
python -m venv myproject_env

# 仮想環境を有効化(Linux/macOS)
source myproject_env/bin/activate

# 仮想環境を有効化(Windows)
myproject_env\Scripts\activate

# 仮想環境のパッケージ一覧
pip list

# グローバル環境との比較
pip list --local

# 仮想環境の情報を保存
pip freeze > myproject_requirements.txt

# 仮想環境を無効化
deactivate

conda環境でのパッケージ管理

# conda環境の作成
conda create -n myproject python=3.11

# 環境の有効化
conda activate myproject

# condaでインストールしたパッケージ
conda list

# pipでインストールしたパッケージ
pip list

# 両方の情報を含む環境ファイル
conda env export > environment.yml

# pip専用のrequirements.txt
pip freeze > requirements.txt

# 環境の無効化
conda deactivate

pipenv環境でのパッケージ管理

# pipenv環境でシェルを開始
pipenv shell

# インストール済みパッケージ
pipenv graph

# Pipfileの内容確認
cat Pipfile

# requirements.txt形式で出力
pipenv requirements > requirements.txt

# 開発用パッケージも含める
pipenv requirements --dev > requirements-dev.txt

複数環境の比較

import subprocess
import json
from pathlib import Path

def compare_environments():
    """複数の仮想環境のパッケージを比較"""
    environments = {
        'global': None,
        'venv1': 'path/to/venv1',
        'venv2': 'path/to/venv2'
    }
    
    env_packages = {}
    
    for env_name, env_path in environments.items():
        if env_path:
            # 仮想環境のpipを使用
            pip_path = Path(env_path) / 'bin' / 'pip'  # Linux/macOS
            if not pip_path.exists():
                pip_path = Path(env_path) / 'Scripts' / 'pip.exe'  # Windows
        else:
            # グローバル環境のpip
            pip_path = 'pip'
        
        try:
            result = subprocess.run(
                [str(pip_path), 'list', '--format=json'],
                capture_output=True,
                text=True,
                check=True
            )
            packages = json.loads(result.stdout)
            env_packages[env_name] = {pkg['name']: pkg['version'] for pkg in packages}
        except Exception as e:
            print(f"Error getting packages for {env_name}: {e}")
            env_packages[env_name] = {}
    
    return env_packages

# 使用例
env_comparison = compare_environments()
for env_name, packages in env_comparison.items():
    print(f"\n{env_name}: {len(packages)} packages")
    for name, version in list(packages.items())[:5]:  # 最初の5個のみ
        print(f"  {name}: {version}")

パッケージの検索とフィルタリング

コマンドラインでの検索

# 特定の文字列を含むパッケージを検索
pip list | grep -i django

# 複数の条件で検索
pip list | grep -E "(test|dev|debug)"

# バージョン番号でフィルタリング
pip list | grep -E "\b2\."  # バージョン2.x系のパッケージ

# アルファベット順でソート
pip list --format=freeze | sort

# パッケージ数のカウント
pip list | wc -l

# 特定のパッケージのバージョンのみ表示
pip show requests | grep Version

高度な検索とフィルタリング

# アップデート可能なパッケージの詳細
pip list --outdated --format=columns

# ローカルにインストールしたパッケージのみ
pip list --local --format=freeze

# 特定の場所にインストールされたパッケージ
pip list --verbose | grep "/usr/local"

# セキュリティ関連パッケージの検索
pip list | grep -i -E "(security|crypto|ssl|auth)"

# 開発ツール関連パッケージの検索
pip list | grep -i -E "(test|lint|format|debug)"

Pythonスクリプトでの高度な検索

import pkg_resources
import re
from collections import defaultdict

class PackageSearcher:
    """パッケージ検索のためのユーティリティクラス"""
    
    def __init__(self):
        self.packages = list(pkg_resources.working_set)
    
    def search_by_name(self, pattern):
        """名前で検索(正規表現対応)"""
        regex = re.compile(pattern, re.IGNORECASE)
        results = []
        
        for package in self.packages:
            if regex.search(package.project_name):
                results.append({
                    'name': package.project_name,
                    'version': package.version,
                    'location': package.location
                })
        
        return results
    
    def search_by_version(self, version_pattern):
        """バージョンで検索"""
        regex = re.compile(version_pattern)
        results = []
        
        for package in self.packages:
            if regex.search(package.version):
                results.append({
                    'name': package.project_name,
                    'version': package.version
                })
        
        return results
    
    def get_packages_by_location(self):
        """インストール場所でグループ化"""
        location_groups = defaultdict(list)
        
        for package in self.packages:
            location_groups[package.location].append({
                'name': package.project_name,
                'version': package.version
            })
        
        return dict(location_groups)
    
    def find_development_packages(self):
        """開発関連パッケージを検索"""
        dev_keywords = ['test', 'lint', 'format', 'debug', 'dev', 'mock', 'coverage']
        dev_packages = []
        
        for package in self.packages:
            package_name = package.project_name.lower()
            if any(keyword in package_name for keyword in dev_keywords):
                dev_packages.append({
                    'name': package.project_name,
                    'version': package.version,
                    'category': self._categorize_dev_package(package_name)
                })
        
        return dev_packages
    
    def _categorize_dev_package(self, package_name):
        """開発パッケージのカテゴリ分け"""
        if 'test' in package_name:
            return 'testing'
        elif any(word in package_name for word in ['lint', 'flake', 'pylint']):
            return 'linting'
        elif any(word in package_name for word in ['format', 'black', 'autopep']):
            return 'formatting'
        elif 'debug' in package_name:
            return 'debugging'
        elif any(word in package_name for word in ['mock', 'fake']):
            return 'mocking'
        elif 'coverage' in package_name:
            return 'coverage'
        else:
            return 'other'

# 使用例
searcher = PackageSearcher()

# Django関連パッケージを検索
django_packages = searcher.search_by_name(r'django')
print("Django related packages:")
for package in django_packages:
    print(f"  {package['name']} ({package['version']})")

# バージョン2.x系のパッケージを検索
v2_packages = searcher.search_by_version(r'^2\.')
print(f"\nVersion 2.x packages: {len(v2_packages)}")

# 開発パッケージを検索
dev_packages = searcher.find_development_packages()
print(f"\nDevelopment packages: {len(dev_packages)}")
dev_by_category = defaultdict(list)
for package in dev_packages:
    dev_by_category[package['category']].append(package['name'])

for category, packages in dev_by_category.items():
    print(f"  {category}: {', '.join(packages)}")

出力形式のカスタマイズ

表形式の出力

# デフォルトのカラム形式
pip list --format=columns

# 幅を調整した表示
pip list --format=columns | column -t

# 特定の列のみ表示
pip list --format=freeze | cut -d'=' -f1  # パッケージ名のみ
pip list --format=freeze | cut -d'=' -f2  # バージョンのみ

JSON形式での出力と加工

# JSON形式で出力
pip list --format=json

# jqコマンドを使った加工(jqがインストールされている場合)
pip list --format=json | jq '.[] | select(.name | contains("django"))'
pip list --format=json | jq 'sort_by(.name)'
pip list --format=json | jq 'length'  # パッケージ数

カスタム形式での出力

import subprocess
import json
from tabulate import tabulate  # pip install tabulate

def format_package_list(format_type='table'):
    """パッケージリストを様々な形式で出力"""
    
    # pipからパッケージ情報を取得
    result = subprocess.run(
        ['pip', 'list', '--format=json'],
        capture_output=True,
        text=True,
        check=True
    )
    
    packages = json.loads(result.stdout)
    
    if format_type == 'table':
        # テーブル形式
        headers = ['Package', 'Version']
        data = [[pkg['name'], pkg['version']] for pkg in packages]
        return tabulate(data, headers=headers, tablefmt='grid')
    
    elif format_type == 'markdown':
        # Markdown形式
        headers = ['Package', 'Version']
        data = [[pkg['name'], pkg['version']] for pkg in packages]
        return tabulate(data, headers=headers, tablefmt='pipe')
    
    elif format_type == 'csv':
        # CSV形式
        import csv
        import io
        
        output = io.StringIO()
        writer = csv.writer(output)
        writer.writerow(['Package', 'Version'])
        for pkg in packages:
            writer.writerow([pkg['name'], pkg['version']])
        return output.getvalue()
    
    elif format_type == 'html':
        # HTML形式
        headers = ['Package', 'Version']
        data = [[pkg['name'], pkg['version']] for pkg in packages]
        return tabulate(data, headers=headers, tablefmt='html')
    
    else:
        return "Unsupported format"

# 使用例
print("=== Table Format ===")
print(format_package_list('table'))

print("\n=== Markdown Format ===")
print(format_package_list('markdown'))

print("\n=== CSV Format ===")
print(format_package_list('csv'))

レポート形式での出力

from datetime import datetime
import platform
import sys
import pkg_resources

def generate_package_report():
    """詳細なパッケージレポートを生成"""
    
    packages = list(pkg_resources.working_set)
    packages.sort(key=lambda x: x.project_name.lower())
    
    report = []
    report.append("=" * 60)
    report.append("PYTHON PACKAGE REPORT")
    report.append("=" * 60)
    report.append(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    report.append(f"Python Version: {sys.version}")
    report.append(f"Platform: {platform.platform()}")
    report.append(f"Total Packages: {len(packages)}")
    report.append("")
    
    # カテゴライズ
    categories = {
        'Web Frameworks': ['django', 'flask', 'fastapi', 'tornado'],
        'Data Science': ['pandas', 'numpy', 'scipy', 'scikit-learn', 'matplotlib'],
        'Testing': ['pytest', 'unittest2', 'mock', 'nose'],
        'Development Tools': ['pip', 'setuptools', 'wheel', 'twine'],
        'HTTP Libraries': ['requests', 'urllib3', 'httpx']
    }
    
    categorized = {cat: [] for cat in categories}
    uncategorized = []
    
    for package in packages:
        package_name = package.project_name.lower()
        found_category = False
        
        for category, keywords in categories.items():
            if any(keyword in package_name for keyword in keywords):
                categorized[category].append(package)
                found_category = True
                break
        
        if not found_category:
            uncategorized.append(package)
    
    # カテゴリ別に出力
    for category, category_packages in categorized.items():
        if category_packages:
            report.append(f"{category} ({len(category_packages)} packages):")
            report.append("-" * 40)
            for package in sorted(category_packages, key=lambda x: x.project_name):
                report.append(f"  {package.project_name:<20} {package.version}")
            report.append("")
    
    # その他のパッケージ
    if uncategorized:
        report.append(f"Other Packages ({len(uncategorized)} packages):")
        report.append("-" * 40)
        for package in uncategorized[:10]:  # 最初の10個のみ
            report.append(f"  {package.project_name:<20} {package.version}")
        if len(uncategorized) > 10:
            report.append(f"  ... and {len(uncategorized) - 10} more")
    
    return "\n".join(report)

# レポート生成と保存
report = generate_package_report()
print(report)

# ファイルに保存
with open('package_report.txt', 'w', encoding='utf-8') as f:
    f.write(report)
print("\nReport saved to package_report.txt")

パッケージの依存関係確認

pip showによる依存関係確認

# 特定のパッケージの依存関係
pip show requests

# 依存関係の詳細表示
pip show --verbose requests

# 複数パッケージの依存関係
pip show django djangorestframework

# 依存関係のみ抽出
pip show requests | grep Requires
pip show requests | grep Required-by

pipdeptreeを使った可視化

# pipdeptreeのインストール
pip install pipdeptree

# 依存関係ツリーの表示
pipdeptree

# 特定のパッケージのみ
pipdeptree -p requests

# JSON形式で出力
pipdeptree --json

# 逆依存関係を表示
pipdeptree --reverse

# 警告のみ表示
pipdeptree --warn silence

# GraphViz形式で出力(図式化用)
pipdeptree --graph-output dot > dependencies.dot

pipdeptreeの出力例:

requests==2.31.0
├── certifi [required: >=2017.4.17, installed: 2023.7.22]
├── charset-normalizer [required: >=2,<4, installed: 3.3.2]
├── idna [required: >=2.5,<4, installed: 3.4]
└── urllib3 [required: >=1.21.1,<3, installed: 2.0.7]

Pythonスクリプトでの依存関係分析

import pkg_resources
from collections import defaultdict, deque

class DependencyAnalyzer:
    """パッケージ依存関係の分析ツール"""
    
    def __init__(self):
        self.packages = {pkg.project_name: pkg for pkg in pkg_resources.working_set}
    
    def get_dependencies(self, package_name):
        """特定のパッケージの直接的な依存関係を取得"""
        if package_name not in self.packages:
            return []
        
        package = self.packages[package_name]
        dependencies = []
        
        for requirement in package.requires():
            dependencies.append({
                'name': requirement.project_name,
                'specs': requirement.specs,
                'installed_version': self.packages.get(requirement.project_name, {}).version if requirement.project_name in self.packages else None
            })
        
        return dependencies
    
    def get_reverse_dependencies(self, package_name):
        """特定のパッケージに依存しているパッケージを取得"""
        reverse_deps = []
        
        for pkg_name, package in self.packages.items():
            for requirement in package.requires():
                if requirement.project_name == package_name:
                    reverse_deps.append({
                        'name': pkg_name,
                        'version': package.version
                    })
        
        return reverse_deps
    
    def get_dependency_tree(self, package_name, max_depth=3):
        """依存関係ツリーを取得"""
        def build_tree(pkg_name, depth=0, visited=None):
            if visited is None:
                visited = set()
            
            if depth > max_depth or pkg_name in visited:
                return {'name': pkg_name, 'children': [], 'circular': pkg_name in visited}
            
            visited.add(pkg_name)
            dependencies = self.get_dependencies(pkg_name)
            children = []
            
            for dep in dependencies:
                child_tree = build_tree(dep['name'], depth + 1, visited.copy())
                child_tree['specs'] = dep['specs']
                child_tree['installed_version'] = dep['installed_version']
                children.append(child_tree)
            
            visited.remove(pkg_name)
            return {'name': pkg_name, 'children': children, 'circular': False}
        
        return build_tree(package_name)
    
    def find_circular_dependencies(self):
        """循環依存を検出"""
        def has_circular_dependency(pkg_name, path=None):
            if path is None:
                path = []
            
            if pkg_name in path:
                return path + [pkg_name]
            
            new_path = path + [pkg_name]
            dependencies = self.get_dependencies(pkg_name)
            
            for dep in dependencies:
                result = has_circular_dependency(dep['name'], new_path)
                if result:
                    return result
            
            return None
        
        circular_deps = []
        checked = set()
        
        for pkg_name in self.packages:
            if pkg_name not in checked:
                result = has_circular_dependency(pkg_name)
                if result:
                    circular_deps.append(result)
                    checked.update(result)
        
        return circular_deps
    
    def get_top_level_packages(self):
        """トップレベルパッケージ(他に依存されていない)を取得"""
        all_packages = set(self.packages.keys())
        dependencies = set()
        
        for package in self.packages.values():
            for req in package.requires():
                dependencies.add(req.project_name)
        
        top_level = all_packages - dependencies
        return sorted(list(top_level))
    
    def print_dependency_tree(self, package_name, max_depth=3):
        """依存関係ツリーを見やすく出力"""
        tree = self.get_dependency_tree(package_name, max_depth)
        
        def print_node(node, indent=0, prefix=""):
            name = node['name']
            specs = node.get('specs', [])
            installed = node.get('installed_version', '')
            circular = node.get('circular', False)
            
            specs_str = f" ({','.join([''.join(spec) for spec in specs])})" if specs else ""
            installed_str = f" [installed: {installed}]" if installed else ""
            circular_str = " [CIRCULAR]" if circular else ""
            
            print(f"{'  ' * indent}{prefix}{name}{specs_str}{installed_str}{circular_str}")
            
            children = node.get('children', [])
            for i, child in enumerate(children):
                is_last = i == len(children) - 1
                child_prefix = "└── " if is_last else "├── "
                print_node(child, indent + 1, child_prefix)
        
        print_node(tree)

# 使用例
analyzer = DependencyAnalyzer()

# Django の依存関係ツリー
print("=== Django Dependency Tree ===")
if 'Django' in analyzer.packages:
    analyzer.print_dependency_tree('Django')
else:
    print("Django is not installed")

print("\n=== Top Level Packages ===")
top_level = analyzer.get_top_level_packages()
for package in top_level[:10]:  # 最初の10個のみ
    print(f"  {package}")

print(f"\nTotal top-level packages: {len(top_level)}")

# 循環依存の検出
print("\n=== Circular Dependencies ===")
circular = analyzer.find_circular_dependencies()
if circular:
    for cycle in circular:
        print(f"  {' -> '.join(cycle)}")
else:
    print("  No circular dependencies found")

詳細な依存関係レポート

import pkg_resources
from collections import defaultdict
import json

class DetailedDependencyReporter:
    """詳細な依存関係レポートを生成"""
    
    def __init__(self):
        self.packages = {pkg.project_name: pkg for pkg in pkg_resources.working_set}
    
    def generate_full_report(self):
        """完全な依存関係レポートを生成"""
        report = {
            'summary': self._get_summary(),
            'packages': self._get_package_details(),
            'dependency_graph': self._get_dependency_graph(),
            'orphaned_packages': self._get_orphaned_packages(),
            'most_depended_on': self._get_most_depended_on(),
            'license_distribution': self._get_license_distribution()
        }
        return report
    
    def _get_summary(self):
        """サマリー情報を取得"""
        total_packages = len(self.packages)
        packages_with_deps = sum(1 for pkg in self.packages.values() if len(list(pkg.requires())) > 0)
        total_dependencies = sum(len(list(pkg.requires())) for pkg in self.packages.values())
        
        return {
            'total_packages': total_packages,
            'packages_with_dependencies': packages_with_deps,
            'total_dependency_relationships': total_dependencies,
            'average_dependencies_per_package': round(total_dependencies / total_packages, 2) if total_packages > 0 else 0
        }
    
    def _get_package_details(self):
        """各パッケージの詳細情報を取得"""
        details = {}
        
        for pkg_name, pkg in self.packages.items():
            dependencies = []
            for req in pkg.requires():
                dependencies.append({
                    'name': req.project_name,
                    'specs': req.specs,
                    'installed': req.project_name in self.packages,
                    'installed_version': self.packages[req.project_name].version if req.project_name in self.packages else None
                })
            
            # このパッケージに依存しているパッケージを取得
            dependents = []
            for other_name, other_pkg in self.packages.items():
                for req in other_pkg.requires():
                    if req.project_name == pkg_name:
                        dependents.append(other_name)
            
            details[pkg_name] = {
                'version': pkg.version,
                'location': pkg.location,
                'dependencies': dependencies,
                'dependents': dependents,
                'dependency_count': len(dependencies),
                'dependent_count': len(dependents)
            }
        
        return details
    
    def _get_dependency_graph(self):
        """依存関係グラフを生成"""
        graph = {}
        
        for pkg_name, pkg in self.packages.items():
            dependencies = [req.project_name for req in pkg.requires()]
            graph[pkg_name] = dependencies
        
        return graph
    
    def _get_orphaned_packages(self):
        """孤立したパッケージ(他に依存されていない)を取得"""
        all_packages = set(self.packages.keys())
        dependencies = set()
        
        for pkg in self.packages.values():
            for req in pkg.requires():
                dependencies.add(req.project_name)
        
        orphaned = all_packages - dependencies
        return sorted(list(orphaned))
    
    def _get_most_depended_on(self, top_n=10):
        """最も依存されているパッケージを取得"""
        dependency_count = defaultdict(int)
        
        for pkg in self.packages.values():
            for req in pkg.requires():
                if req.project_name in self.packages:
                    dependency_count[req.project_name] += 1
        
        sorted_deps = sorted(dependency_count.items(), key=lambda x: x[1], reverse=True)
        return sorted_deps[:top_n]
    
    def _get_license_distribution(self):
        """ライセンス分布を取得"""
        from importlib import metadata
        
        license_count = defaultdict(int)
        
        try:
            for dist in metadata.distributions():
                license_info = dist.metadata.get('License', 'Unknown')
                license_count[license_info] += 1
        except ImportError:
            # Python < 3.8 の場合
            license_count['Unknown'] = len(self.packages)
        
        return dict(license_count)
    
    def export_report(self, filename='dependency_report.json'):
        """レポートをJSONファイルにエクスポート"""
        report = self.generate_full_report()
        
        with open(filename, 'w', encoding='utf-8') as f:
            json.dump(report, f, indent=2, ensure_ascii=False)
        
        print(f"Dependency report exported to {filename}")
        return report
    
    def print_summary(self):
        """サマリーを見やすく出力"""
        report = self.generate_full_report()
        summary = report['summary']
        
        print("=== DEPENDENCY SUMMARY ===")
        print(f"Total packages: {summary['total_packages']}")
        print(f"Packages with dependencies: {summary['packages_with_dependencies']}")
        print(f"Total dependency relationships: {summary['total_dependency_relationships']}")
        print(f"Average dependencies per package: {summary['average_dependencies_per_package']}")
        
        print(f"\n=== TOP 10 MOST DEPENDED ON PACKAGES ===")
        for package, count in report['most_depended_on']:
            print(f"  {package}: {count} packages depend on it")
        
        print(f"\n=== ORPHANED PACKAGES ({len(report['orphaned_packages'])}) ===")
        for package in report['orphaned_packages'][:10]:
            print(f"  {package}")
        if len(report['orphaned_packages']) > 10:
            print(f"  ... and {len(report['orphaned_packages']) - 10} more")

# 使用例
reporter = DetailedDependencyReporter()
reporter.print_summary()

# 詳細レポートをファイルに出力
reporter.export_report()

トラブルシューティング

よくある問題と解決法

1. pip listが実行できない

# エラー例
pip: command not found

# 解決法1: Pythonモジュールとして実行
python -m pip list

# 解決法2: pip3を使用
pip3 list

# 解決法3: パスの確認
which pip
echo $PATH

# 解決法4: pipの再インストール
python -m ensurepip --upgrade

2. 権限エラー

# エラー例
PermissionError: [Errno 13] Permission denied

# 解決法1: 仮想環境を使用
python -m venv venv
source venv/bin/activate
pip list

# 解決法2: ユーザーディレクトリのパッケージのみ表示
pip list --user

# 解決法3: sudoを使用(推奨しない)
sudo pip list

3. パッケージ情報が表示されない

# 問題の診断
pip check  # パッケージの整合性確認
pip list --verbose  # 詳細情報表示

# キャッシュのクリア
pip cache purge

# pipの更新
python -m pip install --upgrade pip

# パッケージデータベースの修復
python -m pip install --force-reinstall --no-deps pip

4. 仮想環境のパッケージが見えない

# 仮想環境の確認
which python
which pip

# 仮想環境の有効化確認
echo $VIRTUAL_ENV  # Linux/macOS
echo $CONDA_DEFAULT_ENV  # conda環境

# 正しい pip を使用
python -m pip list

# 仮想環境の再作成
deactivate
rm -rf venv
python -m venv venv
source venv/bin/activate

5. 文字化けエラー

# エラー例
UnicodeDecodeError: 'ascii' codec can't decode

# 解決法1: 環境変数を設定
export PYTHONIOENCODING=utf-8
pip list

# 解決法2: ロケールを設定
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
pip list

# 解決法3: Pythonスクリプトで処理
python -c "
import sys
import subprocess
result = subprocess.run([sys.executable, '-m', 'pip', 'list'], 
                       capture_output=True, text=True, encoding='utf-8')
print(result.stdout)
"

デバッグ用のスクリプト

import sys
import os
import subprocess
import pkg_resources
import platform

def diagnose_python_environment():
    """Python環境の診断を行う"""
    
    print("=== Python Environment Diagnosis ===")
    print(f"Python version: {sys.version}")
    print(f"Python executable: {sys.executable}")
    print(f"Platform: {platform.platform()}")
    print(f"Architecture: {platform.architecture()}")
    
    print(f"\n=== Environment Variables ===")
    important_vars = [
        'VIRTUAL_ENV', 'CONDA_DEFAULT_ENV', 'PYTHONPATH', 
        'PYTHONHOME', 'PATH', 'PYTHONIOENCODING'
    ]
    
    for var in important_vars:
        value = os.environ.get(var, 'Not set')
        print(f"{var}: {value}")
    
    print(f"\n=== Python Path ===")
    for i, path in enumerate(sys.path):
        print(f"  {i}: {path}")
    
    print(f"\n=== Package Installation Locations ===")
    try:
        import site
        print(f"User site-packages: {site.getusersitepackages()}")
        site_packages = site.getsitepackages()
        for i, site_pkg in enumerate(site_packages):
            print(f"Site-packages {i}: {site_pkg}")
    except Exception as e:
        print(f"Error getting site information: {e}")
    
    print(f"\n=== pip Information ===")
    pip_commands = ['pip', 'pip3', f'{sys.executable} -m pip']
    
    for cmd in pip_commands:
        try:
            if cmd.startswith(sys.executable):
                result = subprocess.run(cmd.split() + ['--version'], 
                                      capture_output=True, text=True, timeout=10)
            else:
                result = subprocess.run([cmd, '--version'], 
                                      capture_output=True, text=True, timeout=10)
            
            if result.returncode == 0:
                print(f"{cmd}: {result.stdout.strip()}")
            else:
                print(f"{cmd}: Error - {result.stderr.strip()}")
        except Exception as e:
            print(f"{cmd}: Not available - {e}")
    
    print(f"\n=== Package Count ===")
    try:
        packages = list(pkg_resources.working_set)
        print(f"Total packages (pkg_resources): {len(packages)}")
        
        # 場所別のカウント
        locations = {}
        for package in packages:
            location = package.location
            locations[location] = locations.get(location, 0) + 1
        
        print(f"Packages by location:")
        for location, count in sorted(locations.items()):
            print(f"  {location}: {count} packages")
    
    except Exception as e:
        print(f"Error counting packages with pkg_resources: {e}")
    
    # pip list テスト
    print(f"\n=== pip list test ===")
    try:
        result = subprocess.run([sys.executable, '-m', 'pip', 'list'], 
                              capture_output=True, text=True, timeout=30)
        
        if result.returncode == 0:
            lines = result.stdout.strip().split('\n')
            print(f"pip list output: {len(lines)} lines")
            print("First 5 lines:")
            for line in lines[:5]:
                print(f"  {line}")
        else:
            print(f"pip list failed: {result.stderr}")
            
    except Exception as e:
        print(f"Error running pip list: {e}")
    
    # importlib.metadata テスト (Python 3.8+)
    print(f"\n=== importlib.metadata test ===")
    try:
        from importlib import metadata
        distributions = list(metadata.distributions())
        print(f"Total packages (importlib.metadata): {len(distributions)}")
    except ImportError:
        print("importlib.metadata not available (Python < 3.8)")
    except Exception as e:
        print(f"Error with importlib.metadata: {e}")

def fix_common_issues():
    """一般的な問題の自動修復を試行"""
    
    print("=== Attempting to fix common issues ===")
    
    # pipの更新
    print("1. Updating pip...")
    try:
        result = subprocess.run([sys.executable, '-m', 'pip', 'install', '--upgrade', 'pip'], 
                              capture_output=True, text=True, timeout=60)
        if result.returncode == 0:
            print("   ✓ pip updated successfully")
        else:
            print(f"   ✗ pip update failed: {result.stderr}")
    except Exception as e:
        print(f"   ✗ pip update error: {e}")
    
    # setuptools と wheel の更新
    print("2. Updating setuptools and wheel...")
    try:
        result = subprocess.run([sys.executable, '-m', 'pip', 'install', '--upgrade', 'setuptools', 'wheel'], 
                              capture_output=True, text=True, timeout=60)
        if result.returncode == 0:
            print("   ✓ setuptools and wheel updated successfully")
        else:
            print(f"   ✗ setuptools/wheel update failed: {result.stderr}")
    except Exception as e:
        print(f"   ✗ setuptools/wheel update error: {e}")
    
    # キャッシュのクリア
    print("3. Clearing pip cache...")
    try:
        result = subprocess.run([sys.executable, '-m', 'pip', 'cache', 'purge'], 
                              capture_output=True, text=True, timeout=30)
        if result.returncode == 0:
            print("   ✓ pip cache cleared successfully")
        else:
            print(f"   ✗ cache clear failed: {result.stderr}")
    except Exception as e:
        print(f"   ✗ cache clear error: {e}")
    
    # パッケージの整合性チェック
    print("4. Checking package integrity...")
    try:
        result = subprocess.run([sys.executable, '-m', 'pip', 'check'], 
                              capture_output=True, text=True, timeout=30)
        if result.returncode == 0:
            print("   ✓ No package conflicts found")
        else:
            print(f"   ⚠ Package conflicts detected:\n{result.stdout}")
    except Exception as e:
        print(f"   ✗ integrity check error: {e}")

# 診断実行
if __name__ == "__main__":
    diagnose_python_environment()
    print("\n" + "="*60 + "\n")
    fix_common_issues()

環境の完全リセット

# 仮想環境の完全リセット
deactivate  # 仮想環境を無効化
rm -rf venv  # 仮想環境ディレクトリを削除
python -m venv venv  # 新しい仮想環境を作成
source venv/bin/activate  # 有効化

# pipの最新化
python -m pip install --upgrade pip

# requirements.txtから再インストール
pip install -r requirements.txt

# または段階的にインストール
pip install --upgrade pip setuptools wheel
pip install -r requirements.txt --no-cache-dir

パフォーマンス最適化

import time
import subprocess
import json

def benchmark_package_listing_methods():
    """パッケージ一覧取得メソッドのベンチマーク"""
    
    methods = {
        'pip_list_json': lambda: subprocess.run(
            ['pip', 'list', '--format=json'], 
            capture_output=True, text=True
        ),
        'pip_freeze': lambda: subprocess.run(
            ['pip', 'freeze'], 
            capture_output=True, text=True
        ),
        'pkg_resources': lambda: list(pkg_resources.working_set),
    }
    
    # Python 3.8+ のみ
    try:
        from importlib import metadata
        methods['importlib_metadata'] = lambda: list(metadata.distributions())
    except ImportError:
        pass
    
    results = {}
    
    for method_name, method_func in methods.items():
        print(f"Testing {method_name}...")
        
        times = []
        for i in range(3):  # 3回実行して平均を取る
            start_time = time.time()
            try:
                result = method_func()
                end_time = time.time()
                times.append(end_time - start_time)
            except Exception as e:
                print(f"  Error in {method_name}: {e}")
                times.append(float('inf'))
        
        avg_time = sum(times) / len(times)
        results[method_name] = {
            'average_time': avg_time,
            'times': times
        }
        
        print(f"  Average time: {avg_time:.4f} seconds")
    
    # 結果のソート
    sorted_results = sorted(results.items(), key=lambda x: x[1]['average_time'])
    
    print(f"\n=== Performance Ranking ===")
    for i, (method, data) in enumerate(sorted_results, 1):
        print(f"{i}. {method}: {data['average_time']:.4f} seconds")
    
    return results

# ベンチマーク実行
if __name__ == "__main__":
    benchmark_results = benchmark_package_listing_methods()

まとめ

Pythonでインストール済みパッケージの一覧を取得する方法は多岐にわたります。用途に応じて適切な方法を選択することで、効率的なパッケージ管理が可能になります。

重要なポイント

基本コマンドの使い分け:

  • pip list: 人間が読みやすい表形式での確認
  • pip freeze: requirements.txt生成やスクリプト処理
  • pip show: 個別パッケージの詳細情報確認

仮想環境での管理:

  • プロジェクトごとの独立した環境での管理
  • 環境間の比較と同期
  • 本番環境への安全なデプロイ

自動化とスクリプト処理:

  • Pythonスクリプトによる高度な分析と処理
  • 依存関係の可視化と管理
  • カスタムレポートの生成

トラブルシューティング:

  • 問題発生時の診断と解決方法
  • 環境の修復と最適化
  • パフォーマンスの改善

実践的な活用法

  1. 日常的な確認: pip list でクイックチェック
  2. 環境の記録: pip freeze > requirements.txt で依存関係を保存
  3. 詳細調査: pip showpipdeptree で問題の特定
  4. 自動化: Pythonスクリプトで定期的な環境監査
  5. チーム開発: 統一された環境管理プロセスの確立

これらの方法を組み合わせることで、Python開発における効率的なパッケージ管理を実現できます。特に大規模なプロジェクトやチーム開発では、パッケージの依存関係を適切に把握し、環境の一貫性を保つことが成功の鍵となります。

■プロンプトだけでオリジナルアプリを開発・公開してみた!!

■AI時代の第一歩!「AI駆動開発コース」はじめました!

テックジム東京本校で先行開始。

■テックジム東京本校

「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。

<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。

<月1開催>放送作家による映像ディレクター養成講座

<オンライン無料>ゼロから始めるPython爆速講座