Scrapyクローラーフレームワークを使用してフードフォーラムデータをクロールし、データベースに保存する方法を説明します

    みなさん、こんにちは。このような共有プロジェクトについての記事を書いたのはこれが初めてです。非常に水っぽくて不完全かもしれませんし、間違いがあるかもしれません。コメントにポインタを入れていただければ幸いです。ありがとうございます。

I.はじめに

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

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

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

 

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サイトのタイプを決定します

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

画像

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

 

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

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

 

画像

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

画像

多くの人がPythonを学び、どこから始めればよいのかわかりません。
多くの人がPythonを学び、基本的な文法を習得した後、どこから始めればよいかわかりません。
事例研究を行った多くの人々は、より高度な知識を学ぶ方法を知りません。
したがって、これら3つのタイプの人々のために、ビデオチュートリアル、電子書籍、およびコースのソースコードを無料で受け取ることができる優れた学習プラットフォームを提供します。
QQグループ:721195303

コード表示の一部:

    第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)
        return item

    このとき、賢い友人の中には、同じデータが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には、各投稿のすべてのコメントと関連ユーザーの公開情報が格納されます。

 

8.まとめ

    1.この記事では、主に食品Webサイトのデータ収集と保存プロセスを紹介し、Webページの構造、クロール戦略、Webサイトの種類、階層関係、クロール方法、データ保存プロセスを分析し、最終的に各コメントを実現する方法について詳しく説明します。クロールをデータベースに投稿し、データを更新してクロールの繰り返しやクロール防止などを防止できるようにするために、乾物がいっぱいになっています。

    2.一般的に言って、このプロジェクトは特に難しいことではありません。アイデアが正しく、データルールが見つかっている限り、簡単に立ち上がることができます。難しいのは、プロセスが以前に完了していないことです。この比較的水の紹介で、私はあなたを助けることができることを願っています、それは私の最大の名誉です。

    3.問題が発生した場合、最初に考えることは、同僚、友人、教師に尋ねるのではなく、GoogleとBaiduにアクセスして、同様の状況があるかどうかを確認し、他の人の経験を確認することです。自分で考えて問題を解決した後は、仕事がとても助かります(以前、学生時代を離れていないと言われていました。つまり、同僚に聞いてみたいです)。オンラインで特定の情報を確認した後も、手がかりがないので、他の人に聞いてください。他の人はあなたをもっと喜んで助けてくれるでしょう〜

 

自分で作成したPython学習グループ:721195303を引き続きお勧めします。全員がPythonを学習しています。Pythonを学習したい、または学習している場合は、ぜひ参加してください。誰もがソフトウェア開発パーティーであり、最新のPythonの高度な資料のコピーや、2021年に私が編集したゼロベースの教育など、随時(Pythonソフトウェア開発に関連するもののみ)。高度でPythonに興味のある友人を歓迎します。

おすすめ

転載: blog.csdn.net/pyjishu/article/details/114652698