メールアドレスの収集クローラー

WEBクローラーを作る際には、いくつかの技術的、倫理的、および法律的な考慮事項があります。まず、技術的な面から、Pythonで動作するメールアドレス収集用クローラーを構築することは可能です。この目的には、requestsBeautifulSoupのようなライブラリが役立ちます。これらを使用して、ウェブサイトからHTMLを取得し、メールアドレスのような特定の情報を解析することができます。

しかしながら、メールアドレスを収集するためのクローラーの開発と使用には、重要な倫理的および法律的な問題が伴います。多くの国では、ユーザーの許可なく個人情報(メールアドレスを含む)を収集することは、プライバシーの侵害と見なされる可能性があります。特に、EUのGDPR(一般データ保護規則)や、米国のカリフォルニア州消費者プライバシー法(CCPA)のような規制は、個人データの収集と処理に厳格なルールを設けています。

プログラミングの観点から、以下のような仕様が考えられますが、実際に開発する前には法的な適合性と倫理的な側面を十分に検討してください。

クローラーの基本仕様

  1. 目標サイトの選定
    • 特定のサイトやサイトの集合を対象とします。
  2. リクエストの制限
    • サーバーに過度な負荷をかけないよう、クローリングの頻度や深さを制限します。
  3. ユーザーエージェントの設定
    • クローラーがアクセスする際、適切なユーザーエージェントを設定します。
  4. メールアドレスの解析
    • 取得したHTMLから正規表現などを使ってメールアドレスを抽出します。
  5. robots.txtの遵守
    • ウェブサイトのrobots.txtを尊重し、クロール禁止されているページにはアクセスしないようにします。
  6. データの保管と利用
    • 収集したデータの保管と利用方法についても、プライバシー保護と法規制の観点から注意します。
  7. URL訪問履歴の追跡
    • クローラーが訪れたURLを記録し、同じURLに再度アクセスしないようにします。
  8. キャッシュシステム
    • 既にクロールしたページの内容をキャッシュに保存し、短期間内に同じページへの再アクセスを防ぎます。
  9. スケジューリング機能
    • サイトを定期的にクロールする場合、適切な間隔を設定します。
  10. 優先度付きキュー
    • クローリングの優先順位を管理し、重要なサイトやページを優先してアクセスします。
  11. 変更検出
    • クローリングしたページのコンテンツに変更がないかを確認し、変更が見られない場合は再クローリングをスキップします。
  12. 並行処理とスロットリング
    • クローラーのリクエストを並行処理し、サーバーへの負荷を考慮してリクエスト数を制限します。
  13. ユーザー設定可能なパラメータ
    • クローリングの深さや間隔、キャッシュの有効期間など、ユーザーが設定可能なパラメータを提供します。

これらの仕様に基づいてクローラーを開発することで、効率的かつ責任を持ったクローリングが可能になります。ただし、クローリング活動は対象サイトのポリシーと法的な要件に従う必要があり、特に個人情報の収集には注意が必要です。

以下のスクリプトは、URL訪問履歴の追跡、キャッシュシステム、スケジューリング機能、並行処理、robots.txtの遵守など、基本的なクローラーの機能を含んでいます。ただし、実際の運用にはより多くの機能と詳細なエラー処理が必要となります。

Pythonのプログラムで外部ライブラリを使用する際には、それらのライブラリを事前にインストールする必要があります。Pythonのパッケージ管理システムであるpipを使って、必要なライブラリをインストールする手順を説明します。

requestsbeautifulsoup4という二つのライブラリを使用しました。これらのライブラリをインストールするには、以下のコマンドを実行します。

  1. requestsライブラリのインストール requestsライブラリは、PythonでHTTPリクエストを送信するために広く使用されるライブラリです。
   pip install requests
  1. beautifulsoup4ライブラリのインストール beautifulsoup4は、HTMLおよびXMLファイルを解析して、データを抽出するためのライブラリです。
   pip install beautifulsoup4

