Python HTMLgen 模块

作者:Michael Hamilton

本文介绍如何使用 HTMLgen,一个用于生成 HTML 的 Python 类库。Python 是一种面向对象的脚本语言,大多数 Linux 发行版都附带它。它在 Caldera 和 Red Hat 等发行版的配置和管理中起着重要作用。HTMLgen 是一个附加的 Python 模块,由 Robin Friedrich 编写,可从 http://starship.python.net/lib.html 获取,采用 BSD 风格的免费软件许可证。

HTMLgen 提供了类来支持所有标准的 HTML 3.2 标签和属性。它可以用于任何需要动态生成 HTML 的情况。例如,您可能希望将数据库查询结果格式化为 HTML 表格,或者生成为每个客户定制的 HTML 订单表单。

我将通过使用 HTMLgen 格式化在典型 Linux 系统上找到的数据来介绍它。我认为这些示例足够简单明了,任何熟悉 HTML 和脚本编写的人都可以理解,而无需事先了解 Python。只需记住,在 Python 中,语句块通过代码缩进表示——没有 begin/end 语句,也没有花括号。(在 Python 中,所见即所得适用。)除此之外,Python 代码看起来很像任何主流编程语言中的代码。

虽然 Perl 是最常用的 Web 脚本语言,但我个人更喜欢 Python。它可以实现类似于 Perl 的结果,并且我认为 Python 的语法,加上其用户社区建立的风格,可以带来更简洁、更简单的编码风格。这在开发和维护过程中都是一个优势。这些相同的优势为新手提供了更平缓的学习曲线。Python 在一定程度上远离了传统的脚本语言,而更接近非脚本的、过程式的编程语言。这使得 Python 脚本能够很好地扩展。当一小组脚本开始增长到完整的应用程序系统的大小时,该语言将支持这种转变。

入门

任何需要 HTMLgen 的 Python 程序都必须将其作为模块导入。从 bash 开始,以下是我如何设置和导入 HTMLgen 以创建一个 “Hello World” 网页

bash$ export PYTHONPATH=/local/HTMLgen:$PYTHONPATH
bash$ python
 >>> import HTMLgen
 >>> doc = HTMLgen.SimpleDocument(title="Hello")
 >>> doc.append(HTMLgen.Heading(1, "Hello World"))
 >>> print doc

首先,我设置 PYTHONPATH 以包含可以找到 HTMLgen.py 模块的目录。然后,我启动 Python 解释器并使用其命令行界面导入 HTMLgen 模块。我创建一个名为 doc 的文档对象,并向其添加一个标题。

最后,我打印 doc 对象,它会将以下 HTML 转储到标准输出

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<!-- This file generated using HTMLgen module.
-->
<HEAD>
 <META NAME="GENERATOR" CONTENT="HTMLgen 2.0.6">
 <TITLE>Hello World</TITLE>
</HEAD>
<BODY>
<H1>Hello World</H1>
</BODY> </HTML>

图 1. 表格——清单 1 中的代码

这是一个开始,虽然不是一个令人兴奋的开始。HTMLgen 是一个非常好的用于生成 HTML 表格和列表的工具。图 1 中的表格是由 清单 1 中的 Python 脚本创建的。表格中的数据来自 Linux /proc/interrupts 文件,该文件详细说明了您的 Linux PC 的 IRQ 中断。在我的 PC 上,执行 cat /proc/interrupts 会产生

0: 2348528 timer
1: 42481 keyboard
2: 0 cascade
3: 47735 + serial
4: 75428 + serial
5: 48 soundblaster
8: 0 + rtc
11: 1 NE2000
13: 1 math error
14: 175816 + ide0
15: 216 + ide1

Python 脚本读取 /proc/interrupts 文件的内容,并将数据复制到 HTML 表格中。我将逐步描述这个过程。与前面的示例一样,我首先创建一个简单的文档。然后,我向文档添加一个 HTMLgen 表格

table = HTMLgen.Table( tabletitle='Interrupts',
 border=2, width=100, cell_align"right",
 heading=[ "Description", "IRQ",
"Count" ])
 doc.append(table)
在创建表格对象时,我通过将一些可选属性作为命名参数提供来设置它们。最后的 headings 参数设置 HTMLgen 将使用的列标题列表。以上所有参数都是可选的。

设置好表格后,我打开 /proc/interrupts 文件并使用 readlines 方法读取其全部内容。我使用 for 循环遍历返回的行,并将它们转换为表格行。在循环内部,使用字符串和正则表达式函数来去除前导空格,并根据空格和冒号 (:) 分隔符将每一行拆分为包含三个数据值的列表

data=regsub.split(string.strip(line),'[ :+]+')

数据列表的元素经过处理,通过将它们重新排序为一个新的三元素列表(包含名称、编号和总调用次数)来形成表格行

