Goose3: Pythonで記事を抽出する強力なツール

## 第1章: Goose3の概要

Goose3は、ウェブページから記事の本文や関連メタデータを抽出するためのPythonライブラリです。ニュース記事やブログ投稿などのウェブコンテンツから重要な情報を自動的に取得するのに役立ちます。

Goose3の主な特徴:
- 記事の本文を抽出
- メインイメージの特定
- メタデータ（タイトル、著者、公開日など）の抽出
- 埋め込み動画の検出

以下は、Goose3を使用して記事を抽出する基本的な例です:

```python
from goose3 import Goose

url = 'https://example.com/news-article'
g = Goose()
article = g.extract(url=url)

print(article.title)
print(article.cleaned_text[:200])  # 最初の200文字を表示
```

## 第2章: Goose3のインストール

Goose3は、pipを使用して簡単にインストールできます。以下のコマンドを実行してください:

```
pip install goose3
```

特定の言語のサポートが必要な場合は、以下のようにインストールします:

```
pip install goose3[japanese]
```

## 第3章: 基本的な使用方法

Goose3の基本的な使い方を見てみましょう:

```python
from goose3 import Goose

def extract_article(url):
    g = Goose()
    article = g.extract(url=url)
    
    print(f"タイトル: {article.title}")
    print(f"本文: {article.cleaned_text[:200]}...")
    print(f"キーワード: {article.keywords}")
    print(f"メタ説明: {article.meta_description}")
    
    if article.top_image:
        print(f"トップ画像: {article.top_image.src}")

url = 'https://example.com/news-article'
extract_article(url)
```

この例では、URLから記事を抽出し、タイトル、本文の一部、キーワード、メタ説明、トップ画像のURLを表示しています。

## 第4章: 設定のカスタマイズ

Goose3の動作をカスタマイズするには、設定オプションを使用します:

```python
from goose3 import Goose
from goose3.configuration import Configuration

config = Configuration()
config.browser_user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
config.strict = True  # より厳密な抽出を行う

g = Goose(config)
article = g.extract(url='https://example.com/news-article')
```

この例では、ユーザーエージェントを設定し、より厳密な抽出モードを有効にしています。

## 第5章: 複数言語のサポート

Goose3は複数の言語をサポートしています。言語を指定するには:

```python
from goose3 import Goose

g = Goose({'use_meta_language': False, 'target_language': 'ja'})
article = g.extract(url='https://example.com/japanese-article')

print(article.cleaned_text[:200])
```

この例では、日本語の記事を抽出するように設定しています。

## 第6章: ローカルHTMLファイルの処理

Goose3はURLだけでなく、ローカルのHTMLファイルも処理できます:

```python
from goose3 import Goose

with open('local_article.html', 'r', encoding='utf-8') as f:
    html_content = f.read()

g = Goose()
article = g.extract(raw_html=html_content)

print(article.title)
print(article.cleaned_text[:200])
```

この例では、ローカルのHTMLファイルを読み込み、その内容を解析しています。

## 第7章: 画像の抽出

Goose3は記事のメイン画像を抽出する機能も持っています:

```python
from goose3 import Goose

def extract_images(url):
    g = Goose()
    article = g.extract(url=url)
    
    if article.top_image:
        print(f"トップ画像: {article.top_image.src}")
    
    print("その他の画像:")
    for image in article.images:
        print(image.src)

url = 'https://example.com/image-rich-article'
extract_images(url)
```

この関数は、記事のトップ画像とその他のすべての画像のURLを表示します。

## 第8章: メタデータの抽出

記事のメタデータを詳細に抽出する方法を見てみましょう:

```python
from goose3 import Goose

def extract_metadata(url):
    g = Goose()
    article = g.extract(url=url)
    
    print(f"タイトル: {article.title}")
    print(f"著者: {article.authors}")
    print(f"公開日: {article.publish_date}")
    print(f"メタキーワード: {article.meta_keywords}")
    print(f"メタ説明: {article.meta_description}")
    print(f"タグ: {article.tags}")

url = 'https://example.com/news-article'
extract_metadata(url)
```

この関数は、記事のタイトル、著者、公開日、メタキーワード、メタ説明、タグを抽出して表示します。

## 第9章: コンテンツの要約

Goose3で抽出したテキストを要約するには、別のライブラリ（例：sumy）と組み合わせることができます:

