# 21.16. `nntplib` - NNTP协议客户端

源代码： Lib / nntplib.py

此模块定义实现网络新闻传输协议的客户端侧的`NNTP`类。它可以用于实现新闻阅读器或海报，或自动新闻处理器。It is compatible with RFC 3977 as well as the older RFC 977 and RFC 2980.

这里有两个小例子，说明如何使用它。列出一些关于新闻组的统计数据并打印最后10篇文章的主题：

    >>> s = nntplib.NNTP('news.gmane.org')
    >>> resp, count, first, last, name = s.group('gmane.comp.python.committers')
    >>> print('Group', name, 'has', count, 'articles, range', first, 'to', last)
    Group gmane.comp.python.committers has 1096 articles, range 1 to 1096
    >>> resp, overviews = s.over((last - 9, last))
    >>> for id, over in overviews:
    ...     print(id, nntplib.decode_header(over['subject']))
    ...
    1087 Re: Commit privileges for Łukasz Langa
    1088 Re: 3.2 alpha 2 freeze
    1089 Re: 3.2 alpha 2 freeze
    1090 Re: Commit privileges for Łukasz Langa
    1091 Re: Commit privileges for Łukasz Langa
    1092 Updated ssh key
    1093 Re: Updated ssh key
    1094 Re: Updated ssh key
    1095 Hello fellow committers!
    1096 Re: Hello fellow committers!
    >>> s.quit()
    '205 Bye!'

要从二进制文件发布文章（假设文章具有有效的标题，并且您有权在特定新闻组上发帖）：

    >>> s = nntplib.NNTP('news.gmane.org')
    >>> f = open('article.txt', 'rb')
    >>> s.post(f)
    '240 Article posted successfully.'
    >>> s.quit()
    '205 Bye!'

模块本身定义了以下类：

class `nntplib.``NNTP`(host, port=119, user=None, password=None, readermode=None, usenetrc=False[, timeout])

  返回一个新的`NNTP`对象，表示与在主机主机上运行的NNTP服务器的连接，在端口端口处侦听。可以为套接字连接指定可选的超时。如果提供了可选的用户和密码 t>，或`/.netrc`和可选标志usenetrc 为真，`AUTHINFO USER`和`AUTHINFO PASS / t8>命令用于标识和验证服务器的用户。`如果可选标志readermode为真，则在执行认证之前发送`模式 读取器` 如果要连接到本地计算机上的NNTP服务器并打算调用读取程序特定的命令（例如`group`），有时需要使用读取模式。如果遇到意外的`NNTPPermanentError`，您可能需要设置readermode。`NNTP`类支持`with`语句无条件地使用`OSError`异常，并在完成后关闭NNTP连接。：

      >>> from nntplib import NNTP
      >>> with NNTP('news.gmane.org') as n:
      ...     n.group('gmane.comp.python.committers')
      ...
      ('211 1755 1 1755 gmane.comp.python.committers', 1755, 1, 1755, 'gmane.comp.python.committers')
      >>>

  在版本3.2中更改： usenetrc现在为`False`。

  在版本3.3中已更改：添加了对`with`语句的支持。

class `nntplib.``NNTP_SSL`(host, port=563, user=None, password=None, ssl_context=None, readermode=None, usenetrc=False[, timeout])

  返回一个新的`NNTP_SSL`对象，表示与在主机主机上运行的NNTP服务器的加密连接，在端口端口处侦听。`NNTP_SSL`对象具有与`NNTP`对象相同的方法。如果省略端口，则使用端口563（NNTPS）。ssl_context也是可选的，并且是`SSLContext`对象。有关最佳做法，请参阅Security considerations。所有其他参数的行为与`NNTP`相同。

  注意，对于如下所述的STARTTLS， RFC 4642不建议使用SSL-on-563。但是，一些服务器只支持前者。

  版本3.2中的新功能。

  在版本3.4中更改：此类现在支持使用`ssl.SSLContext.check_hostname`和服务器名称指示（请参阅`ssl.HAS_SNI`）。

exception `nntplib.``NNTPError`

  派生自标准异常`Exception`，这是由`nntplib`模块引发的所有异常的基类。此类的实例具有以下属性：

  `response`

    服务器的响应（如果可用）作为`str`对象。

exception `nntplib.``NNTPReplyError`

  从服务器收到意外的响应时引发异常。

exception `nntplib.``NNTPTemporaryError`

  接收范围为400-499的响应代码时引发异常。

