如何用Scrapy删除数据库中过期项
我正在抓取一个视频网站,这个网站经常会过期内容。我考虑使用 scrapy 来进行抓取,但不太确定如何删除过期的项目。
检测一个项目是否过期的策略有:
- 抓取网站的 "delete.rss"。
- 每隔几天,尝试重新加载内容页面,确保它仍然可以正常工作。
- 抓取网站内容索引的每一页,如果找不到视频就删除它。
请告诉我如何在 scrapy 中删除过期的项目。我会通过 django 将我的 scrapy 项目存储在 mysql 数据库中。
2010-01-18 更新
我找到了一种有效的解决方案,但可能还不是最优的。我在每个我同步的视频上维护一个 "found_in_last_scan" 的标记。当爬虫开始时,它将所有标记设置为 False。当它完成时,删除那些仍然标记为 False 的视频。我是通过连接 signals.spider_opened
和 signals.spider_closed
来实现的。请确认这是一种有效的策略,并且没有问题。
2 个回答
如果你有一个HTTP网址,怀疑它可能已经失效了(比如你在一个“已删除”的信息源里找到了它,或者你很久没检查过这个网址),最简单、最快的方法就是发送一个HTTP HEAD
请求来检查这个网址。在Python中,最好的做法是使用标准库里的httplib模块:首先用HTTPConnection创建一个连接对象c
,连接到你感兴趣的主机(如果使用HTTP 1.1,这样可以重复使用连接来检查多个网址,性能更好,系统负担更轻),然后调用c
的request方法,第一次参数是'HEAD',第二个参数是你要检查的网址(当然不包括主机部分;-)。
每次调用request
后,你需要调用c.getresponse()
来获取一个HTTPResponse对象,这个对象的status
属性会告诉你这个网址是否仍然有效。
是的,这个方法有点底层,但正因为如此,它能让你更好地优化你的任务,只需要对HTTP有一点了解就可以了;-).
我还没有测试这个!
我得承认,我还没有尝试在Scrapy中使用Django模型,但我来试试解释一下:
我想最简单的方法是为deleted.rss
文件创建一个新的爬虫,方法是扩展XMLFeedSpider(这是从Scrapy文档中复制过来的,然后进行了修改)。我建议你创建一个新的爬虫,因为接下来的逻辑和抓取网站的逻辑关系不大:
from scrapy import log
from scrapy.contrib.spiders import XMLFeedSpider
from myproject.items import DeletedUrlItem
class MySpider(XMLFeedSpider):
domain_name = 'example.com'
start_urls = ['http://www.example.com/deleted.rss']
iterator = 'iternodes' # This is actually unnecesary, since it's the default value
itertag = 'item'
def parse_node(self, response, url):
url['url'] = node.select('#path/to/url').extract()
return url # return an Item
SPIDER = MySpider()
这不是一个可以直接使用的爬虫,但如果我没记错的话,RSS文件是纯XML格式。我不太确定deleted.rss
的具体样子,但我相信你能搞清楚如何从XML中提取URL。现在,这个例子中导入了myproject.items.DeletedUrlItem
,在这个例子里它只是一个字符串,但你需要用下面的代码创建DeletedUrlItem:
你需要创建DeletedUrlItem:
class DeletedUrlItem(Item):
url = Field()
而不是保存,你可以通过使用Django的模型API删除这些项目,在Scrapy的ItemPipeline中进行操作——我假设你正在使用DjangoItem:
# we raise a DropItem exception so Scrapy
# doesn't try to process the item any further
from scrapy.core.exceptions import DropItem
# import your model
import django.Model.yourModel
class DeleteUrlPipeline(item):
def process_item(self, spider, item):
if item['url']:
delete_item = yourModel.objects.get(url=item['url'])
delete_item.delete() # actually delete the item!
raise DropItem("Deleted: %s" % item)
注意delete_item.delete()
这一行。
我知道这个回答可能有错误,因为我是凭记忆写的 :-) 但如果你有评论或者搞不清楚,我一定会更新的。