[ HTMLgen.Text(data[2]), data[0], data[1] ]
外部的方括号构造一个由逗号分隔的参数组成的列表。第一个列表元素 data[2] 是中断名称。中断名称是一个非数字字段,所以我采取了预防措施,通过 HTMLgen Text 过滤器转义任何可能对 HTML 特殊的字符。生成的列表通过将该列表附加到表格的主体中而成为表格的一行
table.body.append(
        [ HTMLgen.Text(data[2]), data[0], data[1] ])
最后,一旦所有行都被处理完毕,文档将被写入 interrupts.html。结果如图 1 所示。

简单的 Table 类旨在显示数据行,例如可能从数据库查询返回的数据行。对于更复杂的表格,TableLite 对象提供了更低级别的表格构建工具,其中包括进行单独的行/列自定义、列/行跨越和嵌套表格的能力。

图 2. 条形图——清单 2 中的代码

从 HTML 表格生成条形图

表格功能也已扩展到创建精美的条形图。图 2 显示了我从 Linux ps 命令的输出生成的条形图。该图表由 HTMLgen 条形图模块创建。psv.py 的代码是 清单 2 中显示的 20 行 Python 代码。ps v 的原始输出看起来像下面这样

PID TTY STAT TIME PAGEIN TSIZ DSIZ RSS LIM %MEM COMMA
 555 p1 S 0:01 232 237 1166 664 xx 2.1 -tcsh
1249 p2 S 0:00 424 514 2613 1676 xx 5.4 xv ps
 ...

我使用 Python 操作系统模块的 popen 函数为命令的输出流返回一个文件输入管道

inpipe = os.popen("ps vax", "r");
然后,我从输入管道中读取第一行,并将其拆分为列名列表。
colnames = string.split(inpipe.readline())
现在,我创建图表对象和图表对象的数据列表对象
chart = barchart.StackedBarChart()
...
chart.datalist = barchart.DataList()
数据列表可以为每个条形图拥有多个数据段,这会导致堆叠条形图,如图 2 所示。我需要通过设置 segment_names 列表来告诉数据列表对象存在多少个数据段。我决定我的图表上的条形图将有两个段,一个用于 TSIZ(程序文本内存大小),另一个用于 DSIZ(程序数据内存大小)。为了实现这一点,我需要将两个列名从 colnames 复制到 segment_names。由于 Python 中的列表从零开始编号,因此我感兴趣的两个 colnames 列是第 5 列 (TSIZ) 和第 6 列 (DSIZ)。我可以使用单个切片语句从 colnames 列表中提取它们
chart.datalist.segment_names = colnames[5:7]
data = chart.datalist
[5:7] 表示法是一种切片表示法。在 Python 中,您可以从字符串、列表和其他序列数据类型中切片出单个项目和项目范围。表示法 [low:high] 表示从 low 到 high 减 1 切片出一个新列表。在第二行中,我将名为 “data” 的变量分配给变量 “chart.datalist”,以缩短以下行的长度,使其适合Linux Journal 中所需的列宽。

初始化图表后,我使用 for 循环从 ps 输出管道中读取剩余的行。我通过使用 string.split(line) 将行分解为列列表来提取我需要的列。我通过从第 10 列开始获取所有单词并将它们连接成一个新的 barname 字符串来提取每个命令的文本

barname = string.join(cols[10:], " " )

我使用 string 模块的 atoi 函数将数字字段中的 ASCII 字符串转换为整数。循环中的最后一个语句将数据组装成一个元组

( barname, tsize, dsize )
元组是一种 Python 结构,很像列表,只是元组是不可变的——您无法从元组中插入或删除元素。尽管两者相似,但它们的差异导致了非常不同的实现效率。Python 既有元组又有列表,因为这允许程序员选择最适合情况的一个。Python 及其模块的许多功能都旨在成为服务的更高级别接口,然后这些服务在编译语言(如 C)中高效实现。这允许 Python 用于使用 OpenGL 的计算机图形编程以及使用快速数值库的数值编程。

回到示例。循环中的最后一个语句将元组插入到图表的数据列表中。

data.load_tuple(( barname, tsize, dsize ))

当最后一行被处理后,循环终止,我按 TSIZ 的降序对数据进行排序

data.sort(key=colnames[5], direction="decreasing")
之后,我创建最终文档并将其保存到文件中。
doc = HTMLgen.SimpleDocument(title='Memory')
doc.append(chart)
doc.write("psv.html")
将 psv.html 加载到浏览器中会得到如图 2 所示的图表。通过更改条形图的参数,例如用于图表的“原子”的 GIF,我可以构建不同样式的图表。

图 3. 清单 3 生成的 HTML 页面

多文档

