使用Scrapy逐个爬取域名

3

我有一些存储在数据库中的网站需要按顺序爬取,即:

  1. 将域名添加到允许的域名列表中(应为空)。
  2. 将此域名添加到请求中(将 http:// 添加到其中可完美运行)。
  3. 根据允许的域名爬取请求。
  4. 进入下一个域,仅将其添加到允许的域名列表中。这是唯一存在的域名,因此不会发生交叉。注意:这个问题 没有帮助我,但也许我漏掉了什么...
  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,它直接工作,因此不需要对其进行编辑。但是如果我需要在其中添加内容,请告诉我,因为这可能是缺失的链接。当前代码会将一个字典添加到一个项目中,然后将该项目放入数据库中。

因此,综上所述:我需要添加什么来使其每次爬取请求时过滤域名?

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

1个回答

4
我的建议是采用完全不同的方法。在数据库中创建一个需要爬取的域名队列(根据您想要的任何标准),然后选择一个域名进行爬取,并使用相关域名初始化蜘蛛中的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正则表达式(根本没有与请求/响应对象相关联)。如果在爬虫开始爬行之前将二十个请求排队到不同的域中(每次更改allowed_domains列表),它将使用最近编译的正则表达式(即最后排队的域的allowed_domains)。

为了克服这个问题,您需要为一个域名爬取所有请求,编写自己的带插槽的OffsiteMiddleware,并让它处理所有过滤。或者,您可以创建一个延迟操作,只有当队列中的所有请求都完成,并且下载器中的所有插槽都为空时才添加下一个域名到列表中(通常通过检查self.crawler.engine.slot.inprogressself.crawler.engine.slot.scheduler实现)。

祝你好运!


很抱歉,直到现在才有时间和资源阅读你的回答。 哇,这让我有很多事情需要思考和考虑,非常感谢你,我会尝试并更新。 - Elvira Gandelman
尝试过了,它很好地分离了请求,并让Scrapy将每个站点作为单独的线程处理。 - Elvira Gandelman

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