インクリメンタルクローラの説明と実装

今日は、主にインクリメンタル クローラーの導入に基づいて、クローラー テクノロジーについていくつか紹介します。

1. 爬虫類の概念

いわゆるクローラーは本質的にはコンピューター プログラムであり、その動作は、インターネットの「ネット」に沿って 1 行ずつ「這う」蜘蛛の巣の上を這うように見えます。そのため、爬虫類は英語で「スパイダー」とも呼ばれ、スパイダーという言葉になります。
爬虫の主な目的は、Web コンテンツを取得して解析することです。
インターネットの発展の勢いに乗って、さまざまな開発技術が日々変化しており、さまざまなシナリオのニーズを満たすために多くの種類の Web クローラーが派生しています。Web クローラーは、系统结构とに従って、一般 Web クローラー集中型 Web クローラーインクリメンタル Web クローラー (幅優先)ディープ Web クローラー (深さ優先)实现技术の 4 種類に大別できます

その他の爬虫類テクノロジーについては、「【浅いところから深いところまで】集める価値のある爬虫類テクノロジー、調べてみましょう~」を参照してください。

1.1. インクリメンタルクローラー

インクリメンタル Web クローラーとは、ダウンロードされた Web ページを段階的に更新し、新しく生成または変更された Web ページのみを取得する Web クローラーを指します。
インクリメンタル Web クローラーは、新しく生成された Web ページまたはコンテンツが変更された Web ページのみをクロールし、コンテンツが変更されていない Web ページを再クロールしません。これにより、Web ページのダウンロード量を効果的に削減し、アクセス時間とストレージ容量の消費を削減できます。 Web クローリング アルゴリズムの複雑さと実装の難易度が増加します。

1.2. ディープクローラー

ディープ Web クローラー (Deep Web Crawler) は、深い Web ページを取得する Web クローラーを指します。取得したい Web ページは比較的深いものであり、それらを自動的に取得するには特定の追加戦略が必要ですが、これは実装が困難です。

2. インクリメンタル クローラーの実装手順

インクリメンタル クローラーを作成する一般的な手順は次のとおりです。

  1. データ ストレージ構造を定義して、クロールされた Web ページと、最終アクセス時刻、ページ コンテンツのハッシュなどの関連情報を記録します。データベースまたはファイル システムに保存できます。

  2. クロールする必要がある Web サイトを決定し、クローラーの開始点を設計します。

  3. 開始点からページをクロールし、ページから必要な情報を抽出します。

  4. 抽出された情報を処理して保存し、クロールされたページの関連情報を更新します。

  5. 更新されたページおよび新しいページの可能性がある場合は、すべてのクロール タスクが完了するまで上記の手順を繰り返します。

  6. ターゲット Web サイトへの過度の負荷を避けるために、クロールの深さ、クロール速度などのいくつかの制限を追加できます。

インクリメンタル クローラーは、すでにクロールされたページを更新および重複除去する必要があり、ページのハッシュ値またはその他の一意の識別子に基づいて比較および判断できることに注意してください。同時に、インクリメンタル クローラーは更新頻度の高いページを処理する必要がある場合があるため、実際の状況に応じて設計および最適化する必要があります。

3. インクリメンタルクローラの場合

以下は、ニュース Web サイトからニュース記事のタイトルとリンクをクロールし、増分クローラ機能を実現するために使用される単純な増分クローラーのケースです。

import requests
import hashlib
import time
import pymysql
from bs4 import BeautifulSoup

# 定义爬虫起始点和目标站点
start_url = 'https://news.example.com'
target_site = 'example.com'

# 定义数据库连接和数据表结构
conn = pymysql.connect(host='localhost', user='root', password='password', database='news_db', charset='utf8mb4')
cursor = conn.cursor()
cursor.execute('''
    CREATE TABLE IF NOT EXISTS news (
        id INT PRIMARY KEY AUTO_INCREMENT,
        title TEXT,
        url TEXT,
        hash TEXT,
        last_modified BIGINT
    )
''')

# 定义页面哈希函数
def get_hash(url, content):
    return hashlib.sha1(f'{
      
      url}{
      
      content}'.encode('utf-8')).hexdigest()

# 定义页面访问函数
def fetch_page(url):
    response = requests.get(url)
    if response.status_code == 200:
        return response.text
    else:
        return None

