【问题标题】:Using middleware to prevent scrapy from double-visiting websites使用中间件防止scrapy重复访问网站
【发布时间】:2013-01-17 22:33:33
【问题描述】:

我有这样的问题:

how to filter duplicate requests based on url in scrapy

所以,我不希望网站被多次抓取。我调整了中间件并编写了一个打印语句来测试它是否正确分类了已经看到的网站。确实如此。

尽管如此,解析似乎被执行了多次,因为我收到的 json-File 包含双重条目。

from scrapy.contrib.spiders import CrawlSpider, Rule
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
from scrapy.selector import HtmlXPathSelector
from scrapy.item import Item

from crawlspider.items import KickstarterItem

from HTMLParser import HTMLParser

### code for stripping off HTML tags:
class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.fed = []
    def handle_data(self, d):
        self.fed.append(d)
    def get_data(self):
        return str(''.join(self.fed))

def strip_tags(html):
    s = MLStripper()
    s.feed(html)
    return s.get_data()
###

items = []

class MySpider(CrawlSpider):
    name = 'kickstarter'
    allowed_domains = ['kickstarter.com']
    start_urls = ['http://www.kickstarter.com']

    rules = (
        # Extract links matching 'category.php' (but not matching 'subsection.php')
        # and follow links from them (since no callback means follow=True by default).
        Rule(SgmlLinkExtractor(allow=('discover/categories/comics', ))),

        # Extract links matching 'item.php' and parse them with the spider's method parse_item
        Rule(SgmlLinkExtractor(allow=('projects/', )), callback='parse_item'),
    )

    def parse_item(self, response):
        self.log('Hi, this is an item page! %s' % response.url)

        hxs = HtmlXPathSelector(response)
        item = KickstarterItem()

        item['date'] = hxs.select('//*[@id="about"]/div[2]/ul/li[1]/text()').extract()
        item['projname'] = hxs.select('//*[@id="title"]/a').extract()
        item['projname'] = strip_tags(str(item['projname']))

        item['projauthor'] = hxs.select('//*[@id="name"]')
        item['projauthor'] = item['projauthor'].select('string()').extract()[0]

        item['backers'] = hxs.select('//*[@id="backers_count"]/data').extract()
        item['backers'] = strip_tags(str(item['backers']))

        item['collmoney'] = hxs.select('//*[@id="pledged"]/data').extract()
        item['collmoney'] = strip_tags(str(item['collmoney']))

        item['goalmoney'] = hxs.select('//*[@id="stats"]/h5[2]/text()').extract()
        items.append(item)
        return items

我的 items.py 看起来像这样:

# Define here the models for your scraped items
#
# See documentation in:
# http://doc.scrapy.org/topics/items.html

from scrapy.item import Item, Field

class KickstarterItem(Item):
    # define the fields for your item here like:
    date = Field()
    projname = Field()
    projauthor = Field()
    backers = Field()
    collmoney = Field()
    goalmoney = Field()
    pass

我的中间件如下所示:

import os

from scrapy.dupefilter import RFPDupeFilter
from scrapy.utils.request import request_fingerprint

class CustomFilter(RFPDupeFilter):
def __getid(self, url):
    mm = url.split("/")[4] #extracts project-id (is a number) from project-URL
    print "_____________", mm
    return mm

def request_seen(self, request):
    fp = self.__getid(request.url)
    self.fingerprints.add(fp)
    if fp in self.fingerprints and fp.isdigit(): # .isdigit() checks wether fp comes from a project ID
        print "______fp is a number (therefore a project-id) and has been encountered before______"
        return True
    if self.file:
        self.file.write(fp + os.linesep)

我在 settings.py 中添加了这一行:

DUPEFILTER_CLASS = 'crawlspider.duplicate_filter.CustomFilter'

我使用“scrapy crawl kickstarter -o items.json -t json”调用脚本。然后我从中间件代码中看到了正确的打印语句。 关于为什么 json 包含包含相同数据的多个条目的任何 cmets?

【问题讨论】:

  • 你能发布你所有的蜘蛛代码吗?这将使跟踪重复项变得更加容易。 :)
  • @Talvalin 我在原始帖子中添加了一些代码。感谢您的帮助。
  • 您不使用默认的scrapy方法的任何具体原因,因为已经有一个dupefilter实现和活动的AFAIK。
  • @DrColossos :如果我刚刚从 settings.py 中删除了“DUPEFILTER_CLASS = 'crawlspider.duplicate_filter.CustomFilter'”行,这个标准的 dupefilter 会被激活吗?当我尝试这个时,我仍然在 json-File 中有多个条目。
  • 我记得我没有使用标准 dupefilter 的原因:有一组概述页面,其中包含指向我感兴趣的十个项目站点的链接。所以重访策略应该是我只是不想重新访问项目站点。所以也许重点是改变蜘蛛中的规则部分?

标签: python web-crawler scrapy


【解决方案1】:

所以现在这些是删除重复项的三个修改:

我将此添加到 settings.py 中: ITEM_PIPELINES = ['crawlspider.pipelines.DuplicatesPipeline',]

让 scrapy 知道我在 pipelines.py 中添加了 DuplicatesPipeline 函数:

from scrapy import signals
from scrapy.exceptions import DropItem

class DuplicatesPipeline(object):

    def __init__(self):
        self.ids_seen = set()

    def process_item(self, item, spider):
        if item['projname'] in self.ids_seen:
            raise DropItem("Duplicate item found: %s" % item)
        else:
            self.ids_seen.add(item['projname'])
            return item

你不需要调整蜘蛛,也不要使用我之前发布的 dupefilter/middleware 的东西。

但我觉得我的解决方案不会减少通信,因为必须先创建项目对象,然后才能对其进行评估并可能删除它。但我没关系。

(提问者找到的解决方案,移到答案中)

【讨论】:

  • 看起来您仍会废弃所有重复的页面。您只在输出结果之前过滤项目
  • 虽然这个问题谈到了重复请求是问题所在,但我相信不同的请求包含重复项,这是实际的潜在问题,所以解决方案是正确的,这是需要一些工作的问题.
猜你喜欢
  • 2019-03-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多