只抓取 URL 一次的 Scrapy 蜘蛛

Scrapy spider that only crawls URLs once

我正在写一个 Scrapy 蜘蛛,它每天抓取一组 URL 一次。然而,其中一些网站非常大,所以我无法每天抓取整个网站,也不想产生这样做所需的大量流量。

一个老问题 (here) asked something similar. However, the upvoted response simply points to a code snippet (here),似乎需要请求实例的某些内容,尽管在响应中和包含代码片段的页面上都没有解释。

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

我已经在下面发布了代码来开始工作,但我不一定需要使用这个中间件。任何可以每天爬行并提取新的 URLs 的 scrapy 蜘蛛都可以。显然,一个解决方案是只写出一个刮掉的 URL 的字典,然后检查以确认每个新的 URL is/isn 不在字典中,但这看起来很 slow/inefficient.

蜘蛛

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)

事情是这样的,您想要做的是能够拥有一个您正在抓取的数据库 scheduled/croned。 dupflier.middleware 不管你是否仍然需要抓取整个网站......我觉得尽管很明显提供的代码不能是整个项目,但代码太多了。

我不太确定你在 scraping 是什么,但我现在假设你有 CNN 作为项目 URL 你在 scraping文章?

我会做的是使用 CNN 的 RSS 提要甚至站点地图,因为它提供了文章元的截止日期并使用 OS 模块...

定义每个抓取实例的日期 使用正则表达式将爬虫定义的日期限制为文章发布日期的条目 部署和安排抓取 to/in scrapinghub 使用 scrapinghubs python api 客户端遍历项目

仍然会抓取整个站点的内容,但是使用 xmlspider 或 rssspider class 非常适合更快地解析所有数据...现在数据库在 "cloud" 中可用.. . 我觉得可以根据项目的可扩展性更加模块化,也更容易 portability/cross-compatibility

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