2017-08-20 2 views
0

私はPythonとSpark GraphXスキルを磨くために、ピンボードユーザーとブックマークのグラフを作成しようとしています。また、保存したすべてのユーザーを見つけ、ユーザーとのScrapeピンボードをScrapyで再帰的にスクラップ - 「スパイダーはリクエストを返す必要があります」エラー

  1. スタートし、すべてのブックマークurl_slugによって識別された各ブックマークの
  2. を、こすり:そうするためには、私は次の方法で再帰的にピンボードのブックマークをこすり同じブックマーク。ステップ2からユーザごとに
  3. は、プロセスを繰り返し、

私はこのロジックを実装しよう(ルールの使用を含む)ここでは、複数のスレッドから、試してみました提案を持つにもかかわらず(、... 1に進みます)私は強く私のコードでyield/returnのこのミックスに関係している疑いがある

ERROR: Spider must return Request, BaseItem, dict or None, got 'generator'

:私は次のエラーを取得します。

ここ

私のコードの簡単な説明:

私の主な解析方法は、(また、同じユーザのブックマークと以前のページ次)1人のユーザーのすべてのブックマークのアイテムを見つけては、これらのブックマークをこすりするparse_bookmark方法を生み出します。

class PinSpider(scrapy.Spider): 
    name = 'pinboard' 

    # Before = datetime after 1970-01-01 in seconds, used to separate the bookmark pages of a user 
    def __init__(self, user='notiv', before='3000000000', *args, **kwargs): 
     super(PinSpider, self).__init__(*args, **kwargs) 
     self.start_urls = ['https://pinboard.in/u:%s/before:%s' % (user, before)] 
     self.before = before 

    def parse(self, response): 
     # fetches json representation of bookmarks instead of using css or xpath 
     bookmarks = re.findall('bmarks\[\d+\] = (\{.*?\});', response.body.decode('utf-8'), re.DOTALL | re.MULTILINE) 

     for b in bookmarks: 
      bookmark = json.loads(b) 
      yield self.parse_bookmark(bookmark) 

     # Get bookmarks in previous pages 
     previous_page = response.css('a#top_earlier::attr(href)').extract_first() 
     if previous_page: 
      previous_page = response.urljoin(previous_page) 
      yield scrapy.Request(previous_page, callback=self.parse) 

この方法は、対応するurl_slug含む、ブックマークの情報を掻き取るPinscrapyItemに格納し、その後url_slugを解析するscrapy.Requestをもたらす:

def parse_bookmark(self, bookmark): 
    pin = PinscrapyItem() 

    pin['url_slug'] = bookmark['url_slug'] 
    pin['title'] = bookmark['title'] 
    pin['author'] = bookmark['author'] 

    # IF I REMOVE THE FOLLOWING LINE THE PARSING OF ONE USER WORKS (STEP 1) BUT NO STEP 2 IS PERFORMED 
    yield scrapy.Request('https://pinboard.in/url:' + pin['url_slug'], callback=self.parse_url_slug) 

    return pin 

最後parse_url_slug方法は、他のユーザーを見つけこのブックマークを保存して再帰的にscrape.Requestを解析し、それぞれを解析します。

def parse_url_slug(self, response): 
    url_slug = UrlSlugItem() 

    if response.body: 
     soup = BeautifulSoup(response.body, 'html.parser') 

     users = soup.find_all("div", class_="bookmark") 
     user_list = [re.findall('/u:(.*)/t:', element.a['href'], re.DOTALL) for element in users] 
     user_list_flat = sum(user_list, []) # Change from list of lists to list 

     url_slug['user_list'] = user_list_flat 

     for user in user_list: 
      yield scrapy.Request('https://pinboard.in/u:%s/before:%s' % (user, self.before), callback=self.parse) 

    return url_slug 

(私は他の興味深いフィールドを格納または重複などをチェックする場合、より簡潔な方法でコードを提示するために、私は部品を除去)

すべてのヘルプははるかに高く評価されるだろう!

答えて

0

問題がでているので、コード

yield self.parse_bookmark(bookmark) 

のあなたの下のブロックであり、あなたのparse_bookmarkあなたはこの関数の戻り値は発電機であるyieldを持っているので、次の2つのライン

# IF I REMOVE THE FOLLOWING LINE THE PARSING OF ONE USER WORKS (STEP 1) BUT NO STEP 2 IS PERFORMED 
yield scrapy.Request('https://pinboard.in/url:' + pin['url_slug'], callback=self.parse_url_slug) 

return pin 

の下に持っています。そのジェネレータをScrapyに戻して、それをどうしたらいいのか分からない。

修正が簡単です。あなたのコードは、これは発電機の代わりに、発電機自体から一度に1つの値が得られます

yield from self.parse_bookmark(bookmark) 

の下に変更します。それとも、この

for ret in self.parse_bookmark(bookmark): 
    yield ret 

編集-1

項目最初

yield pin 
yield scrapy.Request('https://pinboard.in/url:' + pin['url_slug'], callback=self.parse_url_slug) 

そして、他のものを得るために、あなたの関数を変更し

すぎ

url_slug['user_list'] = user_list_flat 
    yield url_slug 
    for user in user_list: 
     yield scrapy.Request('https://pinboard.in/u:%s/before:%s' % (user, self.before), callback=self.parse) 

ようにそれをも行うことができます後で収穫すると、他の多くのリクエストが最初にスケジュールされますスクラップしたアイテムが見え始めると私は変更と上記のコードを実行し、それは私のためによくこすります

2017-08-20 14:02:38 [scrapy.core.scraper] DEBUG: Scraped from <200 https://pinboard.in/u:%5B'semanticdreamer'%5D/before:3000000000> 
{'url_slug': 'e1ff3a9fb18873e494ec47d806349d90fec33c66', 'title': 'Flair Conky Offers Dark & Light Version For All Linux Distributions - NoobsLab | Ubuntu/Linux News, Reviews, Tutorials, Apps', 'author': 'semanticdreamer'} 
2017-08-20 14:02:38 [scrapy.core.scraper] DEBUG: Scraped from <200 https://pinboard.in/url:d9c16292ec9019fdc8411e02fe4f3d6046185c58> 
{'user_list': ['ronert', 'notiv']} 
+0

コードが実行され、ありがとう!問題は、出力ファイルが空であることです。 [このSOの答え](https://stackoverflow.com/questions/17497640/python-scrapy-output-csv-file-empty)と[this one](https://stackoverflow.com/questions/31890731/scrapy-スクラップ - データ - 出力なし - ファイルへの出力)は、アイテムを返す必要があることを示しています。私は1つ、またはより正確には、2つのアイテム(PinscrapyItemとUrlSlugItem)を返すと考えました。何か不足していますか?私はパイプラインを使用していません。 – notiv

+0

私は掻き削り自体が動作していることを忘れていました( 'ダウンローダ/レスポンスカウント:41) – notiv

+0

@notiv、私の編集内容を参照してください –

関連する問題