在解析之前检查SitemapSpider中的URL

时间:2015-03-12 10:07:08

标签: python web-scraping scrapy

我有一只蜘蛛每天抓取有数十万页的网站。页面本身在发布时很少发生变化,但它们会一直被删除/添加。大多数页面仅在几周或几个月内有效。

所以我想阻止我的Spider在已经被抓取一次之后抓取页面,而只是在Sitemap中找到页面时对我的数据库进行快速SQL Update调用(这意味着页面仍然存在)我可以更新" last_found_date"列。)

我想最好的方法是覆盖SitemapSpider并阻止蜘蛛首先发送请求,如果我的数据库中已存在该网址。

怎么做?

1 个答案:

答案 0 :(得分:1)

我能够通过覆盖start_request()函数并根据parse_sitemap() SitemapSpider函数创建自己的_parse_sitemap()函数来解决此问题。

我是按照以下方式做到的:

1)在你自己的蜘蛛__init__()中。打电话给父母初始。这是parse_sitemap函数中某些功能所必需的。

def __init__(self, *a, **kw):
    super(HemnetSitemapSpider, self).__init__(*a, **kw)

2)您需要创建自己的start_request()。如果不是,则默认start_request()将呼叫父母_parse_sitemap()

def start_requests(self):
    return (scrapy.Request(x, callback=self.parse_sitemap) for x in self.sitemap_urls)

3)最后,您需要创建自己的parse_sitemap()。进入scrapy包文件夹并打开包含父类的原始sitemap.py文件,并复制_parse_sitemap()的整个函数。

在该功能中有一部分说:

elif s.type == 'urlset':
    ...

这是找到不是子站点地图的URL的部分,您可以在此处检查URL是否已存在于您的数据库中,或者您想要执行此操作。

所以我的parse_sitemap()函数看起来像这样:

def parse_sitemap(self, response):
    if response.url.endswith('/robots.txt'):
        for url in sitemap_urls_from_robots(response.body):
            yield Request(url, callback=self.parse_sitemap)
    else:
        body = self._get_sitemap_body(response)
        if body is None:
            log.msg(format="Ignoring invalid sitemap: %(response)s",
                    level=log.WARNING, spider=self, response=response)
            return

        s = Sitemap(body)
        if s.type == 'sitemapindex':
            for loc in iterloc(s, self.sitemap_alternate_links):
                if any(x.search(loc) for x in self._follow):
                    yield Request(loc, callback=self.parse_sitemap)
        # If this is a URL Set, then check if it has been parsed before.
        elif s.type == 'urlset':
            for loc in iterloc(s):
                for r, c in self._cbs:
                    if r.search(loc):
                        # Check here for history URL
                        try:
                            self.cursor.execute('_YOUR_SQL_QUERY_', [loc])
                            row = self.cursor.fetchone()
                        except MySQLdb.Error, e:
                            print "Error %d: %s" % (e.args[0], e.args[1])

                        # If no row exist from that source, then send the request.
                        if row is None:
                            yield Request(loc, callback=c)
                        # Else, if this source already exists. Update the date_updated field
                        else:
                            # Update date_updated
                            try:
                                date = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
                                self.cursor.execute('_YOUR_UPDATE_QUERY_', [date, row[0]])
                            except MySQLdb.Error, e:
                                print "Error %d: %s" % (e.args[0], e.args[1])

                        # Break for loop.
                        break