Python 在404页的10次之后,我如何停止刮擦?

Python 在404页的10次之后,我如何停止刮擦?,python,scrapy,Python,Scrapy,我有一个项目,在两个数字之间刮页。我的蜘蛛在下面。它从一个数字开始到一个数字,然后在这些页面之间擦掉 我想让它在连续10页404之后停止。但它必须保存CSV,直到停止位置 额外:是否可以让它将停止位置的数字写入另一个文本文件 以下是我的日志示例: 2017-01-25 19:57:25 [scrapy.core.scraper] DEBUG: Scraped from <200 https://domain.com/entry/65848514> {'basligi': [u'mur

我有一个项目,在两个数字之间刮页。我的蜘蛛在下面。它从一个数字开始到一个数字,然后在这些页面之间擦掉

我想让它在连续10页404之后停止。但它必须保存CSV,直到停止位置

额外:是否可以让它将停止位置的数字写入另一个文本文件

以下是我的日志示例:

2017-01-25 19:57:25 [scrapy.core.scraper] DEBUG: Scraped from <200 https://domain.com/entry/65848514>
{'basligi': [u'murat boz'],
 'entry': [u'<a href=https://domain.com/entry/65848514'],
 'favori': [u'0'],
 'yazari': [u'thrones']}
2017-01-25 19:57:25 [scrapy.core.scraper] DEBUG: Scraped from <200 https://domain.com/entry/65848520>
{'basligi': [u'fatih portakal'],
 'entry': [u'<a href=https://domain.com/entry/65848520'],
 'favori': [u'0'],
 'yazari': [u'agamustaf']}
2017-01-25 19:57:26 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://domain.com/entry/65848525> (referer: None)
2017-01-25 19:57:26 [scrapy.core.engine] DEBUG: Crawled (404) <GET https://domain.com/entry/65848528> (referer: None)
2017-01-25 19:57:26 [scrapy.core.engine] DEBUG: Crawled (404) <GET https://domain.com/entry/65848529> (referer: None)
2017-01-25 19:57:26 [scrapy.core.engine] DEBUG: Crawled (404) <GET https://domain.com/entry/65848527> (referer: None)
还有我的蜘蛛:

from scrapy.spider import BaseSpider
from scrapy.selector import HtmlXPathSelector
from scrapy.selector import Selector
from project.items import ProjectItem
from scrapy import Request

class MySpider(BaseSpider):
    name = "project"
    allowed_domains = ["domain.com"]
    start_urls = ["https://domain.com/entry/%d" % i for i in range(65848505,75848535)]


def parse(self, response):

    titles = HtmlXPathSelector(response).select('//li')
    for title in titles:
        item = ProjectItem()
        item['favori'] = title.select("//*[@id='entry-list']/li/@data-favorite-count").extract()
        item['entry'] = ['<a href=https://domain.com%s'%a for a in title.select("//*[@class='entry-date permalink']/@href").extract()]
        item['yazari'] = title.select("//*[@id='entry-list']/li/@data-author").extract()
        item['basligi'] = title.select("//*[@id='topic']/h1/@data-title").extract()

        return item

有很多方法可以做到这一点,最简单的方法是通过回调捕获404个错误,对它们进行计数,并在特定条件下引发CloseSpider异常。例如:

from scrapy.spider import BaseSpider
from scrapy.selector import HtmlXPathSelector
from scrapy.selector import Selector
from project.items import ProjectItem
from scrapy import Request
from scrapy.exceptions import CloseSpider

class MySpider(BaseSpider):
    name = "project"
    allowed_domains = ["domain.com"]
    start_urls = ["https://domain.com/entry/%d" % i for i in range(65848505,75848535)]
    handle_httpstatus_list = [404] # to catch 404 with callback
    count_404 = 0


    def parse(self, response):
        if response.status == 404:
            self.count_404 += 1
            if self.count_404 == 10:
                # stop spider on condition
                raise CloseSpider('Number of 404 errors exceeded')
            return None
        else:
            self.count_404 = 0
        titles = HtmlXPathSelector(response).select('//li')
        for title in titles:
            item = ProjectItem()
            item['favori'] = title.select("//*[@id='entry-list']/li/@data-favorite-count").extract()
            item['entry'] = ['<a href=https://domain.com%s'%a for a in title.select("//*[@class='entry-date permalink']/@href").extract()]
            item['yazari'] = title.select("//*[@id='entry-list']/li/@data-author").extract()
            item['basligi'] = title.select("//*[@id='topic']/h1/@data-title").extract()

            return item
