我有一只蜘蛛每天抓取有数十万页的网站。页面本身在发布时很少发生变化,但它们会一直被删除/添加。大多数页面仅在几周或几个月内有效。
所以我想阻止我的Spider在已经被抓取一次之后抓取页面,而只是在Sitemap中找到页面时对我的数据库进行快速SQL Update调用(这意味着页面仍然存在)我可以更新" last_found_date"列。)
我想最好的方法是覆盖SitemapSpider
并阻止蜘蛛首先发送请求,如果我的数据库中已存在该网址。
怎么做?
答案 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