以下の内容はhttps://uepon.hatenadiary.com/entry/2025/11/10/233001より取得しました。


Wikipedia API活用術|Pythonで記事・カテゴリ・リンク情報を一括収集する方法

私の研究分野では十分にドキュメント化されていないジャンルを扱っているため、Wikipediaから基礎情報を収集することが度々あります。そこで、効率的にデータを取得する方法をまとめることにしました。

Wikipediaからデータを取得する方法はいくつかありますが、今回はPythonWikipedia APIを使用した方法を紹介します。

この方法を使えば、Wikipedia記事の要約・本文・カテゴリ・リンク情報などを自動的に取得し、MarkdownJSON、テキスト形式で保存できます。また、複数の記事を一括処理することも可能です。

この記事で行っていること


環境構築

uvのインストール

uvは、Rustで書かれた超高速なPythonパッケージマネージャーで、従来のpipよりも圧倒的に高速です。

以下のようにしてインストールを行います。

# uvのインストール
$ curl -LsSf https://astral.sh/uv/install.sh | sh

インストール後、パスを通すために以下を実行します。

# bashを使用している場合
$ source ~/.bashrc

インストールは以下のように実行して確認をしてください。

$ uv --version
# 出力例: uv 0.5.0

プロジェクトのセットアップ

uvによるPythonプロジェクトの作成

先程導入したuvを使って、新しいPythonプロジェクトを作成します。

# プロジェクトディレクトリを作成
$ uv init wikipedia-scraper

# プロジェクトディレクトリに移動
$ cd wikipedia-scraper

実行後に以下のような構成のプロジェクトが生成されます。

作成されるファイル

wikipedia-scraper/
├── pyproject.toml      # プロジェクト設定ファイル
├── README.md           # プロジェクト説明
└── main.py            # サンプルファイル(削除OK)

自動生成されたmain.pyは不要なので削除しておきます。

$ rm main.py

仮想環境の作成

Pythonプロジェクトでは、プロジェクトごとに独立した仮想環境を作成するのが一般的です。

# 仮想環境を作成
$ uv venv

# 仮想環境を有効化
$ source .venv/bin/activate

仮想環境が有効化されると、プロンプトの先頭に(wikipedia-scraper)が表示されます。

(wikipedia-scraper) user@hostname:~/wikipedia-scraper$

仮想環境を終了する場合

以下のようにコマンドを実行してください。

$ deactivate

必要なライブラリのインストール

Wikipedia情報取得ツールに必要なライブラリをインストールします。

$ uv add wikipedia-api tqdm

インストールされるライブラリ

このコマンドを実行すると、pyproject.tomlに依存関係が自動的に追加されます。

pyproject.tomlの確認

$ cat pyproject.toml

以下のように依存関係が記録されています。 pyproject.tomlに記録されることで、別環境に移動しても同じ依存関係を簡単に再現できます。再構築はuv syncコマンドで行います。(pipコマンドでrequirements.txtの生成することも可能)

[project]
name = "wikipedia-scraper"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
    "tqdm>=4.67.1",
    "wikipedia-api>=0.8.1",
]


出力ディレクトリの作成

取得したWikipedia記事を保存するディレクトリを作成します。

$ mkdir output

ソースコードの実装

メインスクリプトの作成

wikipedia_scraper.pyという名前でファイルを作成します。エディタでwikipedia_scraper.pyを開き、以下のコードを記述します。

wikipedia_scraper.py

"""
Wikipedia情報取得ツール

Wikipediaから記事を検索し、要約・本文・カテゴリ・リンク・参照など
すべての利用可能なデータを取得するコマンドラインツール
"""

import argparse
import json
from pathlib import Path
from datetime import datetime
import wikipediaapi
from tqdm import tqdm