# 定义页面解析函数
def parse_page(content):
    soup = BeautifulSoup(content, 'html.parser')
    news_list = soup.find_all('a', {
    
    'class': 'news-link'})
    for news in news_list:
        title = news.text.strip()
        url = news['href'].strip()
        yield (title, url)

# 定义增量爬取函数
def crawl_incremental():
    last_modified = None
    cursor.execute('SELECT MAX(last_modified) FROM news')
    row = cursor.fetchone()
    if row[0]:
        last_modified = row[0]
    current_url = start_url
    while True:
        content = fetch_page(current_url)
        if content is None:
            break
        hash_value = get_hash(current_url, content)
        if hash_value in visited_urls:
            break
        visited_urls.add(hash_value)
        for title, url in parse_page(content):
            if target_site in url:
                cursor.execute('SELECT * FROM news WHERE url = %s', (url,))
                row = cursor.fetchone()
                if row:
                    # 已经爬取过该页面,检查是否需要更新
                    if row[4] < last_modified:
                        # 页面已经更新,重新爬取
                        cursor.execute('DELETE FROM news WHERE url = %s', (url,))
                        conn.commit()
                        crawl_url(url)
                    else:
                        # 页面没有更新,跳过
                        pass
                else:
                    # 第一次爬取该页面
                    cursor.execute('INSERT INTO news (title, url, hash, last_modified) VALUES (%s, %s, %s, %s)',
                                   (title, url, get_hash(url, fetch_page(url)), int(time.time())))
                    conn.commit()
        current_url = get_next_url(content)

# 定义获取下一个页面链接的函数
def get_next_url(content):
    # 略去具体实现
    pass

# 开始爬取
visited_urls = set()
crawl_incremental()

# 关闭数据库连接
conn.close()

具体的には、インクリメンタル クローリング機能ではcrawl_incremental()、データベース内の既存のニュース ページの最終更新時刻をクエリし、SELECT ステートメントを使用して特定のページがデータベースに既に存在するかどうかを確認することで、インクリメンタル クローリングを実装します。データベースにページのレコードがすでにある場合は、ページの最終変更時刻を確認します。時刻が既存の最終変更時刻よりも早い場合は、ページが更新されており、再度クロールする必要があることを意味します。スキップ。データベースにページの記録がない場合、そのページは新しいため、データベースに挿入する必要があります。

MySQL データベースの操作には、接続の確立、カーソルのオープン、トランザクションの送信などの手順が必要であるため、データを正しく挿入または削除できるように、関連する操作を適切な場所で実行する必要があることに注意してください。さらに、MySQL データベースは、中国語などの非 ASCII 文字の保存とクエリをサポートする文字セットを指定する必要があるため、データベースに接続するときに文字セットを utf8mb4 として指定する必要があります。

要約すると、上記は MySQL データベースを使用して増分クローラーを実装するためのサンプル コードです。実際のアプリケーションでは、特定のクローリング要件とデータ保存方法に従って、対応する変更と最適化を行う必要があります。

注:
yield は、ジェネレーター関数のステートメントに使用される Python のキーワードです。関数が yield ステートメントまで実行されると、値を返して関数の実行を一時停止し、次の呼び出しが続行されるまで待機します。したがって、ジェネレーター関数は複数の値を返すことができ、値が返されるたびに関数の実行を一時停止することができ、すべての値を一度に生成することによって引き起こされるパフォーマンスの問題を回避できます。

上記のコードで、parse_page 関数は、ニュース Web サイトの HTML ページを解析し、ニュースのタイトルとリンクを返すジェネレーター関数です。parse_page 関数が呼び出されると、すべてのニュース タイトルとリンクが一度に返されるのではなく、yield キーワードを通じて 1 つずつ返されます。具体的には、parse_page 関数は、最初に BeautifulSoup ライブラリを使用して HTML ページを解析し、次に find_all メソッドを使用してすべての<a class="news-link">要素を検索し、要素の text 属性と href 属性をニュース タイトルとリンクとして 1 つずつ抽出して返します。 yield キーワードを介して 1 つずつ。クローラー関数では、parse_page 関数の戻り値を for ... in ... ループで 1 つずつたどって、各ニュースのタイトルとリンクを取得し、後続の処理を実行できます。

つまり、yield キーワードはジェネレーター関数の重要なステートメントであり、関数の戻り値を 1 つずつ生成して返すことができ、一度にすべての値を生成することによって引き起こされるパフォーマンスの問題を回避し、また、反復処理

おすすめ

転載: blog.csdn.net/qq_44723773/article/details/129051978