scrapy日志系统

前言

如何修改scrapy的日志?我想大部分人想到的都是修改配置文件的几个参数。参数如下:

  • LOG_ENABLED: 是否启用log
  • LOG_ENCODING: log编码,仅针对保存文件时
  • LOG_FILE: 日志文件名
  • LOG_FORMAT:日志格式化字符串
  • LOG_DATEFORMAT:日志时间格式化字符串
  • LOG_LEVEL:日志级别
  • LOG_STDOUT:是否重定向stdout
  • LOG_SHORT_NAMES:过滤掉大部分日志,没啥用
  • LOG_FORMATTER:格式化日志的类,默认为scrapy.logformatter.LogFormatter

但是当你设置了LOG_FILE时,日志虽然保存到了文件,但在终端上不显示了。这其实也很合理,因为你可以实时打印文件将日志打印在终端,比如tail -f 日志文件名。不过这是在Linux上可以这样操作,在Windows的时候,我们更倾向于界面的显示,但是又不能不保存日志待以后检查问题。就不能既打印在终端又保存在文件吗?

scrapy同时保存日志到终端和文件

scrapy的日志其实使用的就是python自带的logging模块,先看看使用logging如何打印终端和保存在文件

import os
import sys
import time
import logging


class Logger:
    @staticmethod
    def get_logger(name=None):
        logger = logging.getLogger(f'wechat.{name}')
        logger.setLevel(level=logging.INFO)
        if not logger.handlers:
            stream_handler = logging.StreamHandler(sys.stdout)
            stream_handler.setLevel(level=logging.INFO)
            formatter = logging.Formatter('%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(message)s')
            stream_handler.setFormatter(formatter)
            logger.addHandler(stream_handler)

            # FileHandler
            os.makedirs('log', exist_ok=True)
            today = time.strftime('%Y-%m-%d',time.localtime())
            file_handler = logging.FileHandler(f'log/{today}.log', encoding='utf-8')
            file_handler.setLevel(level=logging.INFO)
            formatter = logging.Formatter('%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(message)s')
            file_handler.setFormatter(formatter)
            logger.addHandler(file_handler)

        return logger

if not logger.handlers:这一句的判断是因为,logging会重复打印多个日志。

既然scrapy使用的也是logging模块,那我能不能给他加一个打印在屏幕的handle,先看看spider中logger属性的代码: spider源代码

@property
def logger(self):
    logger = logging.getLogger(self.name)
    return logging.LoggerAdapter(logger, {'spider': self})

def log(self, message, level=logging.DEBUG, **kw):
    self.logger.log(level, message, **kw)

那我能不能重写logger方法,来将日志也打印在终端,

@property
def logger(self):
        logger = logging.getLogger(self.name)
        stream_handler = logging.StreamHandler()
        stream_handler.setLevel(level=logging.INFO)
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(message)s')
        stream_handler.setFormatter(formatter)
        logger.addHandler(stream_handler)
        return logging.LoggerAdapter(logger, {'spider': self})

发现还是没打印在终端,为啥呢?因为这个logger只控制你写的spider里面的日志,如果你spider没有使用self.logger就不会打印。我们在spider加一个打印信息就会发现终端有输出结果了。其实我们一般也只关心spider里面的日志内容,所以这个方法足够满足一般情况了。

修改源代码

如何将scrapy所有的日志内容都打印在终端,要想做到这个,肯定是需要修改scrapy的源代码了。我们先看看scrapy是在哪里操作日志的,可以打开scrapy的源码全局搜索LOG_FILE或者FileHandler, 可以看到scrapy控制logging的代码都放在scrapy.utils.log这个文件里面, 也可以在官网查看:官网源代码,处理handle的主要是这两个方法:


def install_scrapy_root_handler(settings):
    global _scrapy_root_handler

    if (_scrapy_root_handler is not None
            and _scrapy_root_handler in logging.root.handlers):
        logging.root.removeHandler(_scrapy_root_handler)
    logging.root.setLevel(logging.NOTSET)
    _scrapy_root_handler = _get_handler(settings)
    logging.root.addHandler(_scrapy_root_handler)

def _get_handler(settings):
    """ Return a log handler object according to settings """
    filename = settings.get('LOG_FILE')
    if filename:
        encoding = settings.get('LOG_ENCODING')
        handler = logging.FileHandler(filename, encoding=encoding)
    elif settings.getbool('LOG_ENABLED'):
        handler = logging.StreamHandler()
    else:
        handler = logging.NullHandler()

    formatter = logging.Formatter(
        fmt=settings.get('LOG_FORMAT'),
        datefmt=settings.get('LOG_DATEFORMAT')
    )
    handler.setFormatter(formatter)
    handler.setLevel(settings.get('LOG_LEVEL'))
    if settings.getbool('LOG_SHORT_NAMES'):
        handler.addFilter(TopLevelFormatter(['scrapy']))
    return handler

