如何使用Scrapy从数据库中删除过期的项目

3

我正在使用爬虫抓取一个视频网站,该网站频繁过期内容。我考虑使用scrapy进行爬虫,但不确定如何删除过期的内容。

检测项目是否过期的策略有:

  1. 爬取网站的“delete.rss”。
  2. 每隔几天重新加载内容页面,并确保它仍然有效。
  3. 爬取网站内容索引的每个页面,并在未找到视频时将其删除。

请告诉我如何在scrapy中删除过期的内容。我将通过django将我的scrapy项目存储在mysql数据库中。

2010-01-18更新

我已经找到了一个可行的解决方案,但可能还不是最优的。我在同步每个视频时维护一个“found_in_last_scan”标志。当爬虫启动时,它将所有标志设置为False。完成后,删除仍然设置为False的视频。我通过连接signals.spider_openedsignals.spider_closed来实现这一点。请确认这是一个有效的策略,没有问题。


1
“delete.rss”是什么?你是如何存储抓取的内容的?过期的项目实际上是什么意思?这似乎是一个“帮我编写代码”的问题,而不是一个真正的问题。 - DisplacedAussie
1
“过期项目”是指URL不再有效的视频。许多网站提供“delete.rss”源以告知您何时删除视频,但有些则没有。这肯定是Scrapy中常见的问题。Scrapy的网站提供了一个示例,说明如何从BitTorrent网站联合内容,但没有提及如何检测种子是否不存在。 - Gattster
您正在爬取的所有网站是否都提供了 delete.rss? - hannson
只有一些网站有 delete.rss,即使存在,我也不确定它是否百分之百可靠。 - Gattster
2个回答

4

我没有测试过这个!
我必须承认,我没有尝试在Scrapy中使用Django模型,但是我们开始吧:

我想象中最简单的方法是通过扩展XMLFeedSpider创建一个新的爬虫来处理deleted.rss文件(从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的Model 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()

我知道这个答案可能有错误,它是根据我的记忆写的 :-) 但如果您有评论或无法理解,请放心我会更新。


好的回答。由于我将具有DeleteUrlItemsVideoItems,您会在DeleteUrlPipeline中执行isinstance检查,以确保它仅在DeletedUrlItems上运行吗? - Gattster

0
如果你有一个HTTP URL,你怀疑它可能已经失效了(因为你在“已删除”feed中找到它,或者仅仅是因为你很久没有检查它了),那么最简单、最快速的检查方法是发送一个HTTP HEAD 请求到该URL。在Python中,最好使用标准库的httplib模块:先用HTTPConnection建立一个与目标主机的连接对象c(如果是HTTP 1.1协议,则可重复利用以提高性能和降低系统负载),然后调用crequest方法一次(或多次,如果可行,即如果使用了HTTP 1.1协议),第一个参数为'HEAD',第二个参数为你要检查的URL(当然不包括主机部分;-))。
在每个request之后,您调用c.getresponse()以获取一个HTTPResponse对象,其status属性将告诉您URL是否仍然有效。
是的,它有点底层,但正因为如此,它让您只需要了解一点HTTP知识就能更好地优化任务。;-)

我认为在这种情况下这是一个糟糕的答案,就像告诉一个使用Django的程序员手动处理HTTP响应一样... 话虽如此,使用Python检查URL的有效性是完全有效的方法。 - hannson
@Hannson,我不同意使用稍低的抽象层是“不好”的观点,因为在使用该层时,与在更高的层上操作相比,它提供了重要的改进-特别是如果删除源不能100%地保证完整性(在某些网站上甚至不存在!),定期重新爬取所有内容(使用HTTP GET隐式)而不是仅使用HTTP HEAD进行检查,这只是对宝贵资源的浪费。 - Alex Martelli
真实的情况是,低级别并不等于不好,但对于高级网络爬虫框架来说可能不是最好的方式。我假设这个特定的网站有一个delete.rss订阅源(我可能错了),但我的观点是你的答案不是问题提问者(可能)正在寻找的 - 我可能是错的;虽然你的答案不是错误的,但在我看来也不是“正确”的。就记录而言,我没有投票支持或反对你的答案 - 无论哪种方式都是有效的。 - hannson
我不知道为什么错过了这句话:“我不同意使用稍低的抽象层是“不好”的说法,当使用该层比在更高的层上操作提供重要改进时。” 我完全同意! 当较低级别更好时,它只是更好;没什么好说的! - hannson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接