使用一个Scrapy爬虫来爬取多个网站

12

我需要创建一个用户可配置的网络爬虫,考虑使用Scrapy。但是,我不能在代码中硬编码域和允许的URL正则表达式:这将在GUI中进行配置。

如何尽可能简单地使用Scrapy创建一个可以动态配置域和允许的URL正则表达式的爬虫或一组爬虫?例如,我将配置写入文件,然后爬虫以某种方式读取它。


2
@Christian Davén:这些回答对您的问题不可接受吗? - dangra
4个回答

10

警告:这个答案是针对Scrapy v0.7版本的,自那时起,蜘蛛管理API发生了很大变化。

覆盖默认的SpiderManager类,从数据库或其他地方加载您的自定义规则,并使用自己的规则/正则表达式和域名实例化自定义蜘蛛。

在mybot/settings.py文件中:

SPIDER_MANAGER_CLASS = 'mybot.spidermanager.MySpiderManager'

在我的机器人/蜘蛛管理器.py文件中:

from mybot.spider import MyParametrizedSpider

class MySpiderManager(object):
    loaded = True

    def fromdomain(self, name):
        start_urls, extra_domain_names, regexes = self._get_spider_info(name)
        return MyParametrizedSpider(name, start_urls, extra_domain_names, regexes)

    def close_spider(self, spider):
        # Put here code you want to run before spiders is closed
        pass

    def _get_spider_info(self, name):
        # query your backend (maybe a sqldb) using `name` as primary key, 
        # and return start_urls, extra_domains and regexes
        ...
        return (start_urls, extra_domains, regexes)

现在是你自定义的蜘蛛类,在 mybot/spider.py 文件中:

from scrapy.spider import BaseSpider

class MyParametrizedSpider(BaseSpider):

    def __init__(self, name, start_urls, extra_domain_names, regexes):
        self.domain_name = name
        self.start_urls = start_urls
        self.extra_domain_names = extra_domain_names
        self.regexes = regexes

     def parse(self, response):
         ...

注:

  • 如果您想利用其规则系统,也可以扩展CrawlSpider
  • 要运行一个蜘蛛,请使用:./scrapy-ctl.py crawl <name>,其中name被传递给SpiderManager.fromdomain,并且是从后端系统检索更多蜘蛛信息的关键
  • 由于解决方案覆盖了默认的SpiderManager,编写经典蜘蛛(每个SPIDER一个Python模块)无法正常工作,但是我认为这对您不是问题。有关默认蜘蛛管理器的更多信息,请访问TwistedPluginSpiderManager

与Alex Martelli的方法不同之处在于,蜘蛛是按需实例化的,而不是预先实例化所有蜘蛛仅使用一个。这种方法可以减少后端负载和scrapy机器人进程的内存占用。 - dangra
我如何为自定义爬虫指定设置(ITEM_PIPELINES、USER_AGENT等)?另外,您提到了 ./scrapy-ctl.py crawl <name>scrapy-ctl.py 是什么? - warvariuc

4
你需要做的是动态创建蜘蛛类,将你最喜欢的通用蜘蛛类作为父类,例如scrapy提供的CrawlSpider子类加上你的rules,或者XmlFeedSpider等,并添加domain_namestart_urls和可能的extra_domain_names(以及/或start_requests()等),从你的GUI(或配置文件等)中获取或推断出它们。

Python使得动态创建类对象变得容易;一个非常简单的例子可能是:

from scrapy import spider

def makespider(domain_name, start_urls,
               basecls=spider.BaseSpider):
  return type(domain_name + 'Spider',
              (basecls,),
              {'domain_name': domain_name,
               'start_urls': start_urls})

allspiders = []
for domain, urls in listofdomainurlpairs:
  allspiders.append(makespider(domain, urls))

这提供了一组非常基本的爬虫类列表 -- 在实例化它们之前,您可能需要添加parse方法。按照自己的口味进行调整...;-)。


这份代码应该放在哪里?我试图动态地将爬虫类添加到我的爬虫模块中,但是 scrapy 没有捕捉到它们。 - user250145

3

关于domo,我想自夸一下!在你的项目中,需要按照示例来实例化爬虫。

同时,你需要在运行时将爬虫配置为可配置项,只需将配置传递给爬虫,并在配置更改时覆盖设置。


0

现在配置Scrapy以实现这些目的非常容易:

  1. 关于要访问的第一个URL,您可以将其作为Spider调用中的属性传递,并使用-a,并使用start_requests函数设置如何启动Spider。

  2. 您不需要为Spider设置allowed_domains变量。如果不包括该类变量,则Spider将能够允许每个域。

最终应该像下面这样:

class MySpider(Spider):

    name = "myspider"

    def start_requests(self):
        yield Request(self.start_url, callback=self.parse)


    def parse(self, response):
        ...

你应该这样调用它:

scrapy crawl myspider -a start_url="http://example.com"

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