_get_handler方法根据settings文件中的配置参数来生成相应的handle,install_scrapy_root_handler方法用来添加handle到logger。只需要稍微修改这两个函数就可以实现功能,需要直接修改python的包路径里面的代码,修改前记得备份原文件:

def install_scrapy_root_handler(settings):
    global _scrapy_root_handler

    if (_scrapy_root_handler is not None
            and _scrapy_root_handler in logging.root.handlers):
        logging.root.removeHandler(_scrapy_root_handler)
    logging.root.setLevel(logging.NOTSET)
    handles = _get_handler(settings)
    _scrapy_root_handler = handles[0]
    for handler in handles:
        logging.root.addHandler(handler)

def _get_handler(settings):
    """ Return a log handler object according to settings """
    filename = settings.get('LOG_FILE')
    handlers = []
    if filename:
        encoding = settings.get('LOG_ENCODING')
        file_handler = logging.FileHandler(filename, encoding=encoding)
        stream_hander = logging.StreamHandler()
        handlers.append(file_handler)
        handlers.append(stream_hander)
    elif settings.getbool('LOG_ENABLED'):
        stream_hander = logging.StreamHandler()
        handlers.append(stream_hander)
    else:
        null_handler = logging.NullHandler()
        handlers.append(null_handler)
    for handler in handlers:
        formatter = logging.Formatter(
            fmt=settings.get('LOG_FORMAT'),
            datefmt=settings.get('LOG_DATEFORMAT')
        )
        handler.setFormatter(formatter)
        handler.setLevel(settings.get('LOG_LEVEL'))
        if settings.getbool('LOG_SHORT_NAMES'):
            handler.addFilter(TopLevelFormatter(['scrapy']))
    return handlers
不修改源代码

修改源码虽然可以一劳永逸,但是一般是不推荐的,怎么在不修改源码的基础上做改动呢?

其实可以从上面的代码看出来,log文件的只是操作了logging.root,那么我们直接在自己写的spider里操作logging.root行不行呢?

直接把这段代码加在爬虫文件的开头:

import logging

stream_handler = logging.StreamHandler()
stream_handler.setLevel(level=logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(message)s')
stream_handler.setFormatter(formatter)
logging.root.addHandler(stream_handler)

这样同样可以达到效果,当然实际使用的话,需要读取配置文件中LOG_FILE的值,如果存在再添加handle,如果不存在就不添加了。另外如果有多个爬虫,还需要判断一下logging.root里是否已经存在stream_handler,不存在的话再添加。

其实还有一种方法。从CrawlerProcess着手,因为在scrapy源码中全局搜索scrapy.utils.log发现只有crawler.py这个文件导入了这个类里面的这两个方法,CrawlerProcess.crawl这个方法传入的第一个参数类型是crawler.Crawler这个类,而在这个类中同样处理了日志的handle。所以我们重写这个类,然后传递给crawler_process.crawl就可以实现同样的效果, 不过这个会更麻烦一点,还是推荐前两种吧。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python电影爬虫系统是一种利用Python语言编写的系统,用于从互联网上自动获取电影相关信息的工具。该系统的设计目的是为了满足用户对电影资源的需求,实现自动化的爬取、整理和展示。 首先,该系统需要通过Python进行网页爬取,可以使用第三方库如Requests、BeautifulSoup等来获取电影相关网页的HTML内容。通过解析HTML,系统可以提取出电影的标题、照片、导演、演员、剧情介绍、上映时间等信息。这些信息可以作为电影数据库的一部分,用于用户的查询和展示。 其次,系统需要实现数据的存储和管理。可以使用关系型数据库如MySQL或非关系型数据库如MongoDB来存储电影数据。通过建立合适的数据模型,可以将电影信息存储在数据库中,并使用SQL或NoSQL语言进行数据的增删改查操作。这样,在用户需要查询电影信息时,系统可以从数据库中快速获取相应数据。 另外,系统还应提供用户友好的界面和交互功能。可以使用Python的Web框架如Django或Flask构建一个用户界面,使用户可以通过网页来搜索和浏览电影信息。用户可以通过关键词查询电影,系统会根据关键词在数据库中进行模糊匹配,并返回符合条件的电影列表。用户还可以通过点击电影的链接,查看电影的详细信息和相关推荐。 最后,为了保证系统的稳定性和可拓展性,可以添加爬虫调度和错误处理功能。可以使用Python的第三方库如Scrapy来实现爬虫的调度,设置爬取频率和优先级,避免对目标网站的过度请求。同时,系统应该具备一定的异常处理机制,当爬虫遇到错误或异常时,能够及时记录日志并进行相应的处理,保证系统的正常运行。 综上所述,Python电影爬虫系统通过Python语言实现了电影信息的自动获取、存储和展示。该系统能够满足用户的电影需求,提供了便捷的查询和浏览功能,并具备稳定性和可拓展性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值