12

私はscrapyを使用してサイトをクロールしています。このサイトには、ページごとに15のリストがあり、[次へ]ボタンがあります。パイプライン内のすべてのリストの解析が完了する前に、次のリンクのリクエストが呼び出されるという問題が発生しています。これが私のスパイダーのコードです:

class MySpider(CrawlSpider):
    name = 'mysite.com'
    allowed_domains = ['mysite.com']
    start_url = 'http://www.mysite.com/'

    def start_requests(self):
        return [Request(self.start_url, callback=self.parse_listings)]

    def parse_listings(self, response):
        hxs = HtmlXPathSelector(response)
        listings = hxs.select('...')

        for listing in listings:
            il = MySiteLoader(selector=listing)
            il.add_xpath('Title', '...')
            il.add_xpath('Link', '...')

            item = il.load_item()
            listing_url = listing.select('...').extract()

            if listing_url:
                yield Request(urlparse.urljoin(response.url, listing_url[0]),
                              meta={'item': item},
                              callback=self.parse_listing_details)

        next_page_url = hxs.select('descendant::div[@id="pagination"]/'
                                   'div[@class="next-link"]/a/@href').extract()
        if next_page_url:
            yield Request(urlparse.urljoin(response.url, next_page_url[0]),
                          callback=self.parse_listings)


    def parse_listing_details(self, response):
        hxs = HtmlXPathSelector(response)
        item = response.request.meta['item']
        details = hxs.select('...')
        il = MySiteLoader(selector=details, item=item)

        il.add_xpath('Posted_on_Date', '...')
        il.add_xpath('Description', '...')
        return il.load_item()

これらの線が問題です。前に言ったように、スパイダーが現在のページをクロールし終える前に実行されています。サイトのすべてのページで、これにより、私のリストの15のうち3つだけがパイプラインに送信されます。

     if next_page_url:
            yield Request(urlparse.urljoin(response.url, next_page_url[0]),
                          callback=self.parse_listings)

これは私の最初のスパイダーであり、私の側の設計上の欠陥である可能性がありますが、これを行うためのより良い方法はありますか?

4

7 に答える 7

4

クモの代わりにこする?

