日志
注意
scrapy.log 及其函数已被弃用,取而代之的是显式调用 Python 标准日志模块。继续阅读以了解更多关于新日志系统的信息。
Scrapy 使用 logging 进行事件日志记录。我们将提供一些简单的示例助您入门,但对于更高级的用例,强烈建议您仔细阅读其文档。
日志功能开箱即用,并且可以通过 日志设置 中列出的 Scrapy 设置在一定程度上进行配置。
Scrapy 在运行命令时会调用 scrapy.utils.log.configure_logging() 来设置一些合理的默认值并处理 日志设置 中的配置。因此,如果您像 从脚本运行 Scrapy 中所述从脚本运行 Scrapy,建议手动调用它。
日志级别
Python 内置的日志模块定义了 5 个不同的级别来指示给定日志消息的严重性。以下是标准级别,按严重性降序排列:
logging.CRITICAL- 用于严重错误(最高严重性)logging.ERROR- 用于常规错误logging.WARNING- 用于警告消息logging.INFO- 用于信息性消息logging.DEBUG- 用于调试消息(最低严重性)
如何记录消息
以下是一个使用 logging.WARNING 级别记录消息的快速示例:
import logging
logging.warning("This is a warning")
有用于在任何标准 5 个级别上发出日志消息的快捷方式,还有一个通用的 logging.log 方法,它接受一个给定级别作为参数。如果需要,上一个示例可以重写为:
import logging
logging.log(logging.WARNING, "This is a warning")
最重要的是,您可以创建不同的“记录器”(loggers)来封装消息。(例如,常见的做法是为每个模块创建不同的记录器)。这些记录器可以独立配置,并允许分层结构。
前面的示例在幕后使用了根记录器(root logger),它是一个顶级记录器,所有消息都会传播到此处(除非另有说明)。使用 logging 助手只是显式获取根记录器的一个快捷方式,因此这也等同于上一个代码片段:
import logging
logger = logging.getLogger()
logger.warning("This is a warning")
您只需使用 logging.getLogger 函数获取其名称即可使用不同的记录器:
import logging
logger = logging.getLogger("mycustomlogger")
logger.warning("This is a warning")
最后,您可以使用 __name__ 变量为正在处理的任何模块确保有一个自定义记录器,该变量会填充当前模块的路径:
import logging
logger = logging.getLogger(__name__)
logger.warning("This is a warning")
从爬虫记录日志
Scrapy 在每个 Spider 实例中提供了一个 logger,可以像这样访问和使用:
import scrapy
class MySpider(scrapy.Spider):
name = "myspider"
start_urls = ["https://scrapy.net.cn"]
def parse(self, response):
self.logger.info("Parse function called on %s", response.url)
该记录器是使用 Spider 的名称创建的,但您可以使用任何您想要的自定义 Python 记录器。例如:
import logging
import scrapy
logger = logging.getLogger("mycustomlogger")
class MySpider(scrapy.Spider):
name = "myspider"
start_urls = ["https://scrapy.net.cn"]
def parse(self, response):
logger.info("Parse function called on %s", response.url)
日志配置
记录器本身不管理通过它们发送的消息如何显示。为此,可以将不同的“处理器”(handlers)附加到任何记录器实例,它们会将这些消息重定向到适当的目的地,例如标准输出、文件、电子邮件等。
默认情况下,Scrapy 会根据以下设置,为根记录器设置和配置一个处理器。
日志设置
这些设置可用于配置日志记录:
前两项设置定义了日志消息的目标。如果设置了 LOG_FILE,则通过根记录器发送的消息将重定向到以 LOG_ENCODING 编码命名的 LOG_FILE 文件中。如果未设置且 LOG_ENABLED 为 True,则日志消息将显示在标准错误输出中。如果设置了 LOG_FILE 且 LOG_FILE_APPEND 为 False,则文件将被覆盖(丢弃之前运行的任何输出)。最后,如果 LOG_ENABLED 为 False,将不会有任何可见的日志输出。
LOG_LEVEL 决定了要显示的最低严重性级别,低于此严重性的消息将被过滤掉。其范围涵盖了 日志级别 中列出的所有可能级别。
LOG_FORMAT 和 LOG_DATEFORMAT 指定了用作所有消息布局的格式字符串。这些字符串可以包含 logging 的 logrecord 属性文档 和 datetime 的 strftime 和 strptime 指令 中列出的任何占位符。
如果设置了 LOG_SHORT_NAMES,则日志将不会显示打印日志的 Scrapy 组件。默认情况下它未设置,因此日志会包含负责该日志输出的 Scrapy 组件。
轮转日志文件
Scrapy 的 LOG_FILE 设置将日志写入单个文件。它不会自动轮转日志文件,但当您从脚本运行 Scrapy 时,可以使用 Python 标准的 logging.handlers 模块。
例如,要每天轮转日志文件:
import logging
from logging.handlers import TimedRotatingFileHandler
from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings
from myproject.spiders.myspider import MySpider
settings = get_project_settings()
process = CrawlerProcess(settings, install_root_handler=False)
handler = TimedRotatingFileHandler(
"scrapy.log",
when="midnight",
backupCount=7,
encoding=settings.get("LOG_ENCODING"),
)
handler.setFormatter(
logging.Formatter(settings.get("LOG_FORMAT"), settings.get("LOG_DATEFORMAT"))
)
root_logger = logging.getLogger()
root_logger.setLevel(settings.get("LOG_LEVEL"))
root_logger.addHandler(handler)
process.crawl(MySpider)
process.start()
命令行选项
所有命令都可用的命令行参数,您可以使用它们来覆盖一些与日志记录相关的 Scrapy 设置。
--logfile 文件覆盖
LOG_FILE
--loglevel/-L 级别覆盖
LOG_LEVEL
--nolog将
LOG_ENABLED设置为False
另请参阅
- 模块
logging.handlers 可用处理器的更多文档
自定义日志格式
通过扩展 LogFormatter 类并让 LOG_FORMATTER 指向您的新类,可以为不同的操作设置自定义日志格式。
- class scrapy.logformatter.LogFormatter[source]
用于为不同操作生成日志消息的类。
所有方法都必须返回一个字典,其中列出参数
level、msg和args,这些参数将用于在调用logging.log时构建日志消息。方法输出的字典键
level是该操作的日志级别,您可以使用 Python logging 库 中的级别:logging.DEBUG、logging.INFO、logging.WARNING、logging.ERROR和logging.CRITICAL。msg应该是一个可以包含不同格式占位符的字符串。这个字符串,通过提供的args格式化后,将成为该操作的完整消息。args应该是一个包含msg格式占位符的元组或字典。最终的日志消息通过msg % args计算得出。
用户可以定义自己的
LogFormatter类,如果他们想自定义每个操作的日志记录方式,或者想完全省略某个操作的日志。为了省略某个操作的日志记录,该方法必须返回None。以下是一个示例,说明如何创建一个自定义日志格式化器,以便在某个 Item 从管道中被丢弃时降低日志消息的严重性级别:
class PoliteLogFormatter(logformatter.LogFormatter): def dropped(self, item, exception, response, spider): return { "level": logging.INFO, # lowering the level from logging.WARNING "msg": "Dropped: %(exception)s" + os.linesep + "%(item)s", "args": { "exception": exception, "item": item, }, }
- crawled(request: Request, response: Response, spider: Spider) LogFormatterResult[source]
当爬虫找到网页时记录消息。
- download_error(failure: Failure, request: Request, spider: Spider, errmsg: str | None = None) LogFormatterResult[source]
记录来自爬虫的下载错误消息(通常来自引擎)。
- dropped(item: Any, exception: BaseException, response: Response | Failure | None, spider: Spider) LogFormatterResult[source]
当一个 Item 在通过 Item Pipeline 时被丢弃时记录消息。
- item_error(item: Any, exception: BaseException, response: Response | Failure | None, spider: Spider) LogFormatterResult[source]
当一个 Item 在通过 Item Pipeline 时导致错误时记录消息。
高级自定义
由于 Scrapy 使用标准库 logging 模块,您可以使用 stdlib logging 的所有功能来自定义日志记录。
例如,假设您正在爬取一个返回许多 HTTP 404 和 500 响应的网站,并且您想隐藏所有此类消息:
2016-12-16 22:00:06 [scrapy.spidermiddlewares.httperror] INFO: Ignoring
response <500 https://quotes.toscrape.com/page/1-34/>: HTTP status code
is not handled or not allowed
首先要注意的是记录器名称——它在方括号中:[scrapy.spidermiddlewares.httperror]。如果您只看到 [scrapy],则可能是 LOG_SHORT_NAMES 设置为 True;将其设置为 False 并重新运行爬取。
接下来,我们可以看到消息具有 INFO 级别。要隐藏它,我们应该将 scrapy.spidermiddlewares.httperror 的日志级别设置为高于 INFO;INFO 之后的下一个级别是 WARNING。这可以在例如爬虫的 __init__ 方法中完成:
import logging
import scrapy
class MySpider(scrapy.Spider):
# ...
def __init__(self, *args, **kwargs):
logger = logging.getLogger("scrapy.spidermiddlewares.httperror")
logger.setLevel(logging.WARNING)
super().__init__(*args, **kwargs)
如果您再次运行此爬虫,则来自 scrapy.spidermiddlewares.httperror 记录器的 INFO 消息将消失。
您还可以通过 LogRecord 数据过滤日志记录。例如,您可以使用子字符串或正则表达式按消息内容过滤日志记录。创建一个 logging.Filter 子类,并为其配备一个正则表达式模式来过滤掉不需要的消息:
import logging
import re
class ContentFilter(logging.Filter):
def filter(self, record):
match = re.search(r"\d{3} [Ee]rror, retrying", record.message)
if match:
return False
项目级别的过滤器可以附加到 Scrapy 创建的根处理器上,这是一种方便的方式来过滤项目中不同部分(中间件、爬虫等)的所有记录器。
import logging
import scrapy
class MySpider(scrapy.Spider):
# ...
def __init__(self, *args, **kwargs):
for handler in logging.root.handlers:
handler.addFilter(ContentFilter())
另外,您可以选择特定的记录器并将其隐藏,而不会影响其他记录器:
import logging
import scrapy
class MySpider(scrapy.Spider):
# ...
def __init__(self, *args, **kwargs):
logger = logging.getLogger("my_logger")
logger.addFilter(ContentFilter())
scrapy.utils.log 模块
- scrapy.utils.log.configure_logging(settings: Settings | dict[str, Any] | None = None, install_root_handler: bool = True) None[source]
初始化 Scrapy 的日志记录默认值。
- 参数:
此函数执行以下操作:
通过 Python 标准日志记录路由警告和 Twisted 日志
分别为 Scrapy 和 Twisted 记录器分配 DEBUG 和 ERROR 级别
如果 LOG_STDOUT 设置为 True,则将 stdout 路由到日志
当
install_root_handler为 True(默认值)时,此函数还会根据给定设置(参见 日志设置)为根记录器创建一个处理器。您可以使用settings参数覆盖默认选项。当settings为空或 None 时,将使用默认值。使用 Scrapy 命令或
CrawlerProcess时,configure_logging会自动调用,但当使用CrawlerRunner运行自定义脚本时,需要显式调用。在这种情况下,不强制使用它,但建议使用。运行自定义脚本时的另一个选项是手动配置日志记录。为此,您可以使用
logging.basicConfig()来设置一个基本的根处理器。请注意,
CrawlerProcess会自动调用configure_logging,因此建议仅将logging.basicConfig()与CrawlerRunner一起使用。这是一个如何将
INFO或更高级别的消息重定向到文件的示例:import logging logging.basicConfig( filename="log.txt", format="%(levelname)s: %(message)s", level=logging.INFO )
有关以这种方式使用 Scrapy 的更多详细信息,请参阅 从脚本运行 Scrapy。