【问题标题】:How to remove expired items from database with Scrapy如何使用 Scrapy 从数据库中删除过期项目
【发布时间】:2010-01-12 19:44:47
【问题描述】:

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

检测物品是否过期的策略是:

  1. 蜘蛛网站的“delete.rss”。
  2. 每隔几天,尝试重新加载内容页面并确保它仍然有效。
  3. 爬取网站内容索引的每一页,如果找不到视频,则将其删除。

请告诉我如何删除 scrapy 中的过期项目。我将通过 django 将我的 scrapy 项目存储在 mysql DB 中。

2010-01-18 更新

我找到了一个可行的解决方案,但仍可能不是最佳的。我在我同步的每个视频上都维护一个“found_in_last_scan”标志。当蜘蛛启动时,它将所有标志设置为 False。完成后,它会删除仍将标志设置为 False 的视频。我通过附加到signals.spider_openedsignals.spider_closed 来做到这一点,请确认这是一个有效的策略并且没有任何问题。

【问题讨论】:

  • 什么是'delete.rss'?您究竟如何存储抓取的内容?过期物品实际上是什么意思?这似乎是一个“为我编写代码”的问题,而不是一个实际的问题。
  • “过期项目”是其 URL 不再有效的视频。许多网站提供“delete.rss”提要,告诉您他们何时删除了视频,但有些网站没有。当然,这一定是scrapy的常见问题。 Scrapy 的网站提供了一个示例,说明了如何从 bittorrent 网站联合内容,但没有提及它如何检测 torrent 何时不再存在。
  • 你抓取的所有网站是否都为你提供了 delete.rss?
  • 只有一些网站有 delete.rss,我不确定它是否 100% 可靠,即使它存在。

标签: python screen-scraping scrapy


【解决方案1】:

我没有测试过这个!
我不得不承认我没有尝试在 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()

不是供您使用的工作蜘蛛,但 IIRC 的 RSS 文件是纯 XML。我不确定deleted.rss 的外观如何,但我相信您可以弄清楚如何从 XML 中提取 URL。现在,此示例导入​​ myproject.items.DeletedUrlItem,在此示例中它只是一个字符串,但您需要使用以下代码创建 DeletedUrlItem:

您需要创建 DeletedUrlItem:

class DeletedUrlItem(Item):
    url = Field()

delete the items using Django's Model API 不是保存,而是 Scrapy's 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()


我知道这个答案可能包含错误,它是由内存编写的 :-) 但如果您有 cmets 或无法弄清楚,我一定会更新。

【讨论】:

  • 不错的答案。既然我将拥有DeleteUrlItemsVideoItems,你会在DeleteUrlPipeline 中检查isinstance 以确保它只在DeletedUrlItems 上运行吗?
【解决方案2】:

如果您怀疑有一个 HTTP URL 可能不再有效(因为您在“已删除”的 Feed 中找到它,或者只是因为您有一段时间没有检查它),那么最简单、最快检查的方法是为该 URL 发送 HTTP HEAD 请求。在 Python 中,最好使用标准库的 httplib 模块来完成:使用 HTTPConnection 将连接对象 c 连接到感兴趣的主机(如果是 HTTP 1.1,它可能可以重用以检查多个 URL,并具有更好的性能和降低系统负载),然后执行一个(或多个,如果可行,即如果使用 HTTP 1.1)调用crequest 方法,第一个参数“HEAD”,第二个参数是您正在检查的 URL(当然没有主机部分;-)。

在每个request 之后调用c.getresponse() 以获取HTTPResponse 对象,其status 属性会告诉您该URL 是否仍然有效。

是的,它有点低级,但正是由于这个原因,它可以让您更好地优化您的任务,只需一点 HTTP 知识;-)。

【讨论】:

  • 我认为在这种情况下这是一个糟糕的答案,就像告诉使用 Django 的程序员进行手动 HTTP 响应一样......也就是说,这是使用 Python 检查 URL 有效性的一种完全有效的方法.
  • @Hannson,我不同意使用稍低的抽象层是“不好的”,因为使用该层提供了比在更高层操作的重要改进——特别是如果删除提要不是 100%依赖于完整(在某些站点甚至不存在!),定期重新抓取所有内容(隐式使用 HTTP GETs)而不是使用 HTTP HEAD 进行检查只是对珍贵的肆意浪费资源。
  • 没错,低级!= 不好,但对于高级网络爬虫框架,它可能不是最好的方法。我假设这个特定网站有一个 delete.rss 提要(我可能错了),但我的意思是,你的答案不是提问者(可能)要找的——不过我可能是错的;虽然您的答案不正确,但 IMO 也不是“正确的”。为了记录,我没有投票赞成或反对你的答案 - 无论哪种方式都是有效的。
  • 我不知何故错过了这一点:'我不同意使用稍低的抽象层是“不好的”,因为使用该层提供了比在更高层操作的重要改进',我完全同意!越低越好,越低越好;说得够多了!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-11
  • 2012-01-27
  • 1970-01-01
  • 2017-12-11
  • 2021-10-15
相关资源
最近更新 更多