2016-09-10 7 views
1

この質問は、Force my scrapy spider to stop crawlingと非常によく似ています。しかし、そこに示唆されている解決策はScrapy 1.1.1の日付か、正確には関連していない。 スパイダーが特定のURLに達すると、スパイダーを閉じます。メディアプロジェクトなどのニュースサイトをクロールするときは、これが絶対必要です。指定されたURLを満たしたときにスパイダーが止まる

CLOSESPIDER_TIMEOUTCLOSESPIDER_ITEMCOUNTCLOSESPIDER_PAGECOUNTCLOSESPIDER_ERRORCOUNTのうち、アイテム数とページ数のオプションは近いですが、ページ数やアイテム数がわからないため、十分ではありません。

例外は仕事をしているようですが、今のところそれはちょっと奇妙な方法で行います。私は“Learning Scrapy”の教科書に従い、私のコードの構造は本の中のもののように見えます。 items.py

私が項目のリストを作る:私はクモがdef parse()内の各インデックスページを解析し、処理するためのページを取るdef start_requests()メソッドを使用myspider.py

class MyProjectItem(scrapy.Item): 

    Headline = scrapy.Field() 
    URL = scrapy.Field() 
    PublishDate = scrapy.Field() 
    Author = scrapy.Field() 

    pass 

、および各項目のXPathを指定しますdef parse_item()中:

class MyProjectSpider(scrapy.Spider): 
    name = 'spidername' 
    allowed_domains = ['domain.name.com'] 


    def start_requests(self): 

     for i in range(1,3000): 
      yield scrapy.Request('http://domain.name.com/news/index.page'+str(i)+'.html', self.parse) 


    def parse(self, response): 

     urls = response.xpath('XPath for the URLs on index page').extract()   
     for url in urls: 
      # The urls are absolute in this case. There’s no need to use urllib.parse.urljoin() 
      yield scrapy.Request(url, callback=self.parse_item) 


    def parse_item(self, response): 

     l = ItemLoader(item=MyProjectItem(), response=response) 

     l.add_xpath('Headline', 'XPath for Headline') 
     l.add_value('URL', response.url) 
     l.add_xpath ('PublishDate', 'XPath for PublishDate') 
     l.add_xpath('Author', 'XPath for Author') 

     return l.load_item() 

raise CloseSpider(reason='some reason')例外がdef parse_item()に配置されている場合、それはまだfinallその前にアイテムの数を削りますyが停止します。

if l.get_output_value('URL') == 'http://domain.name.com/news/1234567.html': 
    raise CloseSpider('No more news items.') 

それは、特定のURLに達したときに停止するdef parse()方法に置かれています場合、それはその特定のURLが含まれているインデックスページからのみ最初の項目グラブの後に停止します。

def parse(self, response):  

    most_recent_url_in_db = 'http://domain.name.com/news/1234567.html ' 
    urls = response.xpath('XPath for the URLs on index page').extract() 

    if most_recent_url_in_db not in urls: 
     for url in urls: 
      yield scrapy.Request(url, callback=self.parse_item) 
    else: 
     for url in urls[:urls.index(most_recent_url_in_db)]: 
      yield scrapy.Request(url, callback=self.parse_item) 
     raise CloseSpider('No more news items.') 

をたとえば、5つのインデックスページ(それぞれ25アイテムのURLを持っています)とmost_recent_url_in_dbが4ページにある場合は、1〜3ページのすべてのアイテムと4ページの最初のアイテムだけを持つことになります。スパイダーは止まる。 most_recent_url_in_dbがリストの番号10の場合、インデックスページ4の項目2-9はデータベースに表示されません。

crawler.engine.close_spider()で示唆されている「ハッキー」のトリックや、多くの場合にはHow do I stop all spiders and the engine immediately after a condition in a pipeline is met?で共有されているものが動作していないようです。

このタスクを正しく完了するにはどのような方法が必要ですか?

答えて

1

あなたのアプローチを変更することをお勧めします。 Scrapyは多くのリクエストを線形順序なしで同時にクロールします。そのため、探しているものが見つかったらスパイダーを閉じるのは、それ以降のリクエストが既に処理されているためです。

この問題に対処するには、Scrapyのクロールを順番に行うことができます。つまり、一度にリクエストを一定の順序で繰り返すことができます。これはさまざまな方法で実現できますが、ここで私はそれについてどうやっていくのかの例です。

まず、一度に1ページずつクロールする必要があります。これは次のようにすることができます:

class MyProjectSpider(scrapy.Spider): 

    pagination_url = 'http://domain.name.com/news/index.page{}.html' 

    def start_requests(self): 
     yield scrapy.Request(
      self.pagination_url.format(1), 
      meta={'page_number': 1}, 
     ) 

    def parse(self, response): 
     # code handling item links 
     ... 

     page_number = response.meta['page_number'] 
     next_page_number = page_number + 1 

     if next_page_number <= 3000: 
      yield scrapy.Request(
       self.pagination_url.format(next_page_number), 
       meta={'page_number': next_page_number}, 
      ) 

これが実装されたら、各ページのリンクと同様のことができます。あなたはどのように上のアイデアを提供します

class MyProjectSpider(scrapy.Spider): 
    name = 'spidername' 
    allowed_domains = ['domain.name.com'] 

    pagination_url = 'http://domain.name.com/news/index.page{}.html' 
    most_recent_url_in_db = 'http://domain.name.com/news/1234567.html ' 

    def start_requests(self): 
     yield scrapy.Request(
      self.pagination_url.format(1), 
      meta={'page_number': 1} 
     ) 

    def parse(self, response): 
     url_found = False 

     urls = response.xpath('XPath for the URLs on index page').extract() 
     for url in urls: 

      if url == self.most_recent_url_in_db: 
       url_found = True 
       break 

      yield scrapy.Request(url, callback=self.parse_item) 

     page_number = response.meta['page_number'] 
     next_page_number = page_number + 1 

     if next_page_number <= 3000 and not url_found: 
      yield scrapy.Request(
       self.pagination_url.format(next_page_number), 
       meta={'page_number': next_page_number}, 
      ) 

    def parse_item(self, response): 

     l = ItemLoader(item=MyProjectItem(), response=response) 

     l.add_xpath('Headline', 'XPath for Headline') 
     l.add_value('URL', response.url) 
     l.add_xpath ('PublishDate', 'XPath for PublishDate') 
     l.add_xpath('Author', 'XPath for Author') 

     return l.load_item() 

希望:あなたが持っているだろうすべて一緒に置く

class MyProjectSpider(scrapy.Spider): 

    most_recent_url_in_db = 'http://domain.name.com/news/1234567.html ' 

    def parse(self, response): 
     url_found = False 

     urls = response.xpath('XPath for the URLs on index page').extract() 
     for url in urls: 

      if url == self.most_recent_url_in_db: 
       url_found = True 
       break 

      yield scrapy.Request(url, callback=self.parse_item) 

     page_number = response.meta['page_number'] 
     next_page_number = page_number + 1 

     if not url_found: 
      yield scrapy.Request(
       self.pagination_url.format(next_page_number), 
       meta={'page_number': next_page_number}, 
      ) 

:あなたはそれらのコンテンツをダウンロードすることなく、それらをフィルタリングすることができますので、あなたはこのような何かを行うことができますあなたが探しているもの、幸運を達成する!

+0

ジュリア、このソリューションは素晴らしいです!それは非常にエレガントで、特に初心者にとっては理解しやすいです。私はそれが多くの人々を助けると確信しています。さらに、あなたはすべてのコードを書いています。私はそれに何かを加える必要さえない。これは素晴らしいことではありませんか?)本当にありがとうございました! –

0

あなたがclose_spider() exceptionを上げるときは、理想的な仮定はscrapyは、他のすべてのアクティビティ(将来のページ要求、パイプライン..etcのいずれかの処理)

を放棄、すぐに停止すべきであるということですが、あなたが上げたとき、これは、そうではありませんclose_spider() exception、scrapyは、現在の要求を停止しますことを意味し、それは現在の操作の優雅だ閉じようとしますが、それは、キューのいずれかに保留中の他の要求を待ちます(複数のキューがあります!)

(すなわちデフォルト設定を上書きし、16の以上の開始URLを持っていない場合は、あなたがclose_spider() exceptionを上げるとすぐにクモを停止したい場合は、次の3つのキュー

をクリアしたいと思うでしょう、一度に16のリクエスト)今

を作るscrapy

- クモミドルウェアレベルで---

  • spider.crawler.engine.slot.scheduler.mqs - >メモリー・キュー・将来の要求
  • spider.crawler.engine.slot.inprogress - >任意で-progress Request

- ダウンロードミドルウェアレベル---

  • spider.requests_queue - >いずれかを訪問からscrapyを防ぐために、適切なミドルウェアをオーバーライドすることにより、要求キューで保留中の要求

フラッシュこのすべてのキューさらなるページ

+0

ありがとうございました!私はあなたの説明の背後にある論理を理解していると思いますが、現在、私はどのように正確にデフォルト設定を変更すべきか分かりません。私はそれを理解する時間が必要です。 –

+0

あなたがする必要があるものへのリンクhttp://doc.scrapy.org/en/latest/topics/downloader-middleware.html#downloader-middlewareおよびhttp://doc.scrapy.org/ja/latest/topics/ spider-middleware.html#spider-middleware – MrPandav

関連する問題