在我的下一个示例中,我将向您展示如何处理数据流以生成一系列相互链接的文档。清单 3 中的脚本创建一组两个文档,总结了 Linux 系统上安装的所有 Red Hat 软件包。生成的 HTML 页面如图 3 所示。索引文档总结了 RPM 主要组,第二个主文档总结了每个组中的 RPM。索引文档中每个组的链接直接跳转到主文档中每个组的条目。

HTML 是从以下 rpm 命令的输出生成的

rpm -q -a --queryformat \
   '%{group} %{name} %{summary}\n'

输出通常如下所示

System/Base sh-utils GNU shell utilities.
Browser/WWW lynx tty WWW browser
Programming/Tools make GNU Make.
System/Library xpm X11 Pixmap Library
System/Shell pdksh Public Domain Korn Shell
我将此输出读取到 Python 列表中,并通过按字母顺序对其进行排序来预处理它。

我使用 HTMLgen 的 SeriesDocument 生成两个文档,一个索引文档 (idoc) 和一个主文档 (mdoc),使两个文档具有相同的外观和风格。通过使用 SeriesDocument,我可以通过 rcfile 和其他可选参数配置标准文档标头、页脚和导航按钮。

索引文档 (idoc) 只有一个 HTMLgen 组件:RPM 组的 HTML 列表。我使用了 HTMLgen.List columns 选项来创建一个多列列表

ilist = HTMLgen.List(style="compact", columns=3)
idoc.append(ilist)

for 循环处理来自 rpm 命令的每一行,并生成 idocmdoc 文本。每次组名更改时,我都会向 ilist 添加一个新的列表条目

if group != lastgroup:
 lastgroup = group
 title = HTMLgen.Text(group)
 href = HTMLgen.Href(mainfile+"#"+ group,
        title)
 index.append(href)
我将列表文本包装在 HTML 命名 HREF 中,将其链接回 mdoc。我使用主文件名和组标题来形成 HREF 链接。例如,对于 “Browser/FTP” RPM 组,我的代码将生成以下 HREF 链接
<A HREF="rpmlist.html#Browser/FTP">Browser/FTP</A>
主文档 (mdoc) 具有更复杂的结构。它由一系列 HTML 定义列表组成,每个 RPM 组一个。每次组名更改时,我都会生成命名锚点,该锚点是上面生成的引用的目标
anchor = HTMLgen.Name(group, title)
我将锚点作为新的组标题附加到 mdoc
mdoc.append(HTMLgen.Heading(2, anchor))
对于 “Browser/FTP” 组,这将生成以下 HTML
<H2><A NAME="Browser/FTP">Browser/FTP</A></H2>
一旦组标题被附加,我就开始一个组中的 RPM 列表
grplist = HTMLgen.DefinitionList()
一旦新的组列表启动,我的 for 循环将继续将 RPM 摘要附加到 mdoc,直到下一个组名发生更改
grplist.append(
 (HTMLgen.Text(name),HTMLgen.Text(summary)))
当整个 rpmlist 被处理完毕后,我生成您在图 3 中看到的两个文档。

在这个例子中,我同时生成了两个简单的文档,并将一个文档链接到另一个文档。这个例子可以很容易地扩展,以便为每个 RPM 提供进一步的链接到单独的文档,并从每个 RPM 链接到它所依赖的 RPM。

后续方向

我只是浅尝辄止地介绍了 HTMLgen 和 Python 的可能性。我没有涵盖 HTML 表单、图像地图、嵌套表格、框架或 Netscape 脚本的 HTMLgen 对象。我也还没用过 Python 的面向对象特性。例如,我可以对一些 HTMLgen 对象进行子类化,以针对每个应用程序的特定性进行自定义。我没有讨论用于 CGI 处理的 Python 模块。您可以通过将浏览器指向本文随附的一些参考资料(请参阅“资源”)来阅读有关这些主题的更多信息。

如果您正尝试开始使用 HTMLgen,则 HTMLgen 附带的 HTMLtest.py 文件提供了一些很好的示例。HTMLgen 文档相当不错,尽管在某些情况下,更多的示例会有所帮助。我不认为我的示例需要任何特定的 Linux 发行版、libc 或 Python。所有这些都是使用 HTMLgen 2.0 和 Python 1.4 在 Caldera OpenLinux Standard 1.2 版本上编写的。

资源

The Python HTMLgen Module
Michael Hamilton 是一位自由 UNIX C/C++ 开发人员。最近,他一直在从事 Web 和 Java 项目,以及 C++ 容错 UNIX 应用程序。Michael 在 1992 年初偶然看到了 Linus 的一篇帖子,从此就迷上了。Michael 目前运行着两台 Linux 主机,一台是主要工作站,另一台是用作 X 终端的旧 386。这两个系统都已用于将在 Linux 和其他 UNIX 平台上交付的项目。可以通过 michael@actrix.gen.nz 与他联系。
加载 Disqus 评论