使用Python和Scrapy进行递归爬行

imn*_*mns 12 python django scrapy

我正在使用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()
Run Code Online (Sandbox Code Playgroud)

这些线是问题所在.就像我之前说过的那样,它们在蜘蛛爬完当前页面之前就被执行了.在网站的每个页面上,这导致我的列表中只有3个被发送到管道.

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

这是我的第一个蜘蛛,可能是我的设计缺陷,有更好的方法吗?

Mah*_*der 1

请参阅下面的 EDIT 2 部分的更新答案(2017 年 10 月 6 日更新)

\n\n

您使用产量有什么具体原因吗?.next()Yield 将返回一个生成器,当调用该生成器时,该生成器将返回 Request 对象。

\n\n

yield将您的陈述更改为return陈述,事情应该按预期进行。

\n\n

这是生成器的示例:

\n\n
In [1]: def foo(request):\n   ...:     yield 1\n   ...:     \n   ...:     \n\nIn [2]: print foo(None)\n<generator object foo at 0x10151c960>\n\nIn [3]: foo(None).next()\nOut[3]: 1\n
Run Code Online (Sandbox Code Playgroud)\n\n

编辑:

\n\n

更改您的def start_requests(self)函数以使用该follow参数。

\n\n
return [Request(self.start_url, callback=self.parse_listings, follow=True)]\n
Run Code Online (Sandbox Code Playgroud)\n\n

编辑2:

\n\n

从2017-05-18发布的Scrapy v1.4.0开始,现在建议使用response.follow而不是scrapy.Request直接创建对象。

\n\n

发行说明来看:

\n\n
\n

有\xe2\x80\x99s一个新的response.follow方法来创建请求;现在,这是在 Scrapy 蜘蛛中创建请求的推荐方法。这种方法可以更容易地编写正确的爬虫程序;与直接创建 scrapy.Request 对象相比,response.follow 有几个优点:

\n\n
    \n
  • 它处理相对 URL;
  • \n
  • 它可以与非 UTF8 页面上的非 ASCII URL 正常工作;
  • \n
  • 除了绝对和相对 URL 之外,它还支持选择器;对于元素,它还可以提取它们的 href 值。
  • \n
\n
\n\n

因此,对于上面的OP,将代码更改为:

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

到:

\n\n
    next_page_url = hxs.select(\'descendant::div[@id="pagination"]/\'\n                               \'div[@class="next-link"]/a/@href\')\n    if next_page_url is not None:\n        yield response.follow(next_page_url, self.parse_listings)\n
Run Code Online (Sandbox Code Playgroud)\n

  • follow 看起来不像是 Request() 的参数,我收到错误“得到意外的关键字参数 'follow'” (2认同)