【问题标题】:Crawling domains serially with Scrapy使用 Scrapy 连续爬取域
【发布时间】:2016-11-02 10:43:15
【问题描述】:

我在数据库中有要连续抓取的网站,即:

  1. 将域添加到允许的域列表(应为空)。
  2. 将此域添加到请求中(添加http:// 效果很好)。
  3. 根据允许的域抓取请求。
  4. 前进到下一个域,仅将其添加到允许的域列表中。它将是那里唯一的一个,因此不会发生纵横交错。注意:criss-cross question 对我没有帮助,但也许我遗漏了一些东西......
  5. 抓取此请求。
  6. 依次完成我拥有的所有域。

到目前为止,我所取得的成就是抓取域,它做得很好,它完美地抓取了请求。 我唯一的问题是allowed_domains 似乎没有更新,它会抓取各种网站。

我设置了DEPTH_LIMIT=1,所以它不会是无限爬行,我还添加了 DF 爬行而不是 BF 爬行:

DEPTH_LIMIT= 1
SCHEDULER_DISK_QUEUE = 'scrapy.squeues.PickleFifoDiskQueue'
SCHEDULER_MEMORY_QUEUE = 'scrapy.squeues.FifoMemoryQueue'

这是我的蜘蛛的代码(只是开始;因为你并不真正关心我的项目处理......):

from __future__ import with_statement
import scrapy
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
from scrapy.http.request import Request
from company.items import CompanyItem, UrlItem
from urlparse import urlparse
import MySQLdb
import json


class RecipeSpider(CrawlSpider):
    name = "company"

    allowed_domains = []
    start_urls = []
    rules = (Rule(LinkExtractor(allow=(), deny=(), tags=('a', 'link', 'li' 'area')), callback='extract_raw_recipe', follow=True),)

    def start_requests(self):
        # print "before sources"
        sources = self.get_allowed_domains()
        dummy_tuple = (long(len(sources) + 1), u'dummy', None, None, None, None, None, 0L, long(len(sources) + 1),)
        final_sources = sources + (dummy_tuple,)

        for source in final_sources:
            self.allowed_domains = [str(source[1])]
            url_string = str("http://" + source[1])
            self.start_urls = [url_string]
            self.update_running_source(source)
            yield Request(url_string, self.extract_raw_recipe)
    ### start_requests()

    def extract_raw_recipe(self, response):
        pass

extract_raw_recipe 的代码没有请求或解析下一个 url,它只是工作,所以不需要编辑它。但是如果我需要在那里添加一些东西,请告诉我,因为也许这是缺少的链接。当前代码将字典添加到项目中,然后将该项目放入数据库中。

所以总结:我需要添加什么来让它在每次抓取请求时过滤域?

如果需要提供更多代码,请告诉我。

【问题讨论】:

    标签: python scrapy web-crawler scrapy-spider


    【解决方案1】:

    我的建议是采取完全不同的方法。在需要抓取的域的数据库中创建一个队列(基于您想要的任何标准),然后选择一个域进行抓取并使用allowed_domains 列表中的相关域初始化蜘蛛。爬虫完成后,为下一个域重新开始另一次爬网。重复直到队列中的所有域都完成。

    这将使您能够更好地控制整个过程(例如,重新排队失败的抓取、取消有问题的抓取并继续前进而不丢失进度、一次抓取多个域而不会出现“串扰”等)如果您打算扩展它,它还允许您执行自定义设置(例如,USER_AGENT、DUPEFILTER、DOWNLOAD_DELAY)或基于每个域的自定义规则,从而显着扩展蜘蛛的可用性。


    如果这不是一个选项,您可以重置您的 allowed_domains 列表。这样做有一些问题,但首先是关于 Scrapy 的异地过滤的一些背景知识。

    OffSiteMiddleware 负责根据allowed_domains 编译允许的域列表。它为此使用了一个正则表达式,它只在蜘蛛启动时编译一次(使用spider_opened 信号)。更新变量allowed_domains 不会对蜘蛛产生影响,除非您还强制OffSideMiddleWare 重新编译其正则表达式。

    以下方法(放置在您的蜘蛛中)应该可用于将您的 allowed_domains 替换为新列表:

    from scrapy.spidermiddlewares.offsite import OffsiteMiddleware
    
    def change_allowed_domains(self, allowed_domains):
        self.allowed_domains = allowed_domains
    
        for middleware in self.crawler.engine.scraper.spidermw.middlewares:
            if isinstance(middleware, OffsiteMiddleware):
                middleware.spider_opened(self)
    

    这将重置OffsiteMiddleware 使用的domains_seen set(),因此如果您将其用于其他任何用途,请记住这一点。

    所以,花一点时间吸收所有这些,一个问题开始浮出水面:当您在start_requests() 中对每个域进行排队时,您当前更改allowed_domains 的方法将不起作用,因为Spider 类只保留跟踪一个allowed_domains 正则表达式(根本不与请求/响应对象相关联)。如果您在蜘蛛开始爬行之前对不同域的 20 个请求进行排队(每次更改 allowed_domains 列表),它将使用最近编译的正则表达式(即,最后排队的域的 allowed_domains)。

    要克服这个问题,您需要抓取一个域的所有请求,编写自己的带有插槽的OffsiteMiddleware,并让它处理所有过滤。或者,只有在队列中的所有请求都完成并且下载器中的所有插槽都是空的(可能通过检查self.crawler.engine.slot.inprogress 和`self.crawler.engine.slot.scheduler)后,您才可以创建一个不同的域以将下一个域添加到列表中.

    祝你好运!

    【讨论】:

    • 抱歉,现在才有时间和资源来阅读您的答案。哇,给了我很多思考和考虑,非常感谢,会尝试并更新。
    • 试过了,分离请求并让scrapy将每个站点作为单独的线程处理,效果很好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-22
    • 1970-01-01
    • 2016-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多