exception `nntplib.``NNTPPermanentError`

  当接收到范围500-599中的响应代码时引发异常。

exception `nntplib.``NNTPProtocolError`

  当从服务器接收到的回复不是以1-5范围内的数字开头时引发的异常。

exception `nntplib.``NNTPDataError`

  在响应数据中存在一些错误时引发异常。

## 21.16.1. NNTP Objects

连接时，`NNTP`和`NNTP_SSL`对象支持以下方法和属性。

### 21.16.1.1. Attributes

`NNTP.``nntp_version`

  表示服务器支持的NNTP协议版本的整数。在实践中，对于为其他人广告 RFC 3977符合性和`1`的服务器，这应该是`2`。

  版本3.2中的新功能。

`NNTP.``nntp_implementation`

  描述NNTP服务器的软件名称和版本的字符串，或`None`（如果服务器未通知）。

  版本3.2中的新功能。

### 21.16.1.2. Methods

作为几乎所有方法的返回元组中的第一个项目返回的响应是服务器的响应：以三位数代码开头的字符串。如果服务器的响应指示错误，则方法引发上述异常之一。

许多以下方法采用可选的仅关键字参数文件。当提供文件参数时，它必须是为二进制写入打开的file object或要写入的磁盘文件的名称。然后，该方法将服务器返回的任何数据（响应行和终止点除外）写入文件；方法通常返回的任何行，元组或对象的列表将为空。

在版本3.2中更改：以下许多方法已重做并修复，这使得它们与3.1版本不兼容。

`NNTP.``quit`()

  发送`QUIT`命令并关闭连接。调用此方法后，不应调用NNTP对象的其他方法。

`NNTP.``getwelcome`()

  返回服务器发送的欢迎消息以回复初始连接。（此消息有时包含可能与用户相关的免责声明或帮助信息。）

`NNTP.``getcapabilities`()

  将服务器公布的 RFC 3977功能作为`dict`实例映射功能名称返回（可能为空）值列表。在不理解`CAPABILITIES`命令的旧服务器上，将返回一个空字典。

      >>> s = NNTP('news.gmane.org')
      >>> 'POST' in s.getcapabilities()
      True

  版本3.2中的新功能。

`NNTP.``login`(user=None, password=None, usenetrc=True)

  使用用户名和密码发送`AUTHINFO`命令。如果用户和密码为无，usenetrc为true，则将使用`~/.netrc` 。

  除非故意延迟，否则通常在`NNTP`对象初始化期间执行登录，并且不需要单独调用此函数。要强制身份验证延迟，您不能在创建对象时设置用户或密码，并且必须将usenetrc设置为False。

  版本3.2中的新功能。

`NNTP.``starttls`(ssl_context=None)

  发送`STARTTLS`命令。这将启用NNTP连接上的加密。ssl_context参数是可选的，应为`ssl.SSLContext`对象。有关最佳做法，请参阅Security considerations。

  请注意，在传输验证信息后，可能无法进行验证，如果可能，在`NNTP`对象初始化期间，默认会进行验证。有关抑制此行为的信息，请参见`NNTP.login()`。

  版本3.2中的新功能。

  在版本3.4中更改：此方法现在支持使用`ssl.SSLContext.check_hostname`和服务器名称指示进行主机名检查（请参阅`ssl.HAS_SNI`）。

`NNTP.``newgroups`(date, *, file=None)

  发送`NEWGROUPS`命令。date参数应为`datetime.date`或`datetime.datetime`对象。返回一对`（响应， 组）`其中组是表示自给定日期。如果提供文件，则组将为空。

      >>> from datetime import date, timedelta
      >>> resp, groups = s.newgroups(date.today() - timedelta(days=3))
      >>> len(groups)
      85
      >>> groups[0]
      GroupInfo(group='gmane.network.tor.devel', last='4', first='1', flag='m')

`NNTP.``newnews`(group, date, *, file=None)

  发送`NEWNEWS`命令。这里，group是组名或`'*'`，日期与`newgroups()`返回一对`（响应， 文章）`其中文章是邮件ID列表。

  NNTP服务器管理员经常禁用此命令。