```python
from goose3 import Goose
from sumy.parsers.plaintext import PlaintextParser
from sumy.nlp.tokenizers import Tokenizer
from sumy.summarizers.lex_rank import LexRankSummarizer

def summarize_article(url, sentences_count=3):
    g = Goose()
    article = g.extract(url=url)
    
    parser = PlaintextParser.from_string(article.cleaned_text, Tokenizer("japanese"))
    summarizer = LexRankSummarizer()
    summary = summarizer(parser.document, sentences_count)
    
    print(f"タイトル: {article.title}")
    print("要約:")
    for sentence in summary:
        print(sentence)

url = 'https://example.com/long-article'
summarize_article(url)
```

この関数は、Goose3で記事を抽出し、sumyを使用して3文の要約を生成します。

## 第10章: エラー処理

Goose3を使用する際のエラー処理の例を見てみましょう:

```python
from goose3 import Goose
from goose3.network import NetworkError

def safe_extract(url):
    try:
        g = Goose()
        article = g.extract(url=url)
        return article.cleaned_text
    except NetworkError:
        print(f"ネットワークエラー: {url} にアクセスできません")
    except Exception as e:
        print(f"予期せぬエラー: {e}")
    return None

urls = [
    'https://example.com/valid-article',
    'https://example.com/nonexistent-page',
    'https://invalid-domain.com/article'
]

for url in urls:
    content = safe_extract(url)
    if content:
        print(f"{url}: 抽出成功")
    else:
        print(f"{url}: 抽出失敗")
```

この例では、ネットワークエラーや他の例外を適切に処理しています。

## 第11章: 並行処理

多数の記事を効率的に処理するために、並行処理を使用できます:

```python
from goose3 import Goose
from concurrent.futures import ThreadPoolExecutor
import time

def extract_article(url):
    g = Goose()
    try:
        article = g.extract(url=url)
        return url, article.title, len(article.cleaned_text)
    except Exception as e:
        return url, None, str(e)

urls = [
    'https://example.com/article1',
    'https://example.com/article2',
    'https://example.com/article3',
    # ... 多数のURL
]

def process_urls(urls):
    start_time = time.time()
    with ThreadPoolExecutor(max_workers=5) as executor:
        results = list(executor.map(extract_article, urls))
    
    for url, title, content_length in results:
        if title:
            print(f"{url}: {title} ({content_length} 文字)")
        else:
            print(f"{url}: 抽出失敗 - {content_length}")
    
    print(f"処理時間: {time.time() - start_time:.2f} 秒")

process_urls(urls)
```

この例では、ThreadPoolExecutorを使用して複数のURLを並行して処理しています。

## 第12章: カスタム抽出ルール

Goose3のデフォルトの抽出ルールをカスタマイズする方法を見てみましょう:

```python
from goose3 import Goose
from goose3.extractors import ContentExtractor

class CustomContentExtractor(ContentExtractor):
    def get_title(self):
        # カスタムのタイトル抽出ロジック
        title = self.article.doc.xpath('//h1[@class="custom-title"]')
        if title:
            return title[0].text_content().strip()
        return super().get_title()

class CustomGoose(Goose):
    def __init__(self, config=None):
        super().__init__(config)
        self.extractor = CustomContentExtractor(self.config)

g = CustomGoose()
article = g.extract(url='https://example.com/custom-article')
print(f"カスタム抽出タイトル: {article.title}")
```

この例では、カスタムのタイトル抽出ロジックを実装しています。

## 第13章: コンテンツのフィルタリング

抽出したコンテンツをフィルタリングする例を見てみましょう:

```python
from goose3 import Goose
import re

def filter_content(text):
    # 広告や不要なテキストを削除
    text = re.sub(r'広告|スポンサーリンク|関連記事', '', text)
    # 空行を削除
    text = re.sub(r'\n\s*\n', '\n', text)
    return text.strip()

def extract_and_filter(url):
    g = Goose()
    article = g.extract(url=url)
    
    filtered_content = filter_content(article.cleaned_text)
    
    print(f"タイトル: {article.title}")
    print("フィルタリング後のコンテンツ:")
    print(filtered_content[:500])  # 最初の500文字を表示

url = 'https://example.com/article-with-ads'
extract_and_filter(url)
```

この例では、抽出されたテキストから広告や不要なコンテンツを削除しています。

## 第14章: データベースへの保存