更优雅的解决方案是编写定制的downloader中间件来处理这种情况


注意:保留了问题中的起始URL,但是生成10000个链接的列表并将其保存在内存中会带来极大的开销,您应该使用生成器来生成起始URL或覆盖起始请求。

对于更干净的项目,您可以将其作为扩展:

extensions.py

然后不要忘记在设置中激活它,并使用此扩展正在添加的新设置变量:

设置.py


现在,您可以从设置中配置状态和计数。

感谢回复,但这总共计404页。我需要连续404页的10倍。因此,它必须在404之后停止。由于底层Twisted框架的异步性质,在Scrapy中处理连续性非常困难,并且只有当您使用CONCURRENT_requests=1或逐个发出请求时,请求才能真正连续。在这种情况下,只需重置response.status==200上的计数。如果希望保持并发性,则需要为中间件或扩展创建高级逻辑。在If response.status==404之后添加一个else怎么样:else self.count_404=0是的,我的意思是我添加了我说过的其他内容,但日志显示调试:爬网200太多。现在怎么了?谢谢你的回复。我有两个问题,这是连续10页的404页还是总共404页?如果是这样,我该如何运行我的项目?用我的蜘蛛还是用我的分机?我是说搔痒的爬行。。。首先,scrapy是异步的,因此很难真正知道您将获得10个连续的404状态,但无论如何,我将更改代码来处理它。现在,要启用它,您只需按照我在答案中的说明执行,只需将扩展名复制到extensions.py文件中,然后在settings.py中使用此处显示的代码启用它。它通常不会处理连续状态,因为downloader不会按连续顺序返回响应。不过,由于某种程度的“饱和”,它当然会起作用,但在10次请求之后就不行了。@mizhgun准确地说,它会在一段时间后响应超过10次,因为这些链接将在某个点结束,所以我想让它在这一点上停止。这就是我需要这个解决方案的原因。所以下载程序不按连续顺序返回响应是没有问题的,因为在开始刮取404链接之后,所有响应都会返回404。我希望我能讲述我的故事:@eLRuLL首先它说全球名称信号没有定义。然后我安装了signals并将import signals代码放在extensions.py的开头,但它说attributeerror模块对象现在没有收到属性响应
from scrapy.exceptions import NotConfigured
from scrapy import signals
from urlparse import urlparse

class CloseSpiderByStatusCount(object):

    def __init__(self, crawler):
        if not crawler.settings.getint('CLOSESPIDER_BYSTATUS_ENABLED', False):
            raise NotConfigured

        self.crawler = crawler
        self.status = crawler.settings.getint('CLOSESPIDER_BYSTATUS_STATUS', 404)
        self.closing_count = crawler.settings.getint('CLOSESPIDER_BYSTATUS_COUNT', 10)
        self.count = 0

        crawler.signals.connect(self.status_count, signal=signals.response_received)

    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler)

    def status_count(self, response, request, spider):
        if response.status == self.status:
            self.count += 1
        else:
            self.count = 0

        if self.count == self.closing_count:
            f = open('filename.txt', 'w')
            f.write(urlparse(request.url).path.split('/')[-1])
            self.crawler.engine.close_spider(spider, 'closespider_statuscount')
# activating the extension

EXTENSIONS = {
    ...
    'myproject.extensions.CloseSpiderByStatusCount': 100,
    ...
}

CLOSESPIDER_BYSTATUS_ENABLED = True
CLOSESPIDER_BYSTATUS_STATUS = 404
CLOSESPIDER_BYSTATUS_COUNT = 10