Python Scrapフレームワークの教育、フードフォーラムデータのクロール

この記事のテキストと写真はインターネットからのものであり、学習とコミュニケーションのみを目的としており、商用目的ではありません。ご不明な点がございましたら、処理についてお問い合わせください。

次の記事は、Pythonクローラーとデータマイニング、著者のカップワイン氏からのものです。

I.はじめに

Webクローラー(WebスパイダーまたはWebロボットとも呼ばれます)は、特定のルールに従ってWorld WideWeb上の情報を自動的にクロールするプログラムまたはスクリプトです。他のあまり一般的に使用されない名前は、アリ、自動インデックス作成、シミュレーター、またはワームです。------百度百科事典

人間の言葉で言えば、クローラーは定期的に大量のデータを取得し、それらを処理して使用するために使用されます。これは、ビッグデータ、財務、機械学習などで必要なサポート条件の1つです。

現在、第1層の都市では、クローラーの給与と扱いは比較的客観的です。その後、中堅および上級のクローラーエンジニア、データアナリスト、ビッグデータ開発のポジションへの昇進はすべて良い移行です。

Pythonクローラー、データ分析、Webサイト開発、その他のケースチュートリアルビデオはオンラインで無料で視聴できます

https://space.bilibili.com/523606542 

Python学習交換グループ:1039645993

2.プロジェクトの目標

今回紹介したプロジェクトは、それほど複雑である必要はありません。最終的な目標は、投稿の各コメントをデータベースにクロールし、データを更新して、クロールやクロール防止などの対策が繰り返されないようにすることです。

 

3.プロジェクトの準備

このパートでは、主に、この記事で使用されているツール、関連するライブラリ、Webページやその他の情報などを紹介します。

ソフトウェア:PyCharm

必要なライブラリ:Scrapy、selenium、pymongo、user_agent、datetime

ターゲットウェブサイト:

http://bbs.foodmate.net

プラグイン:chromedriver(バージョンが正しい必要があります)

 

4、プロジェクト分析

1.クロールWebサイトの構造を決定します

要するに:ウェブサイトの読み込み方法、レベルごとにデータレベルを取得するために投稿を正しく入力する方法、データを保存するために使用する形式などを決定します。

次に、Webサイトの階層構造、つまり、セクションに従って少しずつ投稿ページに入る方法を観察します。これは、このクローラータスクにとって非常に重要であり、コード作成の主要部分でもあります。

 

2.データをクロールする正しい方法を選択する方法は?

現在、私が知っているクローラーの方法は次のとおりです(不完全ですが、より一般的に使用されています)。

1)リクエストフレームワーク:このhttpライブラリは、必要なデータを柔軟かつシンプルにクロールするために使用できますが、プロセスは少し面倒であり、パケットキャプチャツールとともに使用してデータを取得できます。ただし、ヘッダーと対応するリクエストパラメータを決定する必要があります。そうしないと、データを取得できません。多くのアプリクロール、画像と動画のクロールと停止はいつでも可能で、比較的軽量で柔軟性があり、同時実行性と分散展開も非常に柔軟です。 、および機能はより良い実現である可能性があります。

2)Scrapyフレームワーク:Scrapyフレームワークは、クローラーにとって最も一般的に使用され、最高のクローラーフレームワークであると言えます。Scrapyは非同期であり、通常の代わりに読みやすいxpathを使用し、強力な統計とログシステムを使用します。同時に、異なるURLでのクロール、独立したデバッグに便利なシェルモードのサポート、統合フィルターの作成を容易にするミドルウェアの作成のサポート、パイプラインを介したデータベースへの保存などが可能です。これは、この記事で紹介するフレームワークでもあります(セレンライブラリと組み合わせて)。

 

5、プロジェクトの実現

1.最初のステップ:Webサイトのタイプを決定します

まず、それが何を意味し、どのWebサイトを見るかを説明します。まず、静的ロード、動的ロード(jsロード)、またはその他の方法であるかどうか、Webサイトのロード方法によって異なります。ロード方法が異なれば、必要な方法も異なります。それに対処する。次に、今日クロールされたWebサイトを観察したところ、これは時系列のフォーラムであることがわかりました。最初は、静的にロードされたWebサイトであると推測しました。次の図に示すように、プラグインを開いてjsのロードを整理しました。

 

更新した後、それは確かに静的なWebサイトであることがわかります(正常にロードできる場合は、基本的に静的にロードされます)。

 

2.ステップ2:階層を決定する