元の問題では、サイズが不明なコンテンツのツリーではなく、コンテンツの連続した繰り返しセットを繰り返しナビゲーションする必要があるため、mechanize (http://wwwsearch.sourceforge.net/mechanize/) と beautifulsoup (http://www) を使用します。 .crummy.com/software/BeautifulSoup/)。

これは、mechanize を使用してブラウザをインスタンス化する例です。また、 br.follow_link(text="foo") を使用すると、例の xpath とは異なり、先祖パスの要素の構造に関係なく、リンクがたどられることを意味します。つまり、HTML を更新すると、スクリプトが壊れます。カップリングを緩めると、メンテナンスを節約できます。次に例を示します。

br = mechanize.Browser()
br.set_handle_equiv(True)
br.set_handle_redirect(True)
br.set_handle_referer(True)
br.set_handle_robots(False)
br.addheaders = [('User-agent', 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:9.0.1)Gecko/20100101 Firefox/9.0.1')]
br.addheaders = [('Accept-Language','en-US')]
br.addheaders = [('Accept-Encoding','gzip, deflate')]
cj = cookielib.LWPCookieJar()
br.set_cookiejar(cj)
br.open("http://amazon.com")
br.follow_link(text="Today's Deals")
print br.response().read()

また、「次の 15」href には、おそらく &index=15 などのページネーションを示すものがあります。すべてのページの項目の合計数が最初のページで利用できる場合、次のようになります。

soup = BeautifulSoup(br.response().read())
totalItems = soup.findAll(id="results-count-total")[0].text
startVar =  [x for x in range(int(totalItems)) if x % 15 == 0]

次に、startVar を繰り返し処理して URL を作成し、startVar の値を URL に追加し、br.open() してデータをスクレイピングします。そうすれば、ページ上の「次の」リンクをプログラムで「見つけ」、それをクリックして次のページに進む必要がなくなります。すべての有効な URL は既にわかっています。コードによるページの操作を必要なデータのみに最小限に抑えることで、抽出が高速化されます。

于 2012-02-28T16:48:50.810 に答える
3

これを順番に行うには、次の2つの方法があります。

  1. listing_urlクラスの下にリストを定義することによって。
  2. listing_urlの内側を定義することによってparse_listings()

唯一の違いは言葉遣いです。また、取得するページが5つあるとしますlisting_urls。ですからpage=1、クラスにも入れてください。

このparse_listingsメソッドでは、リクエストを1回だけ行います。meta追跡する必要のあるすべてのデータをに入れます。そうは言ってもparse_listings、「フロントページ」を解析するためにのみ使用してください。

行の終わりに達したら、アイテムを返します。このプロセスは順次です。

class MySpider(CrawlSpider):
    name = 'mysite.com'
    allowed_domains = ['mysite.com']
    start_url = 'http://www.mysite.com/'

    listing_url = []
    page = 1

    def start_requests(self):
        return [Request(self.start_url, meta={'page': page}, callback=self.parse_listings)]

    def parse_listings(self, response):
        hxs = HtmlXPathSelector(response)
        listings = hxs.select('...')

        for listing in listings:
            il = MySiteLoader(selector=listing)
            il.add_xpath('Title', '...')
            il.add_xpath('Link', '...')

        items = il.load_item()

        # populate the listing_url with the scraped URLs
        self.listing_url.extend(listing.select('...').extract())

        next_page_url = hxs.select('descendant::div[@id="pagination"]/'
                                   'div[@class="next-link"]/a/@href').extract()

        # now that the front page is done, move on to the next listing_url.pop(0)
        # add the next_page_url to the meta data
        return Request(urlparse.urljoin(response.url, self.listing_url.pop(0)),
                            meta={'page': self.page, 'items': items, 'next_page_url': next_page_url},
                            callback=self.parse_listing_details)

    def parse_listing_details(self, response):
        hxs = HtmlXPathSelector(response)
        item = response.request.meta['item']
        details = hxs.select('...')
        il = MySiteLoader(selector=details, item=item)

        il.add_xpath('Posted_on_Date', '...')
        il.add_xpath('Description', '...')
        items = il.load_item()

        # check to see if you have any more listing_urls to parse and last page
        if self.listing_urls:
            return Request(urlparse.urljoin(response.url, self.listing_urls.pop(0)),
                            meta={'page': self.page, 'items': items, 'next_page_url': response.meta['next_page_url']},
                            callback=self.parse_listings_details)
        elif not self.listing_urls and response.meta['page'] != 5:
            # loop back for more URLs to crawl
            return Request(urlparse.urljoin(response.url, response.meta['next_page_url']),
                            meta={'page': self.page + 1, 'items': items},
                            callback=self.parse_listings)
        else:
            # reached the end of the pages to crawl, return data
            return il.load_item()
于 2012-06-29T00:30:23.490 に答える
1

EDIT 2セクションの下の更新された回答については、以下を参照してください(2017年10月6日更新)

yield を使用している特定の理由はありますか? Yield はジェネレーターを返します。ジェネレーターは、.next()呼び出されたときに Request オブジェクトを返します。

yieldステートメントをステートメントに変更するreturnと、期待どおりに動作するはずです。

ジェネレーターの例を次に示します。

In [1]: def foo(request):
   ...:     yield 1
   ...:     
   ...:     

In [2]: print foo(None)
<generator object foo at 0x10151c960>

In [3]: foo(None).next()
Out[3]: 1

編集:

def start_requests(self)パラメーターを使用するように関数を変更してfollowください。

return [Request(self.start_url, callback=self.parse_listings, follow=True)]

編集2:

2017 年 5 月 18 日にリリースされた Scrapy v1.4.0 の時点で、オブジェクトを直接response.follow作成する代わりに使用することが推奨されるようになりました。scrapy.Request

リリースノートから:

リクエストを作成するための新しい response.follow メソッドがあります。Scrapy スパイダーでリクエストを作成するための推奨される方法になりました。この方法により、正しいスパイダーを簡単に作成できます。response.follow には、直接 Scrapy.Request オブジェクトを作成するよりもいくつかの利点があります。

  • 相対 URL を処理します。
  • 非 UTF8 ページの非 ASCII URL で適切に動作します。
  • 絶対および相対 URL に加えて、セレクターをサポートします。要素の場合、href 値を抽出することもできます。

したがって、上記の OP では、コードを次のように変更します。

    next_page_url = hxs.select('descendant::div[@id="pagination"]/'
                               'div[@class="next-link"]/a/@href').extract()
    if next_page_url:
        yield Request(urlparse.urljoin(response.url, next_page_url[0]),
                      callback=self.parse_listings)

に:

    next_page_url = hxs.select('descendant::div[@id="pagination"]/'
                               'div[@class="next-link"]/a/@href')
    if next_page_url is not None:
        yield response.follow(next_page_url, self.parse_listings)
于 2011-03-08T03:43:12.433 に答える
1

依頼やアイテムは何度でも譲れます。

def parse_category(self, response):
    # Get links to other categories
    categories = hxs.select('.../@href').extract()

    # First, return CategoryItem
    yield l.load_item()

    for url in categories:
        # Than return request for parse category
        yield Request(url, self.parse_category)

ここで見つけました — https://groups.google.com/d/msg/scrapy-users/tHAAgnuIPR4/0ImtdyIoZKYJ

于 2012-08-03T10:22:20.060 に答える
0

http://autopython.blogspot.com/2014/04/recursive-scraping-using-different.html

この例は、さまざまな手法を使用して Web サイトから複数の次のページをスクラップする方法を示しています

于 2014-04-10T14:53:23.953 に答える
0

コードでこの同じ問題を修正しました。私はそれを修正するために Python 2.7 の一部として提供される SQLite3 データベースを使用しました: 情報を収集している各アイテムは、解析関数の最初のパスでデータベース テーブルに配置された一意の行を取得し、解析コールバックの各インスタンスはそれぞれを追加しますそのアイテムのテーブルと行へのアイテムのデータ。最後のコールバック解析ルーチンがそれが最後のものであることを認識し、データベースなどから CSV ファイルを書き込むように、インスタンス カウンターを保持します。コールバックは再帰的に行うことができ、どの解析スキーマ (そしてもちろんどの項目) を処理するためにディスパッチされたかをメタで通知されます。魅力のように私のために働きます。Python を使用している場合は、SQLite3 を使用しています。この点でスクレイピーの制限を最初に発見したときの私の投稿は次のとおりです。 Scrapy の非同期性は、私の CSV 結果ファイルが直接作成されるのを妨げているのですか?

于 2014-01-30T08:12:49.047 に答える
0

あなたは2つのことを調べたいと思うかもしれません。

  1. クロールしている Web サイトが、定義したユーザー エージェントをブロックしている可能性があります。
  2. スパイダーに DOWNLOAD_DELAY を追加してみてください。
于 2011-12-05T05:07:59.427 に答える