class WikipediaScraper:
    """Wikipedia情報取得クラス"""
    
    def __init__(self, language='ja'):
        """
        初期化
        
        Args:
            language (str): 言語コード (デフォルト: 'ja')
        """
        self.wiki = wikipediaapi.Wikipedia(
            user_agent='WikipediaScraper/2.0',
            language=language
        )
        self.language = language
        self.output_dir = Path('./output')
        self.output_dir.mkdir(exist_ok=True)
    
    def search_page(self, title):
        """
        Wikipedia記事を検索
        
        Args:
            title (str): 記事タイトル
            
        Returns:
            wikipediaapi.WikipediaPage or None: 記事ページ
        """
        page = self.wiki.page(title)
        if page.exists():
            return page
        else:
            print(f"警告: 記事 '{title}' が見つかりませんでした")
            return None
    
    def get_all_data(self, title):
        """
        記事のすべてのデータを取得
        
        Args:
            title (str): 記事タイトル
            
        Returns:
            dict: すべてのデータを含む辞書
        """
        page = self.search_page(title)
        if not page:
            return None
        
        data = {
            'title': page.title,
            'pageid': page.pageid,
            'url': page.fullurl,
            'canonical_url': page.canonicalurl,
            'language': self.language,
            'timestamp': datetime.now().isoformat(),
            'summary': page.summary,
            'full_text': page.text,
            'categories': self._get_categories(page),
            'sections': self._get_sections(page),
            'links': self._get_links(page),
            'references': self._get_references(page),
            'backlinks': [],  # APIの制限により取得できない
        }
        
        return data
    
    def _get_categories(self, page):
        """
        カテゴリ情報を取得
        
        Args:
            page: WikipediaPage オブジェクト
            
        Returns:
            list: カテゴリのリスト
        """
        try:
            return [cat for cat in page.categories.keys()]
        except Exception as e:
            print(f"  カテゴリ取得エラー: {e}")
            return []
    
    def _get_sections(self, page, section=None, level=0):
        """
        セクション構造を再帰的に取得
        
        Args:
            page: WikipediaPage オブジェクトまたはセクション
            section: 現在のセクション
            level: セクションの階層レベル
            
        Returns:
            list: セクション情報のリスト
        """
        sections = []
        
        if section is None:
            section = page
        
        for s in section.sections:
            section_info = {
                'title': s.title,
                'level': level,
                'text': s.text[:500] + '...' if len(s.text) > 500 else s.text,
                'subsections': self._get_sections(page, s, level + 1)
            }
            sections.append(section_info)
        
        return sections
    
    def _get_links(self, page):
        """
        リンク情報を取得
        
        Args:
            page: WikipediaPage オブジェクト
            
        Returns:
            dict: 内部リンクと外部リンクのリスト
        """
        try:
            internal_links = list(page.links.keys())[:100]  # 最初の100件のみ
            return {
                'internal_links': internal_links,
                'internal_links_count': len(page.links)
            }
        except Exception as e:
            print(f"  リンク取得エラー: {e}")
            return {
                'internal_links': [],
                'internal_links_count': 0
            }
    
    def _get_references(self, page):
        """
        参照文献情報を取得(本文から抽出)
        
        Args:
            page: WikipediaPage オブジェクト
            
        Returns:
            dict: 参照情報
        """
        text = page.text
        references_section = ""
        
        for section in page.sections:
            if any(keyword in section.title.lower() for keyword in ['参考', '脚注', '出典', 'references', 'notes']):
                references_section = section.text
                break
        
        return {
            'has_references_section': bool(references_section),
            'references_text': references_section[:1000] if references_section else ""
        }
    
    def save_to_markdown(self, title, data):
        """
        データをMarkdown形式で保存
        
        Args:
            title (str): 記事タイトル
            data (dict): 保存するデータ
        """
        safe_title = "".join(c if c.isalnum() or c in (' ', '-', '_') else '_' for c in title)
        filename = f"{safe_title}_complete.md"
        filepath = self.output_dir / filename
        
        with open(filepath, 'w', encoding='utf-8') as f:
            # ヘッダー情報
            f.write(f"# {data['title']}\n\n")
            f.write(f"**ページID**: {data['pageid']}  \n")
            f.write(f"**URL**: {data['url']}  \n")
            f.write(f"**言語**: {data['language']}  \n")
            f.write(f"**取得日時**: {data['timestamp']}  \n\n")
            f.write("---\n\n")
            
            # 要約
            f.write("## 要約\n\n")
            f.write(f"{data['summary']}\n\n")
            f.write("---\n\n")
            
            # カテゴリ
            if data['categories']:
                f.write("## カテゴリ\n\n")
                for cat in data['categories'][:20]:
                    f.write(f"- {cat}\n")
                if len(data['categories']) > 20:
                    f.write(f"\n(他 {len(data['categories']) - 20} 件)\n")
                f.write("\n---\n\n")
            
            # セクション構造
            if data['sections']:
                f.write("## セクション構造\n\n")
                self._write_sections_markdown(f, data['sections'])
                f.write("\n---\n\n")
            
            # 本文
            f.write("## 本文\n\n")
            f.write(f"{data['full_text']}\n\n")
            f.write("---\n\n")
            
            # リンク情報
            f.write("## リンク情報\n\n")
            f.write(f"**内部リンク総数**: {data['links']['internal_links_count']}\n\n")
            if data['links']['internal_links']:
                f.write("### 主要な内部リンク(最大100件)\n\n")
                for link in data['links']['internal_links'][:50]:
                    f.write(f"- {link}\n")
                if len(data['links']['internal_links']) > 50:
                    f.write(f"\n(他 {len(data['links']['internal_links']) - 50} 件)\n")
            f.write("\n---\n\n")
            
            # 参照情報
            if data['references']['has_references_section']:
                f.write("## 参照・脚注\n\n")
                f.write(f"{data['references']['references_text']}\n\n")
        
        print(f"✓ Markdown保存完了: {filepath}")
    
    def save_to_json(self, title, data):
        """
        データをJSON形式で保存
        
        Args:
            title (str): 記事タイトル
            data (dict): 保存するデータ
        """
        safe_title = "".join(c if c.isalnum() or c in (' ', '-', '_') else '_' for c in title)
        filename = f"{safe_title}_complete.json"
        filepath = self.output_dir / filename
        
        with open(filepath, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
        
        print(f"✓ JSON保存完了: {filepath}")
    
    def save_to_text(self, title, data):
        """
        データをテキスト形式で保存
        
        Args:
            title (str): 記事タイトル
            data (dict): 保存するデータ
        """
        safe_title = "".join(c if c.isalnum() or c in (' ', '-', '_') else '_' for c in title)
        filename = f"{safe_title}_complete.txt"
        filepath = self.output_dir / filename
        
        with open(filepath, 'w', encoding='utf-8') as f:
            f.write(f"タイトル: {data['title']}\n")
            f.write(f"ページID: {data['pageid']}\n")
            f.write(f"URL: {data['url']}\n")
            f.write(f"言語: {data['language']}\n")
            f.write(f"取得日時: {data['timestamp']}\n")
            f.write("=" * 70 + "\n\n")
            
            f.write("【要約】\n")
            f.write(data['summary'] + "\n\n")
            f.write("=" * 70 + "\n\n")
            
            if data['categories']:
                f.write("【カテゴリ】\n")
                for cat in data['categories']:
                    f.write(f"  - {cat}\n")
                f.write("\n" + "=" * 70 + "\n\n")
            
            f.write("【本文】\n")
            f.write(data['full_text'] + "\n\n")
            f.write("=" * 70 + "\n\n")
            
            f.write(f"【リンク情報】\n")
            f.write(f"内部リンク数: {data['links']['internal_links_count']}\n\n")
        
        print(f"✓ テキスト保存完了: {filepath}")
    
    def _write_sections_markdown(self, f, sections, indent=0):
        """
        セクション情報をMarkdown形式で書き込み
        
        Args:
            f: ファイルオブジェクト
            sections: セクション情報のリスト
            indent: インデント レベル
        """
        for section in sections:
            prefix = "  " * indent + "- "
            f.write(f"{prefix}**{section['title']}** (レベル {section['level']})\n")
            if section['subsections']:
                self._write_sections_markdown(f, section['subsections'], indent + 1)
    
    def process_single(self, title, output_format='markdown'):
        """
        単一の記事を処理
        
        Args:
            title (str): 記事タイトル
            output_format (str): 出力形式 ('markdown', 'json', 'text', 'all')
        """
        print(f"\n検索中: {title}")
        
        data = self.get_all_data(title)
        
        if data:
            if output_format in ['markdown', 'all']:
                self.save_to_markdown(title, data)
            if output_format in ['json', 'all']:
                self.save_to_json(title, data)
            if output_format in ['text', 'all']:
                self.save_to_text(title, data)
        else:
            print(f"✗ 記事 '{title}' の取得に失敗しました")
    
    def process_multiple(self, input_file, output_format='markdown'):
        """
        複数の記事を一括処理
        
        Args:
            input_file (str): 検索キーワードが書かれたテキストファイル
            output_format (str): 出力形式
        """
        try:
            with open(input_file, 'r', encoding='utf-8') as f:
                titles = [line.strip() for line in f if line.strip()]
            
            print(f"\n{len(titles)}件の記事を処理します...\n")
            
            for title in tqdm(titles, desc="記事取得中", unit="記事"):
                self.process_single(title, output_format)
            
            print(f"\n完了! {len(titles)}件の記事を処理しました")
        
        except FileNotFoundError:
            print(f"エラー: ファイル '{input_file}' が見つかりません")
        except Exception as e:
            print(f"エラー: {e}")


def main():
    """メイン関数"""
    parser = argparse.ArgumentParser(
        description='Wikipediaから記事のすべてのデータを取得するツール',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
使用例:
  # 単一記事をMarkdown形式で取得(デフォルト)
  python wikipedia_scraper.py "Python"
  
  # 単一記事をJSON形式で取得
  python wikipedia_scraper.py "機械学習" --format json
  
  # すべての形式で保存
  python wikipedia_scraper.py "Python" --format all
  
  # 複数記事を一括取得
  python wikipedia_scraper.py --inputs keywords.txt
  
  # 英語版Wikipediaから取得
  python wikipedia_scraper.py "Python" --lang en
        """
    )
    
    parser.add_argument(
        'title',
        nargs='?',
        help='Wikipedia記事のタイトル'
    )
    
    parser.add_argument(
        '--inputs',
        type=str,
        help='検索キーワードが書かれたテキストファイル(1行1キーワード)'
    )
    
    parser.add_argument(
        '--format',
        type=str,
        choices=['markdown', 'json', 'text', 'all'],
        default='markdown',
        help='出力形式 (デフォルト: markdown)'
    )
    
    parser.add_argument(
        '--lang',
        type=str,
        default='ja',
        help='言語コード (デフォルト: ja)'
    )
    
    args = parser.parse_args()
    
    # 引数チェック
    if not args.title and not args.inputs:
        parser.print_help()
        print("\nエラー: 記事タイトルまたは--inputsオプションを指定してください")
        return
    
    # スクレイパーの初期化
    scraper = WikipediaScraper(language=args.lang)
    
    # 処理実行
    if args.inputs:
        scraper.process_multiple(args.inputs, args.format)
    else:
        scraper.process_single(args.title, args.format)


if __name__ == '__main__':
    main()

プログラムの実行方法

単一記事の取得(Markdown形式で保存)

$ python wikipedia_scraper.py "Python"

実行すると、output/ディレクトリにPython_complete.mdが作成されます。

単一記事の取得(JSON形式で保存)

$ python wikipedia_scraper.py "機械学習" --format json

単一記事の取得(すべての形式で保存)

$ python wikipedia_scraper.py "人工知能" --format all

これで、MarkdownJSON、テキストの3形式すべてで保存されます。


複数記事の一括取得

keywords.txtの作成

複数の記事を取得する場合には、まず、取得したい記事のタイトルを記載したファイルであるkeywords.txtを作成します。

$ nano keywords.txt

記載例

Python
機械学習
人工知能
データサイエンス
Node-RED
自然言語処理
深層学習

一括取得の実行

$ python wikipedia_scraper.py --inputs keywords.txt

実行すると、プログレスバーが表示され、取得の進捗状況が確認できます。


英語版Wikipediaから取得

$ python wikipedia_scraper.py "Python" --lang en

出力結果の確認

実行後、output/ディレクトリに以下のようなファイルが作成されます:

output/
├── Python_complete.md
├── 機械学習_complete.md
├── 人工知能_complete.json
└── ...


活用のアイデア

自分はRAGとして使用するためにデータを取得したこともあるので、以下のような活用を考えています。

RAGシステムのナレッジベース構築

取得したWikipediaデータを、LLMのRAG(Retrieval-Augmented Generation)システムのナレッジベースとして活用

# LangChainやLlamaIndexと組み合わせる例
from langchain.document_loaders import DirectoryLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 取得したMarkdownファイルを読み込み
loader = DirectoryLoader('./output', glob="**/*.md")
documents = loader.load()

# チャンク分割してベクトルDBに格納
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000)
chunks = text_splitter.split_documents(documents)

ナレッジグラフの構築

カテゴリやリンク情報を使って、Neo4jなどのグラフデータベースに格納する。

from neo4j import GraphDatabase

# Wikipedia記事間の関係性を可視化
# カテゴリによる関連記事の発見
# リンク構造の分析

おわりに

今回はPythonを使って、Wikipediaから効率的にデータを取得する方法を紹介しました。これを活用して、研究やプロジェクトの基礎情報収集を自動化し、時間を節約してください。

⚠️この手法は取得するデータが少ないときには有効ですが、多くなる場合には直接Wikipediaアーカイブデータをダウンロードする方がいいと思います。


ソースコード一式

github.com


参考リンク




以上の内容はhttps://uepon.hatenadiary.com/entry/2025/11/10/233001より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14