次に、今日クロールしたいWebサイトは、静的にロードされたWebサイトであるフードフォーラムWebサイトです。前回の分析ですでに理解しており、階層構造は次のとおりです。

 

おそらく上記のプロセスでは、下の図に示すように、合計3つのレベルのプログレッシブ訪問があり、その後、投稿ページに到達します。

 

コード表示の一部:

第1レベルのインターフェース:

def parse(self, response):
    self.logger.info("已进入网页!")
    self.logger.info("正在获取版块列表!")
    column_path_list = response.css('#ct > div.mn > div:nth-child(2) > div')[:-1]
    for column_path in column_path_list:
        col_paths = column_path.css('div > table > tbody > tr > td > div > a').xpath('@href').extract()
        for path in col_paths:
            block_url = response.urljoin(path)
            yield scrapy.Request(
                url=block_url,
                callback=self.get_next_path,
           

二次インターフェース:

def get_next_path(self, response):
    self.logger.info("已进入版块!")
    self.logger.info("正在获取文章列表!")
    if response.url == 'http://www.foodmate.net/know/':
        pass
    else:
        try:
            nums = response.css('#fd_page_bottom > div > label > span::text').extract_first().split(' ')[-2]
        except:
            nums = 1
        for num in range(1, int(nums) + 1):
            tbody_list = response.css('#threadlisttableid > tbody')
            for tbody in tbody_list:
                if 'normalthread' in str(tbody):
                    item = LunTanItem()
                    item['article_url'] = response.urljoin(
                        tbody.css('* > tr > th > a.s.xst').xpath('@href').extract_first())
                    item['type'] = response.css(
                        '#ct > div > div.bm.bml.pbn > div.bm_h.cl > h1 > a::text').extract_first()
                    item['title'] = tbody.css('* > tr > th > a.s.xst::text').extract_first()
                    item['spider_type'] = "论坛"
                    item['source'] = "食品论坛"
                    if item['article_url'] != 'http://bbs.foodmate.net/':
                        yield scrapy.Request(
                            url=item['article_url'],
                            callback=self.get_data,
                            meta={'item': item, 'content_info': []}
                        )
        try:
            callback_url = response.css('#fd_page_bottom > div > a.nxt').xpath('@href').extract_first()
            callback_url = response.urljoin(callback_url)
            yield scrapy.Request(
                url=callback_url,
                callback=self.get_next_path,
            )
        except IndexError:
            pass

3レベルのインターフェース:

def get_data(self, response):
    self.logger.info("正在爬取论坛数据!")
    item = response.meta['item']
    content_list = []
    divs = response.xpath('//*[@id="postlist"]/div')
    user_name = response.css('div > div.pi > div:nth-child(1) > a::text').extract()
    publish_time = response.css('div.authi > em::text').extract()
    floor = divs.css('* strong> a> em::text').extract()
    s_id = divs.xpath('@id').extract()
    for i in range(len(divs) - 1):
        content = ''
        try:


            strong = response.css('#postmessage_' + s_id[i].split('_')[-1] + '').xpath('string(.)').extract()
            for s in strong:
                content += s.split(';')[-1].lstrip('\r\n')
            datas = dict(content=content,  # 内容
                         reply_id=0,  # 回复的楼层,默认0
                         user_name=user_name[i],  # ⽤户名
                         publish_time=publish_time[i].split('于 ')[-1],  # %Y-%m-%d %H:%M:%S'
                         id='#' + floor[i],  # 楼层
                         )
            content_list.append(datas)
        except IndexError:
            pass
    item['content_info'] = response.meta['content_info']
    item['scrawl_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    item['content_info'] += content_list


    data_url = response.css('#ct > div.pgbtn > a').xpath('@href').extract_first()
    if data_url != None:
        data_url = response.urljoin(data_url)
        yield scrapy.Request(
            url=data_url,
            callback=self.get_data,
            meta={'item': item, 'content_info': item['content_info']}
        )
    else:
        item['scrawl_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        self.logger.info("正在存储!")
        print('储存成功')
        yield item

 

3.ステップ3:クロール方法を決定する

静的なWebページであるため、最初にスクレイプフレームワークを使用してデータを直接取得することにしましたが、予備テストの結果、この方法は実際に実行可能であることがわかりました。しかし、当時、私は若くて軽薄で過小評価されていました。ウェブサイトの保護対策。忍耐力が限られているため、クロールを制限するタイマーが追加されていません。速度が原因でウェブサイトによって制限され、ウェブサイトが静的に読み込まれるウェブページから動的に読み込まれるウェブページ検証アルゴリズムに変更されてから、ウェブページ、直接アクセスはバックグラウンドによって拒否されます。

しかし、この種の問題はどうして私の賢さになるのでしょうか?少し考えた後(1日)、スキームをスクレイプフレームワーク+セレンライブラリの方法に変更し、chromedriverを呼び出してWebサイトへのアクセスをシミュレートしました。ロードされている場合、クロールされません。それだけです。フォローアップにより、この方法が実際に実行可能で効率的であることが証明されます。

コードの実装部分は次のとおりです。

def process_request(self, request, spider):
    chrome_options = Options()
    chrome_options.add_argument('--headless')  # 使用无头谷歌浏览器模式
    chrome_options.add_argument('--disable-gpu')
    chrome_options.add_argument('--no-sandbox')
    # 指定谷歌浏览器路径
    self.driver = webdriver.Chrome(chrome_options=chrome_options,
                                   executable_path='E:/pycharm/workspace/爬虫/scrapy/chromedriver')
    if request.url != 'http://bbs.foodmate.net/':
        self.driver.get(request.url)
        html = self.driver.page_source
        time.sleep(1)
        self.driver.quit()
        return scrapy.http.HtmlResponse(url=request.url, body=html.encode('utf-8'), encoding='utf-8',
                                        request=request)

 

4.ステップ4:クロールされたデータのストレージ形式を決定します

言うまでもなく、この部分では、必要に応じて、items.pyでクロールする必要のあるデータ形式を設定します。この形式を使用してプロジェクトに保存するだけです。

class LunTanItem(scrapy.Item):
    """
        论坛字段
    """
    title = Field()  # str: 字符类型 | 论坛标题
    content_info = Field()  # str: list类型 | 类型list: [LunTanContentInfoItem1, LunTanContentInfoItem2]
    article_url = Field()  # str: url | 文章链接
    scrawl_time = Field()  # str: 时间格式 参照如下格式 2019-08-01 10:20:00 | 数据爬取时间
    source = Field()  # str: 字符类型 | 论坛名称 eg: 未名BBS, 水木社区, 天涯论坛
    type = Field()  # str: 字符类型 | 板块类型 eg: '财经', '体育', '社会'
    spider_type = Field()  # str: forum | 只能写 'forum'

 

5.ステップ5:データベースの保存を確認します

このプロジェクト用に選択されたデータベースはmongodbです。これは非リレーショナルデータベースであるため、利点は明らかであり、フォーマット要件はそれほど高くなく、多次元データは柔軟に保存できます。これは一般的にクローラーに推奨されるデータベースです。 (私にredisを言わないでください、私がそれを知っているなら私はそれを使います、主にそうではありません)

コード:

import pymongo


class FMPipeline():
    def __init__(self):
        super(FMPipeline, self).__init__()
        # client = pymongo.MongoClient('139.217.92.75')
        client = pymongo.MongoClient('localhost')
        db = client.scrapy_FM
        self.collection = db.FM


    def process_item(self, item, spider):
        query = {
            'article_url': item['article_url']
        }
        self.collection.update_one(query, {"$set": dict(item)}, upsert=True)
m

このとき、賢い友人の中には、同じデータが2回クロールされた場合はどうなるかという質問があります。(つまり、重複チェック機能です)

以前はこの質問について考えていませんでした。後で、大物に聞いたときにわかりました。これは、データを保存したときに行われました。次の文だけです。

query = {
    'article_url': item['article_url']
}
self.collection.update_one(query, {"$set": dict(item)}, upsert=True)

投稿のリンクを介してクロールする重複データがあるかどうかを判断します。重複データが上書きされていると理解できる場合は、データを更新することもできます。

 

6.その他の設定

マルチスレッド、ヘッダー、パイプライン送信シーケンスなどの問題はすべて、settings.pyファイルに設定されています。詳細については、エディターのプロジェクトを参照してください。ここでは詳しく説明しません。

 

セブン、エフェクトディスプレイ

1. [実行]をクリックすると、次の図に示すように、結果がコンソールに表示されます。

 

 

2.途中で、キューに多くの投稿を積み上げるクロールタスクがあり、マルチスレッド処理が行われます。16スレッドを設定しましたが、速度は依然として非常に印象的です。

 

3.データベースデータの表示:

 

content_infoには、各投稿のすべてのコメントと関連ユーザーの公開情報が格納されます。

おすすめ

転載: blog.csdn.net/m0_48405781/article/details/114693866