`NNTP.``list`(group_pattern=None, *, file=None)

  发送`LIST`或`LIST ACTIVE`命令。返回一个`（响应， 列表）`其中列表是表示所有可用组NNTP服务器，可选择匹配模式字符串group_pattern。每个元组具有`（组， 最后， 第一， 标志）`其中组是组名称，最后和第一个是最后和第一个商品编号，而这些值之一：

  - `y`：允许来自同伴的本地帖子和文章。
  - `m`：群组已审核，所有过帐都必须通过审核。
  - `n`：不允许发布本地内容，仅允许来自同行的文章。
  - `j`：来自对等体的文章将替换为垃圾组。
  - `x`：没有本地发布，并且来自对等端的文章被忽略。
  - `=foo.bar`：文章被归档在`foo.bar`组中。

  如果标志有另一个值，则新闻组的状态应被视为未知。

  此命令可以返回非常大的结果，特别是如果未指定group_pattern。最好是离线缓存结果，除非你真的需要刷新它们。

  在版本3.2中更改： group_pattern已添加。

`NNTP.``descriptions`(grouppattern)

  发送`LIST NEWSGROUPS`命令，其中grouppattern是RFC 3977（它本质上与DOS或UNIX shell通配符字符串相同）。返回一对`（响应， 描述）`，其中描述是将组名称映射到文本描述的字典。

      >>> resp, descs = s.descriptions('gmane.comp.python.*')
      >>> len(descs)
      295
      >>> descs.popitem()
      ('gmane.comp.python.bio.general', 'BioPython discussion list (Moderated)')

`NNTP.``description`(group)

  获取单个组组的说明。如果多个组匹配（如果'group'是一个真正的wildmat字符串），返回第一个匹配。如果没有匹配，返回一个空字符串。

  这会从服务器中删除响应代码。如果需要响应代码，请使用`descriptions()`。

`NNTP.``group`(name)

  发送`GROUP`命令，其中名称是组名称。选择组作为当前组（如果存在）。返回元组`（响应， 计数， 第一， 最后， t5>`其中计数是组中的（估计）物品数量，第一是组中的第一个物品编号，t8>是组中的最后一个商品编号，name是组名称。

`NNTP.``over`(message_spec, *, file=None)

  在旧服务器上发送`OVER`命令或`XOVER`命令。message_spec can be either a string representing a message id, or a `(first, last)` tuple of numbers indicating a range of articles in the current group, or a `(first, None)` tuple indicating a range of articles starting from first to the last article in the current group, or `None` to select the current article in the current group.

  返回一对`（响应， 概述）`。概述是`（article_number， 概述）`元组的列表，由message_spec 。每个概述是具有相同数量项目的字典，但此数字取决于服务器。这些项目是消息头（键是较小包头的名称）或元数据项（键是元数据名称前加`":"`）。NNTP规范保证以下项目存在：

  - `subject`，`from`，`date`，`message-id`和`references`
  - `:bytes`元数据：整个原始文章中的字节数（包括标题和正文）
  - `:lines` metadata：文章正文中的行数

  每个项目的值为字符串，如果不存在，则为`None`。

  建议对头值使用`decode_header()`函数，它们可能包含非ASCII字符：

      >>> _, _, first, last, _ = s.group('gmane.comp.python.devel')
      >>> resp, overviews = s.over((last, last))
      >>> art_num, over = overviews[0]
      >>> art_num
      117216
      >>> list(over.keys())
      ['xref', 'from', ':lines', ':bytes', 'references', 'date', 'message-id', 'subject']
      >>> over['from']
      '=?UTF-8?B?Ik1hcnRpbiB2LiBMw7Z3aXMi?= <martin@v.loewis.de>'
      >>> nntplib.decode_header(over['from'])
      '"Martin v. Löwis" <martin@v.loewis.de>'

  版本3.2中的新功能。

`NNTP.``help`(*, file=None)

  发送`HELP`命令。返回一对`（响应， 列表）`其中列表是帮助字符串列表。

`NNTP.``stat`(message_spec=None)

  Send a `STAT` command, where message_spec is either a message id (enclosed in `'<'` and `'>'`) or an article number in the current group. 如果省略message_spec或`None`，则会考虑当前组中的当前文章。返回三次`（回应， 号码， id）`其中号码 number和id是消息ID。

      >>> _, _, first, last, _ = s.group('gmane.comp.python.devel')
      >>> resp, number, message_id = s.stat(first)
      >>> number, message_id
      (9099, '<20030112190404.GE29873@epoch.metaslash.com>')

`NNTP.``next`()

  发送`NEXT`命令。返回`stat()`。