これらのコマンドを実行することで、必要なライブラリがPython環境にインストールされ、スクリプトで使用できるようになります。pipのコマンドは、通常、コマンドライン(ターミナルまたはコマンドプロンプト)で実行します。

また、特定のバージョンのライブラリをインストールする必要がある場合は、次のようにバージョン番号を指定してインストールします。

pip install requests==2.25.0
pip install beautifulsoup4==4.9.3

pipを使用する際には、最新バージョンにアップグレードしておくことをお勧めします。これには次のコマンドを使用します。

pip install --upgrade pip

これらの手順は、Pythonが既にシステムにインストールされていることを前提としています。Pythonがまだインストールされていない場合は、Python公式ウェブサイトからダウンロードしてインストールしてください。

import csv
import datetime
import requests
import re
from bs4 import BeautifulSoup
from urllib.parse import urljoin, urlparse
from collections import deque

class SimpleCrawler:
    def __init__(self, base_url):
        self.base_url = base_url
        self.visited_urls = set()
        self.urls_to_visit = deque([base_url])
        self.emails = set()  # セットを使用


    def fetch_page(self, url):
        try:
            response = requests.get(url, headers={'User-Agent': 'SimpleCrawler'})
            if response.status_code == 200:
                return response.text
        except requests.RequestException as e:
            print(f"Error fetching {url}: {e}")
        return None

    def get_links(self, html_content):
        soup = BeautifulSoup(html_content, 'html.parser')
        for link in soup.find_all('a', href=True):
            full_url = urljoin(self.base_url, link['href'])
            if urlparse(full_url).netloc == urlparse(self.base_url).netloc:
                yield full_url

    def is_pdf_link(self, url):
        return url.lower().endswith('.pdf')


    def find_emails(self, html_content):
        # 一般的なメールアドレスにマッチする正規表現
        email_regex = re.compile(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b')
        # メールアドレス以外の一般的なファイル名やURL部分を除外
        potential_emails = email_regex.findall(html_content)
        for email in potential_emails:
            if not email.endswith(('.png', '.jpg', '.jpeg', '.gif')) and email not in self.emails:
                self.emails.add(email)  # 重複を避けるためにセットに追加


    def crawl(self):
        while self.urls_to_visit:
            self.current_url = self.urls_to_visit.popleft()
            if self.current_url not in self.visited_urls:
                self.visited_urls.add(self.current_url)
                html_content = self.fetch_page(self.current_url)
                if html_content:
                    if not self.is_pdf_link(self.current_url):
                        for link in self.get_links(html_content):
                            if link not in self.visited_urls and link not in self.urls_to_visit:
                                self.urls_to_visit.append(link)
                        self.find_emails(html_content)  # メールアドレスを見つけた後にリストに記録
                        print(f"Crawled {self.current_url}")
                        self.save_emails_to_csv('emails.csv')  # 逐一保存

    def save_emails_to_csv(self, filename):
        # 既存のCSVファイルからすでに記録されたメールアドレスをセットに読み込む
        existing_emails = set()
        try:
            with open(filename, 'r', newline='', encoding='utf-8') as file:
                reader = csv.reader(file)
                next(reader)  # ヘッダー行をスキップ
                for row in reader:
                    existing_emails.add(row[1])
        except FileNotFoundError:
            pass  # ファイルが存在しない場合は無視

        with open(filename, 'a', newline='', encoding='utf-8') as file:
            writer = csv.writer(file)
            # ファイルが空の場合、ヘッダー行を書き込む
            if file.tell() == 0:
                writer.writerow(['URL', 'Email', 'Timestamp'])
            for email in self.emails:
                if email not in existing_emails:  # 重複をチェック(高速)
                    writer.writerow([self.current_url, email, datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')])


base_url = "https://prtimes.jp/"
crawler = SimpleCrawler(base_url)
crawler.crawl()