1

私の問題は次のとおりです。たとえば、www.example.com などのドメインから貴重なテキストをすべて抽出したいのです。だから私はこのウェブサイトに行き、最大深さ2のすべてのリンクにアクセスし、それをcsvファイルに書きます。

1 つのプロセスを使用して複数のクローラーを生成するこの問題を解決するモジュールを Scrapy で作成しましたが、非効率的です。1 時間あたり最大 1,000 個のドメイン/最大 5,000 個の Web サイトをクロールでき、私のボトルネックが CPU であることがわかる限り (なぜならGILの?)。しばらく PC を離れた後、ネットワーク接続が切断されていることに気付きました。

複数のプロセスを使用したいとき、ツイストからエラーが発生しました:マルチプロセッシング オブ スクレイピー スパイダーの並列プロセス

だから私は何をすべきかいくつかのアイデアを持っています

  • 反撃してねじれを学び、マルチプロセッシングを実装し、Redis で分散キューを実装しようとしますが、scrapy がこの種の仕事に適したツールであるとは思いません。
  • pyspider を使用します - 必要なすべての機能を備えています (使用したことはありません)。
  • Nutch を使用します。これは非常に複雑です (私は使用したことがありません)。
  • 独自の分散クローラーを構築しようとしましたが、4 つの Web サイトをクロールした後、SSL、重複、タイムアウトの 4 つのエッジ ケースが見つかりました。ただし、次のような変更を加えるのは簡単です。

どのような解決策をお勧めしますか?

Edit1: コードの共有

class ESIndexingPipeline(object):
    def __init__(self):
        # self.text = set()
        self.extracted_type = []
        self.text = OrderedSet()
        import html2text
        self.h = html2text.HTML2Text()
        self.h.ignore_links = True
        self.h.images_to_alt = True

    def process_item(self, item, spider):
        body = item['body']
        body = self.h.handle(str(body, 'utf8')).split('\n')

        first_line = True
        for piece in body:
            piece = piece.strip(' \n\t\r')
            if len(piece) == 0:
                first_line = True
            else:
                e = ''
                if not self.text.empty() and not first_line and not regex.match(piece):
                    e = self.text.pop() + ' '
                e += piece
                self.text.add(e)
                first_line = False

        return item

    def open_spider(self, spider):
        self.target_id = spider.target_id
        self.queue = spider.queue

    def close_spider(self, spider):
        self.text = [e for e in self.text if comprehension_helper(langdetect.detect, e) == 'en']
        if spider.write_to_file:
            self._write_to_file(spider)

    def _write_to_file(self, spider):
        concat = "\n".join(self.text)
        self.queue.put([self.target_id, concat])

そして呼び出し:

def execute_crawler_process(targets, write_to_file=True, settings=None, parallel=800, queue=None):
    if settings is None:
        settings = DEFAULT_SPIDER_SETTINGS

    # causes that runners work sequentially
    @defer.inlineCallbacks
    def crawl(runner):
        n_crawlers_batch = 0
        done = 0
        n = float(len(targets))
        for url in targets:
            #print("target: ", url)
            n_crawlers_batch += 1
            r = runner.crawl(
                TextExtractionSpider,
                url=url,
                target_id=url,
                write_to_file=write_to_file,
                queue=queue)
            if n_crawlers_batch == parallel:
                print('joining')
                n_crawlers_batch = 0
                d = runner.join()
                # todo: print before yield
                done += n_crawlers_batch
                yield d  # download rest of data
        if n_crawlers_batch < parallel:
            d = runner.join()
            done += n_crawlers_batch
            yield d

        reactor.stop()

    def f():
        runner = CrawlerProcess(settings)
        crawl(runner)
        reactor.run()

    p = Process(target=f)
    p.start()

クモは特に興味深いものではありません。

4

1 に答える 1

6

Scrapy-Redisを使用できます。基本的には、Redis のキューからクロールする URL をフェッチする Scrapy スパイダーです。利点は、多数のスパイダーを同時に開始できるため、クロールを高速化できることです。スパイダーのすべてのインスタンスは、キューから URL をプルし、クロールする URL がなくなるとアイドル状態で待機します。Scrapy-Redis のリポジトリには、これを実装するためのサンプル プロジェクトが付属しています。

Scrapy-Redis を使用してクローラーの 64 インスタンスを起動し、約 1 時間で 100 万の URL をスクレイピングします。

于 2016-12-21T14:41:47.543 に答える