`NNTP.``last`()

  发送`LAST`命令。返回`stat()`。

`NNTP.``article`(message_spec=None, *, file=None)

  发送`ARTICLE`命令，其中message_spec的含义与`stat()`相同。返回元组`（响应， 信息）`其中info是`namedtuple`属性号，message_id和行（按此顺序）。number是组中的文章编号（如果信息不可用，则为0），message_id消息ID为字符串，行包括包括头部和主体的原始消息的行的列表（不终止换行符）。

      >>> resp, info = s.article('<20030112190404.GE29873@epoch.metaslash.com>')
      >>> info.number
      0
      >>> info.message_id
      '<20030112190404.GE29873@epoch.metaslash.com>'
      >>> len(info.lines)
      65
      >>> info.lines[0]
      b'Path: main.gmane.org!not-for-mail'
      >>> info.lines[1]
      b'From: Neal Norwitz <neal@metaslash.com>'
      >>> info.lines[-3:]
      [b'There is a patch for 2.3 as well as 2.2.', b'', b'Neal']

`NNTP.``head`(message_spec=None, *, file=None)

  与`article()`相同，但发送`HEAD`命令。返回的（或写入到文件）的行将仅包含消息头，而不包含正文。

`NNTP.``body`(message_spec=None, *, file=None)

  与`article()`相同，但发送`BODY`命令。返回的（或写入到文件）的行将仅包含消息正文，而不包含头。

`NNTP.``post`(data)

  使用`POST`命令发布文章。数据参数是为二进制读取打开的file object 它应该代表一个格式正确的新闻文章，包括所需的标题。`post()`方法自动转义以`.`并附加终止线。

  如果方法成功，则返回服务器的响应。如果服务器拒绝发布，则会引发`NNTPReplyError`。

`NNTP.``ihave`(message_id, data)

  发送`IHAVE`命令。message_id is the id of the message to send to the server (enclosed in `'<'` and `'>'`). 数据参数和返回值与`post()`相同。

`NNTP.``date`()

  返回一对`（响应， 日期）`。date是包含服务器当前日期和时间的`datetime`对象。

`NNTP.``slave`()

  发送`SLAVE`命令。返回服务器的响应。

`NNTP.``set_debuglevel`(level)

  设置实例的调试级别。这控制打印的调试输出量。默认值`0`不产生调试输出。值`1`产生适量的调试输出，通常每个请求或响应都有一行。值`2`或更高会产生最大调试输出量，记录连接上发送和接收的每行（包括消息文本）。

以下是在 RFC 2980中定义的可选NNTP扩展。其中一些已被 RFC 3977中的新命令取代。

`NNTP.``xhdr`(hdr, str, *, file=None)

  发送`XHDR`命令。hdr参数是标题关键字，例如`'subject'`。str参数应为`'first-last'`，其中第一和最后数字进行搜索。返回一对`（响应， 列表）`，其中列表是`（id， text）`，其中id是文章编号（字符串），该文章的请求标题的文本。如果提供了文件参数，则`XHDR`命令的输出存储在文件中。如果文件是字符串，那么该方法将打开具有该名称的文件，写入该文件，然后关闭它。如果文件是file object，则它将开始调用`write()`来存储命令输出的行。如果提供文件，则返回的列表是空列表。

`NNTP.``xover`(start, end, *, file=None)

  发送`XOVER`命令。开始和结束是定义要选择的文章范围的文章编号。`over()`的返回值相同。建议改用`over()`，因为它会自动使用较新的`OVER`命令（如果可用）。

`NNTP.``xpath`(id)

  Return a pair `(resp, path)`, where path is the directory path to the article with message ID id. 大多数情况下，NNTP服务器管理员未启用此扩展。

  自版本3.3后已弃用：未主动使用XPATH扩展。

## 21.16.2. Utility functions

该模块还定义了以下效用函数：

`nntplib.``decode_header`(header_str)

  解码标头值，取消转义任何转义的非ASCII字符。header_str必须是`str`对象。返回未转义的值。建议使用此功能以人类可读的形式显示一些标题：

      >>> decode_header("Some subject")
      'Some subject'
      >>> decode_header("=?ISO-8859-15?Q?D=E9buter_en_Python?=")
      'Débuter en Python'
      >>> decode_header("Re: =?UTF-8?B?cHJvYmzDqG1lIGRlIG1hdHJpY2U=?=")
      'Re: problème de matrice'