使用CrawlerProcess.crawl()从脚本向spider传递custom_settings的scrapy方法

8

我正在尝试通过脚本编程调用一个爬虫。我无法通过CrawlerProcess的构造函数重写设置。让我举个例子,使用官方scrapy网站上抓取引号的默认爬虫(最后一个代码片段位于official scrapy quotes example spider)。

class QuotesSpider(Spider):

    name = "quotes"

    def __init__(self, somestring, *args, **kwargs):
        super(QuotesSpider, self).__init__(*args, **kwargs)
        self.somestring = somestring
        self.custom_settings = kwargs


    def start_requests(self):
        urls = [
            'http://quotes.toscrape.com/page/1/',
            'http://quotes.toscrape.com/page/2/',
        ]
        for url in urls:
            yield Request(url=url, callback=self.parse)

    def parse(self, response):
        for quote in response.css('div.quote'):
            yield {
                'text': quote.css('span.text::text').extract_first(),
                'author': quote.css('small.author::text').extract_first(),
                'tags': quote.css('div.tags a.tag::text').extract(),
            }

这是我尝试运行报价爬虫的脚本。
from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings
from scrapy.settings import Settings

    def main():

    proc = CrawlerProcess(get_project_settings())

    custom_settings_spider = \
    {
        'FEED_URI': 'quotes.csv',
        'LOG_FILE': 'quotes.log'
    }
    proc.crawl('quotes', 'dummyinput', **custom_settings_spider)
    proc.start()
4个回答

11

Scrapy设置有点像Python字典。因此,在将其传递给 CrawlerProcess 之前,您可以更新设置对象:

from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings
from scrapy.settings import Settings

def main():

    s = get_project_settings()
    s.update({
        'FEED_URI': 'quotes.csv',
        'LOG_FILE': 'quotes.log'
    })
    proc = CrawlerProcess(s)

    proc.crawl('quotes', 'dummyinput', **custom_settings_spider)
    proc.start()

编辑以下OP的评论:
这是一个使用 CrawlerRunner 的变化,每次爬行都使用一个新的 CrawlerRunner 并在每次迭代时重新配置日志记录以每次写入不同的文件:
import logging
from twisted.internet import reactor, defer

import scrapy
from scrapy.crawler import CrawlerRunner
from scrapy.utils.log import configure_logging, _get_handler
from scrapy.utils.project import get_project_settings


class QuotesSpider(scrapy.Spider):
    name = "quotes"

    def start_requests(self):
        page = getattr(self, 'page', 1)
        yield scrapy.Request('http://quotes.toscrape.com/page/{}/'.format(page),
                             self.parse)

    def parse(self, response):
        for quote in response.css('div.quote'):
            yield {
                'text': quote.css('span.text::text').extract_first(),
                'author': quote.css('small.author::text').extract_first(),
                'tags': quote.css('div.tags a.tag::text').extract(),
            }


@defer.inlineCallbacks
def crawl():
    s = get_project_settings()
    for i in range(1, 4):
        s.update({
            'FEED_URI': 'quotes%03d.csv' % i,
            'LOG_FILE': 'quotes%03d.log' % i
        })

        # manually configure logging for LOG_FILE
        configure_logging(settings=s, install_root_handler=False)
        logging.root.setLevel(logging.NOTSET)
        handler = _get_handler(s)
        logging.root.addHandler(handler)

        runner = CrawlerRunner(s)
        yield runner.crawl(QuotesSpider, page=i)

        # reset root handler
        logging.root.removeHandler(handler)
    reactor.stop()

crawl()
reactor.run() # the script will block here until the last crawl call is finished

对于我的使用情况,我需要在每次运行蜘蛛时使用proc.crawl()传递一个.csv文件。我想要有1个爬虫进程(具有公共设置),但是使用不同的名称连续调用crawl以获取日志和csv输出。我能否使用Scrapy实现这一点? - hAcKnRoCk
@hAcKnRoCk,当调用CrawlerProcess并更新设置时,您可以使用for循环,而不是覆盖custom_settings - eLRuLL
@hAcKnRoCk,你看过在同一进程中运行多个爬虫的最后一个示例吗?即使用CrawlerRunner按顺序运行爬虫? - paul trmbrth
@eLRuLL:是的,我已经尝试使用for循环了。代码在http://pastebin.com/RTnUWntQ。在第二次迭代期间,我收到了“twisted.internet.error.ReactorNotRestartable”错误。 - hAcKnRoCk
@paultrmbrth 是的,我确实看到了那个例子。但是我不确定它是否适用于我的用例。问题仍然存在。我无法在每次运行时都得到一个.csv和一个.log文件。 - hAcKnRoCk
显示剩余2条评论

1

我认为当作脚本调用Spider类时,无法覆盖custom_settings变量,基本上是因为设置在实例化Spider之前加载。

现在,我真的看不出来特别更改custom_settings变量的意义,因为它只是覆盖默认设置的一种方式,而这正是CrawlerProcess所提供的,它可以按预期工作:

import scrapy
from scrapy.crawler import CrawlerProcess


class MySpider(scrapy.Spider):
    name = 'simple'
    start_urls = ['http://httpbin.org/headers']

    def parse(self, response):
        for k, v in self.settings.items():
            print('{}: {}'.format(k, v))
        yield {
            'headers': response.body
        }

process = CrawlerProcess({
    'USER_AGENT': 'my custom user anget',
    'ANYKEY': 'any value',
})

process.crawl(MySpider)
process.start()

能够覆盖custom_settings的重点在于此。我想要能够执行'crawl('myspider', list1_urlstoscrape, 'list1output.csv', 'list1.log' )',然后再执行'crawl('myspider', list2_urlstoscrape, 'list2output.csv', 'list2.log')。因此,我必须创建多个CrawlerProcess实例,但由于twister反应器问题,这是不可能的。 - hAcKnRoCk
你可以修改你的爬虫代码,使其能够一次接收多个列表,然后逐个进行处理。 - eLRuLL
是的,但问题仍然存在。问题不在于传递要被抓取的输入列表,而在于说明您希望为这些列表中的每个列表输出什么(也就是说,对于同一爬虫的每次爬行)。 - hAcKnRoCk

0

0

看起来你想为每个爬虫设置自定义日志。你需要像这样激活日志记录:

from scrapy.utils.log import configure_logging

class MySpider(scrapy.Spider):
    #ommited
    def __init__(self):
        configure_logging({'LOG_FILE' : "logs/mylog.log"})

这实际上在我遇到一种非常独特的情况时帮了我大忙,那就是我的爬虫调用一个API并且有多个可以与该爬虫一起使用的“账户”。谢谢! - CaffeinatedMike

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