抽出した記事をデータベースに保存する例を見てみましょう:

```python
from goose3 import Goose
import sqlite3
import datetime

def extract_and_save(url):
    g = Goose()
    article = g.extract(url=url)
    
    conn = sqlite3.connect('articles.db')
    c = conn.cursor()
    
    # テーブルが存在しない場合は作成
    c.execute('''CREATE TABLE IF NOT EXISTS articles
                 (url TEXT PRIMARY KEY, title TEXT, content TEXT, extracted_date TEXT)''')
    
    # データを挿入
    c.execute("INSERT OR REPLACE INTO articles VALUES (?, ?, ?, ?)",
              (url, article.title, article.cleaned_text, datetime.datetime.now().isoformat()))
    
    conn.commit()
    conn.close()
    
    print(f"記事が保存されました: {article.title}")

urls = [
    'https://example.com/article1',
    'https://example.com/article2',
    'https://example.com/article3'
]

for url in urls:
    extract_and_save(url)
```

この例では、抽出した記事をSQLiteデータベースに保存しています。

## 第15章: APIの作成

Goose3を使用してシンプルなWeb APIを作成する例を見てみましょう:

```python
from flask import Flask, request, jsonify
from goose3 import Goose

app = Flask(__name__)
g = Goose()

@app.route('/extract', methods=['POST'])
def extract_article():
    data = request.json
    url = data.get('url')
    
    if not url:
        return jsonify({"error": "URLが提供されていません"}), 400
    
    try:
        article = g.extract(url=url)
        return jsonify({
            "title": article.title,
            "content": article.cleaned_text,
            "authors": article.authors,
            "publish_date": str(article.publish_date),
            "top_image": article.top_image.src if article.top_image else None
        })
    except Exception as e:
        return jsonify({"error": str(e)}), 500

if __name__ == '__main__':
    app.run(debug=True)
```

この例では、Flaskを使用してGoose3の機能をWeb APIとして公開しています。URLを受け取り、抽出した記事の情報をJSON形式で返します。

## 第16章: 複数のURLの一括処理

複数のURLを効率的に処理する方法を見てみましょう：

```python
from goose3 import Goose
from concurrent.futures import ThreadPoolExecutor
import csv

def extract_article(url):
    g = Goose()
    try:
        article = g.extract(url=url)
        return {
            'url': url,
            'title': article.title,
            'content': article.cleaned_text[:500],  # 最初の500文字
            'authors': ', '.join(article.authors),
            'publish_date': str(article.publish_date)
        }
    except Exception as e:
        return {'url': url, 'error': str(e)}

def process_urls(urls, output_file):
    with ThreadPoolExecutor(max_workers=5) as executor:
        results = list(executor.map(extract_article, urls))
    
    with open(output_file, 'w', newline='', encoding='utf-8') as f:
        writer = csv.DictWriter(f, fieldnames=['url', 'title', 'content', 'authors', 'publish_date', 'error'])
        writer.writeheader()
        for result in results:
            writer.writerow(result)

urls = [
    'https://example.com/article1',
    'https://example.com/article2',
    'https://example.com/article3',
    # ... 多数のURL
]

process_urls(urls, 'extracted_articles.csv')
print("処理が完了し、結果がCSVファイルに保存されました。")
```

この例では、複数のURLを並行して処理し、結果をCSVファイルに保存しています。

## 第17章: HTMLクリーニングの改善

Goose3が抽出したHTMLをさらにクリーニングする方法を見てみましょう：

```python
from goose3 import Goose
from bs4 import BeautifulSoup

def clean_html(html_content):
    soup = BeautifulSoup(html_content, 'html.parser')
    
    # 不要なタグを削除
    for tag in soup(['script', 'style', 'iframe', 'meta', 'link']):
        tag.decompose()
    
    # クラスやIDに基づいて不要な要素を削除
    for element in soup.find_all(class_=['advertisement', 'sidebar', 'comment']):
        element.decompose()
    
    # テキストのみを抽出
    text = soup.get_text(separator='\n', strip=True)
    
    # 空行を削除
    lines = [line for line in text.split('\n') if line.strip()]
    cleaned_text = '\n'.join(lines)
    
    return cleaned_text

def extract_and_clean(url):
    g = Goose()
    article = g.extract(url=url)
    
    cleaned_content = clean_html(article.cleaned_text)
    
    print(f"タイトル: {article.title}")
    print("クリーニング後のコンテンツ:")
    print(cleaned_content[:500])  # 最初の500文字を表示

url = 'https://example.com/messy-article'
extract_and_clean(url)
```

