Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用Python和Scrapy进行递归爬行_Python_Django_Scrapy - Fatal编程技术网

使用Python和Scrapy进行递归爬行

使用Python和Scrapy进行递归爬行,python,django,scrapy,Python,Django,Scrapy,我正在使用scrapy来爬网一个站点。该网站每页有15个列表,然后有一个“下一步”按钮。我遇到了一个问题,在我完成对管道中所有列表的解析之前,我对下一个链接的请求被调用。以下是我的spider的代码: class MySpider(CrawlSpider): name = 'mysite.com' allowed_domains = ['mysite.com'] start_url = 'http://www.mysite.com/' def start_req

我正在使用scrapy来爬网一个站点。该网站每页有15个列表,然后有一个“下一步”按钮。我遇到了一个问题,在我完成对管道中所有列表的解析之前,我对下一个链接的请求被调用。以下是我的spider的代码:

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)

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

有关更新的答案,请参见下面的编辑2部分(2017年10月6日更新)

您使用收益率有什么具体原因吗?Yield将返回一个生成器,当对其调用
.next()
时,该生成器将返回请求对象

将您的
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
编辑2:

从2017年5月18日发布的Scrapy v1.4.0开始,现在建议使用
响应。遵循
而不是直接创建
Scrapy。请求
对象

从:

有一个新的response.follow方法用于创建请求;现在是 在Scrapy Spider中创建请求的推荐方法。这种方法 使编写正确的spider更容易;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)
致:


你可能想调查两件事

  • 您正在爬网的网站可能正在阻止您定义的用户代理
  • 尝试向您的爬行器添加下载延迟

  • 用刮擦代替蜘蛛

    因为您的原始问题需要重复导航连续和重复的内容集,而不是未知大小的内容树,所以请使用mechanize(http://wwwsearch.sourceforge.net/mechanize/)还有美丽的乌苏(http://www.crummy.com/software/BeautifulSoup/)

    下面是一个使用mechanize实例化浏览器的示例。此外,使用br.follow_链接(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。将代码驱动的页面操作最小化到只处理所需的数据将加快提取速度。

    有两种方法可以按顺序执行此操作:

  • 通过定义类下的
    列表\u url
    列表
  • 通过在
    解析清单()
    中定义
    清单url
    唯一的区别是冗长。另外,假设有五个页面要获取
    列表\u URL
    。所以也把
    page=1
    放在类下面

    parse_listings
    方法中,只发出一次请求。将所有数据放入需要跟踪的
    元中。也就是说,使用
    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()
    

    您可以根据需要多次生成请求或项目

    def parse_类别(自我,响应):
    #获取指向其他类别的链接
    categories=hxs.select('../@href').extract()
    #首先,返回CategoryItem
    产量l.装载量_项()
    对于类别中的url:
    #然后返回解析类别的请求
    屈服请求(url、self.parse_类别)
    

    我发现这里-

    我刚刚在代码中修复了同样的问题。我使用了作为Python2.7一部分的SQLite3数据库来修复它:在第一次传递parse函数时,收集信息的每个项都会将其唯一的行放入数据库表中,并且parse回调的每个实例都会将每个项的数据添加到该项的表和行中。保留一个实例计数器,以便最后一个回调解析例程知道它是最后一个,并从数据库或其他地方写入CSV文件。回调可以是递归的,在meta中被告知它要处理哪个解析模式(当然还有哪个项)。对我来说很有魅力。如果有Python,就有SQLite3。当我第一次发现scrapy在这方面的局限性时,我的帖子如下:


    这个例子展示了如何使用不同的技术从网站中删除多个后续页面,因为如果使用return,它只会抓取一个列表并停止。它不会迭代每个列表并为其创建请求。你看,我在包含所有15个列表的页面上获得了一些信息,但随后我必须对该列表的各个页面进行爬网,以获得我需要的其余信息。Yield工作得很好,直到我想添加爬网“下一页”的功能。follow看起来不像是Request()的参数,我得到了一个错误
    得到了一个意外的关键字参数“follow”
    这里同样得到了
    得到了一个意外的关键字参数“follow”
    你是怎么做到的
    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]
    
    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()