只捕获一次URL的Scrapy蜘蛛

时间:2016-06-10 01:37:32

标签: python scrapy web-crawler middleware scrapy-spider

我正在编写一个Scrapy蜘蛛,每天抓取一组URL。但是,其中一些网站非常大,所以我不能每天抓取整个网站,也不想产生必要的大量流量。

一个老问题(here)问了类似的问题。但是,upvoted响应只是指向一个代码片段(here),它似乎需要一些请求实例,尽管在响应中没有解释,也没有在包含代码片段的页面上解释。

我试图理解这一点,但发现中间件有点令人困惑。无论是否使用链接的中间件,一个可以多次运行而不重新编写URL的刮刀的完整示例将非常有用。

我已经发布了以下代码来推动这项工作,但我不一定需要使用这个中间件。任何可以每天抓取并提取新网址的scrapy蜘蛛都可以。显然,一种解决方案是只写出一个已删除URL的字典,然后检查确认每个新URL是否在字典中,但这似乎非常慢/效率低。

蜘蛛

from scrapy.contrib.spiders import CrawlSpider, Rule
from scrapy.contrib.linkextractors import LinkExtractor
from cnn_scrapy.items import NewspaperItem



class NewspaperSpider(CrawlSpider):
    name = "newspaper"
    allowed_domains = ["cnn.com"]
    start_urls = [
        "http://www.cnn.com/"
    ]

    rules = (
        Rule(LinkExtractor(), callback="parse_item", follow=True),
    )

    def parse_item(self, response):
        self.log("Scraping: " + response.url)
        item = NewspaperItem()
        item["url"] = response.url
        yield item

import scrapy


class NewspaperItem(scrapy.Item):
    url = scrapy.Field()
    visit_id = scrapy.Field()
    visit_status = scrapy.Field()

中间件(ignore.py)

from scrapy import log
from scrapy.http import Request
from scrapy.item import BaseItem
from scrapy.utils.request import request_fingerprint

from cnn_scrapy.items import NewspaperItem

class IgnoreVisitedItems(object):
    """Middleware to ignore re-visiting item pages if they were already visited
    before. The requests to be filtered by have a meta['filter_visited'] flag
    enabled and optionally define an id to use for identifying them, which
    defaults the request fingerprint, although you'd want to use the item id,
    if you already have it beforehand to make it more robust.
    """

    FILTER_VISITED = 'filter_visited'
    VISITED_ID = 'visited_id'
    CONTEXT_KEY = 'visited_ids'

    def process_spider_output(self, response, result, spider):
        context = getattr(spider, 'context', {})
        visited_ids = context.setdefault(self.CONTEXT_KEY, {})
        ret = []
        for x in result:
            visited = False
            if isinstance(x, Request):
                if self.FILTER_VISITED in x.meta:
                    visit_id = self._visited_id(x)
                    if visit_id in visited_ids:
                        log.msg("Ignoring already visited: %s" % x.url,
                                level=log.INFO, spider=spider)
                        visited = True
            elif isinstance(x, BaseItem):
                visit_id = self._visited_id(response.request)
                if visit_id:
                    visited_ids[visit_id] = True
                    x['visit_id'] = visit_id
                    x['visit_status'] = 'new'
            if visited:
                ret.append(NewspaperItem(visit_id=visit_id, visit_status='old'))
            else:
                ret.append(x)
        return ret

    def _visited_id(self, request):
        return request.meta.get(self.VISITED_ID) or request_fingerprint(request)

1 个答案:

答案 0 :(得分:0)

这就是你想要做的事情,就是能够有一个数据库,你的爬网被安排/ croned。 dupflier.middleware或不是你仍然不得不刮掉整个网站...而且我觉得尽管显而易见的是代码提供的不是整个项目,但是那些代码太多了。

我不确定你是在抓什么,但我现在假设你有CNN作为你正在抓文章的项目网址?

我会做的是使用CNNs RSS提要甚至网站地图给出提供文章meta和使用OS模块的截止日期......

定义每个抓取实例的日期 使用正则表达式将使用爬网程序定义的日期的项目化限制为发布的日期文章 部署并安排抓取到/在scrapinghub中 使用scrapinghubs python api client迭代项目

仍然可以抓取整个网站的内容,但是使用xmlspider或rssspider类非常适合更快地解析所有数据...现在数据库可以在“云”中使用...我觉得可以更加模块化具有项目的可扩展性以及更容易的可移植性/交叉兼容性

我确信我所描述的流程会受到一些修补,但这个想法是直截了当的。