この例では、BeautifulSoupを使用してGoose3が抽出したHTMLをさらにクリーニングしています。

## 第18章: センチメント分析の統合

Goose3で抽出した記事のセンチメント（感情）分析を行う例を見てみましょう：

```python
from goose3 import Goose
from textblob import TextBlob
import nltk
nltk.download('punkt')

def analyze_sentiment(text):
    blob = TextBlob(text)
    sentiment = blob.sentiment.polarity
    if sentiment > 0:
        return "ポジティブ"
    elif sentiment < 0:
        return "ネガティブ"
    else:
        return "中立"

def extract_and_analyze(url):
    g = Goose()
    article = g.extract(url=url)
    
    sentiment = analyze_sentiment(article.cleaned_text)
    
    print(f"タイトル: {article.title}")
    print(f"センチメント: {sentiment}")
    print("本文の一部:")
    print(article.cleaned_text[:300])

url = 'https://example.com/news-article'
extract_and_analyze(url)
```

この例では、TextBlobライブラリを使用して、抽出した記事のセンチメント分析を行っています。

## 第19章: 記事の類似性比較

Goose3で抽出した複数の記事の類似性を比較する方法を見てみましょう：

```python
from goose3 import Goose
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity

def extract_articles(urls):
    g = Goose()
    articles = []
    for url in urls:
        try:
            article = g.extract(url=url)
            articles.append({
                'url': url,
                'title': article.title,
                'content': article.cleaned_text
            })
        except Exception as e:
            print(f"エラー ({url}): {e}")
    return articles

def compare_articles(articles):
    contents = [article['content'] for article in articles]
    
    vectorizer = TfidfVectorizer()
    tfidf_matrix = vectorizer.fit_transform(contents)
    
    similarity_matrix = cosine_similarity(tfidf_matrix)
    
    for i, article1 in enumerate(articles):
        for j, article2 in enumerate(articles):
            if i < j:
                similarity = similarity_matrix[i][j]
                print(f"類似度 ({article1['title']} - {article2['title']}): {similarity:.2f}")

urls = [
    'https://example.com/article1',
    'https://example.com/article2',
    'https://example.com/article3'
]

articles = extract_articles(urls)
compare_articles(articles)
```

この例では、TF-IDFとコサイン類似度を使用して、抽出した記事間の類似性を計算しています。

## 第20章: 記事の自動分類

Goose3で抽出した記事を自動的に分類する例を見てみましょう：

```python
from goose3 import Goose
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.model_selection import train_test_split

# 訓練データ（実際のプロジェクトではもっと多くのデータが必要です）
training_data = [
    {"text": "株価が上昇し、投資家たちは喜んでいる。", "category": "経済"},
    {"text": "新しい映画が大ヒットを記録した。", "category": "エンターテイメント"},
    {"text": "サッカーチームが優勝を果たした。", "category": "スポーツ"},
    # ... より多くの訓練データ
]

# 分類器の訓練
texts = [item["text"] for item in training_data]
categories = [item["category"] for item in training_data]

vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(texts)
X_train, X_test, y_train, y_test = train_test_split(X, categories, test_size=0.2, random_state=42)

classifier = MultinomialNB()
classifier.fit(X_train, y_train)

def classify_article(url):
    g = Goose()
    article = g.extract(url=url)
    
    content_vector = vectorizer.transform([article.cleaned_text])
    category = classifier.predict(content_vector)[0]
    
    print(f"タイトル: {article.title}")
    print(f"分類: {category}")
    print("本文の一部:")
    print(article.cleaned_text[:300])

url = 'https://example.com/news-article'
classify_article(url)
```

この例では、ナイーブベイズ分類器を使用して、抽出した記事を自動的に分類しています。実際の使用では、より多くの訓練データと、より洗練された機械学習モデルが必要になるでしょう。

以上で、Goose3の高度な使用方法と、関連するテキスト処理タスクの例を20章にわたって解説しました。これらの例を組み合わせることで、Goose3を使用した複雑なウェブコンテンツ分析システムを構築することができます。Goose3は非常に柔軟なツールであり、様々なテキスト処理タスクと組み合わせることで、その可能性を最大限に引き出すことができます。