10

私はScrapyを使用して非常に単純なクローラーをコーディングすることができましたが、次のような制約があります。

  • すべてのリンク情報(例:アンカーテキスト、ページタイトル)を保存するため、2つのコールバック
  • CrawlSpiderを使用してルールを利用するため、BaseSpiderは使用できません

最初のリクエストにコールバックを追加した場合にルールが実装されないことを除いて、正常に動作します。

これが私のコードです:(実際の例では、正しくは機能しませんが)

from scrapy.contrib.spiders import CrawlSpider,Rule
from scrapy.selector import HtmlXPathSelector
from scrapy.http import Request
from scrapySpider.items import SPage
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor

class TestSpider4(CrawlSpider):
    name = "spiderSO"
    allowed_domains = ["cumulodata.com"]
    start_urls = ["http://www.cumulodata.com"]
    extractor = SgmlLinkExtractor()

    def parse_start_url(self, response):
        #3
        print('----------manual call of',response)
        self.parse_links(response)
        print('----------manual call done')
        # 1 return Request(self.start_urls[0]) # does not call parse_links(example.com)
        # 2 return Request(self.start_urls[0],callback = self.parse_links) # does not call parse_links(example.com)

    rules = (
        Rule(extractor,callback='parse_links',follow=True),
        )

    def parse_links(self, response):
        hxs = HtmlXPathSelector(response)
        print('----------- manual parsing links of',response.url)
        links = hxs.select('//a')
        for link in links:
                title = link.select('@title')
                url = link.select('@href').extract()[0]
                meta={'title':title,}
                yield Request(url, callback = self.parse_page,meta=meta)

    def parse_page(self, response):
        print('----------- parsing page: ',response.url)
        hxs = HtmlXPathSelector(response)
        item=SPage()
        item['url'] = str(response.request.url)
        item['title']=response.meta['title']
        item['h1']=hxs.select('//h1/text()').extract()
        yield item

私はこの問題を3つの方法で解決しようとしました:

  • 1:開始URLを含むリクエストを返すには-ルールは実行されません
  • 2:上記と同じですが、コールバックがありますparse_links-同じ問題
  • 3:開始URLをスクレイピングしたparse_links 後にparse_start_url呼び出す、実装することにより、関数は呼び出されません

ログは次のとおりです。

----------manual call of <200 http://www.cumulodata.com>)

----------manual call done

#No '----------- manual parsing links', so `parse_links` is never called!

バージョン

  • Python 2.7.2
  • Scrapy 0.14.4
4

1 に答える 1

19

完璧に機能するスクレーパーは次のとおりです。

from scrapy.contrib.spiders import CrawlSpider,Rule
from scrapy.selector import HtmlXPathSelector
from scrapy.http import Request
from scrapySpider.items import SPage
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor

class TestSpider4(CrawlSpider):
    name = "spiderSO"
    allowed_domains = ["cumulodata.com"]
    start_urls = ["http://www.cumulodata.com/"]

    extractor = SgmlLinkExtractor()

    rules = (
        Rule(extractor,callback='parse_links',follow=True),
        )

    def parse_start_url(self, response):
        list(self.parse_links(response))

    def parse_links(self, response):
        hxs = HtmlXPathSelector(response)
        links = hxs.select('//a')
        for link in links:
            title = ''.join(link.select('./@title').extract())
            url = ''.join(link.select('./@href').extract())
            meta={'title':title,}
            cleaned_url = "%s/?1" % url if not '/' in url.partition('//')[2] else "%s?1" % url
            yield Request(cleaned_url, callback = self.parse_page, meta=meta,)

    def parse_page(self, response):
        hxs = HtmlXPathSelector(response)
        item=SPage()
        item['url'] = response.url
        item['title']=response.meta['title']
        item['h1']=hxs.select('//h1/text()').extract()
        return item

変更点

  1. 実装済みparse_start_url-残念ながら、最初のリクエストにコールバックを指定すると、ルールは実行されません。これはScrapyに組み込まれており、回避策を使用してのみ管理できます。したがって、 list(self.parse_links(response))この関数の内部で実行します。なぜlist()?はジェネレーターであり、parse_linksジェネレーターは怠惰です。したがって、明示的に完全に呼び出す必要があります。

  2. cleaned_url = "%s/?1" % url if not '/' in url.partition('//')[2] else "%s?1" % url-ここで起こっていることがいくつかあります:

    a。URLの末尾に「/?1」を追加しています-parse_links重複するURLを返すため、Scrapyはそれらを除外します。これを回避する簡単な方法はdont_filter=True、Request()に渡すことです。ただし、すべてのページが相互にリンクされており(pageAAからインデックスに戻るなど)、dont_filterここでは重複するリクエストとアイテムが多すぎます。

    b。if not '/' in url.partition('//')[2]-繰り返しますが、これはあなたのウェブサイトのリンクのためです。内部リンクの1つは「www.cumulodata.com」へのリンクであり、もう1つは「www.cumulodata.com/」へのリンクです。重複を許可するメカニズムを明示的に追加しているため、これにより1つの余分なアイテムが発生していました。完璧が必要だったので、このハックを実装しました。

  3. title = ''.join(link.select('./@title').extract())-ノードではなくデータを返します。また、空のリストの場合、'' .join(list)はlist[0]よりも優れています。

奇妙な問題を引き起こしたテストWebサイトを作成しておめでとうございます-複製は必要であると同時に不要です!

于 2012-10-04T21:35:03.610 に答える