2017-08-20 48 views
0

为了磨练我的python和Spark GraphX技能,我一直在尝试构建一个Pinboard用户和书签图。为了做到这一点,我递归刮插件板书签以下列方式:用Scrapy递归地刮擦插接板 - “蜘蛛必须返回请求”错误

  1. 开始与用户和刮所有书签
  2. 每个书签,由url_slug鉴定,发现也拯救了所有用户相同的书签。
  3. 对于从第2步的每个用户,重复上述过程,(去1,...),尽管有来自多个线程在这里(包括使用规则),当我尝试实现这个逻辑试图建议

,我得到以下错误:

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

我强烈怀疑有在我的代码这样的搭配yield/return做。

这里我的代码的简短描述:

我主要的解析方法找到所有书签项目为一个用户(也是继与同一用户的书签以往任何网页),并产生了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,然后产生一个scrapy.Request解析url_slug:

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 

(为了呈现更简明的方式代码,我去掉其中I存储其它有趣字段或重复检查等份)

任何帮助将不胜感激!

回答

0

的问题是你的代码

yield self.parse_bookmark(bookmark) 

下面的框由于您parse_bookmark你有下面两个线路

# 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 

既然你有yield这个函数的返回值是发电机。并且您将该生成器返回给Scrapy,并且不知道如何处理它。

修复很简单。将您的代码更改为以下代码

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)和[这一个](https://stackoverflow.com/questions/31890731/scrapy-擦除数据但不输出到文件)表明我应该返回一个项目。我以为我会返回一个,更确切地说,是两个项目(一个PinscrapyItem和一个UrlSlugItem)。我错过了什么吗?我没有使用管道。 – notiv

+0

我忘了提,刮自己的作品('下载/ response_count':41) – notiv

+0

@notiv,看到我编辑 –