使用 Python 通过网络查询 MySQL
最近,我拥有了一千多条专业或业余棋手下过的围棋(一种古老的东方战略棋盘游戏)棋谱记录。所有棋谱都以 Smart Game Format (SGF) 格式存储,这是一种基于文本的格式,旨在保存两位棋手的棋盘游戏记录。自然地,我通过我的网页向其他棋手提供了这些记录。
为了使搜索存档更容易,我首先使用了一个只有一个文本输入字段的 HTML 表单。用户可以输入一个字符串(例如,棋手姓名),该字符串被传递给 Python CGI 脚本,该脚本反过来调用了古老的 grep 来查找匹配的文件,如图 1 所示。但这是一种粗略的查找信息的方式。例如,它可以用来查找某个棋手的所有棋局,但不能查找更复杂的内容,例如该棋手使用黑子下的所有棋局,或者该棋手在 1995 年赢得的所有棋局。

图 1. पुराने, 粗略的搜索方法
为了制作更好的搜索引擎,我不得不使用不同的方法。首先,数据库应该描述游戏文件集合。然后,应该创建一个多输入字段表单,以允许用户同时搜索各种信息。最后,应该有一种方法来建立浏览器和数据库服务器之间的通信,以便将数据库查询结果作为 HTML 文档提供给用户。因此,整个搜索过程将如图 2 所示映射。

图 2. 更优雅的搜索方法
我选择了 MySQL 3.22.32 作为数据库,Python 1.5.2 用于 CGI 脚本。我玩过 Perl,但更喜欢 Python 的感觉。这些程序的安装过程已经在许多文章中解释过了,这里不再赘述。读者应查看“资源”中的网站,以获取有关安装的更多详细信息。MySQL 和 Python 之间的通信由一个贡献模块处理,如下所示。
Python 通过 Joerg Senekowitsch 设计的特殊模块进行 MySQL 查询。当然,网络上还有其他几个模块可用,但 MySQLmodule1.4 易于安装和学习,并且对我来说效果很好。在某些系统(如 FreeBSD)上,可以与 Python 同时安装此模块。在我的 Slackware 7.1 Linux 上,我必须将其构建并安装为动态加载模块。这是一个三步过程:解压 MySQLmodule 存档,编译共享模块,并将模块安装到 Python 的库路径中的某个位置。
第一步非常简单。作为 root 用户,可以键入
myhost:~# tar xvzf MySQLmodule-1.4.tar.gz
将创建一个名为 MySQLmodule-1.4/ 的新目录,其中包含多个文件,其中最重要的是 MySQLmodule.c(要编译的模块的源代码)和 README(包含安装和使用信息的文件)。
关于第二步,有几个棘手的事情。例如,必须准确知道 MySQL 和 Python 的库和包含文件在哪里可以找到。在我的系统上,MySQL 3.22.32 将 mysqlclient 库放在 /usr/lib/mysql 中,将 mysql.h 包含文件放在 /usr/include/mysql 中。Python 库可以在 /usr/lib/python1.5/config 中找到,包含文件可以在 /usr/include/python1.5 中找到。编译 MySQLmodule 的命令是
myhost:~# gcc -shared -I/usr/include/mysql MySQLmodule.so
另一个提示:上面命令行中项目的顺序很重要,不得更改!相信我,这是一个惨痛的教训。
第三步包括将 MySQLmodule.so 复制到 Python 在运行时可以找到的目录。对于 Python 1.5,这可以是 /usr/lib/python1.5/lib-dynload,其中也驻留着其他共享对象文件。对于 Python 2.0(我也测试过),我建议使用目录 /usr/lib/python2.0/site-packages/。
安装模块后,它应该可以从 Python 访问。最好立即使用简单的 import 语句检查这一点,例如
myhost:~$ python Python 1.5.2 (#1, May 28 2000, 18:04:10) Copyright 1991-1995 Stichting Matematisch Centrum, Amsterdam >>> import MySQL >>>
如果 Python 没有报错,则 MySQL 模块很可能已正确安装并正在工作。
用户应该能够搜索数据库中的多个项目,例如锦标赛名称、黑白棋手姓名、游戏日期(至少年份)以及游戏的获胜者。所有这些信息都可以在 SGF 文件以及实际的游戏记录中找到。
因此,我编写了清单 1 中所示的 HTML 文档。是的,我喜欢手工编写 HTML,而且不需要更复杂的东西。当然,“myhost”必须替换为 Web 服务器的实际主机名。读者还会注意到,一旦单击提交按钮,HTML 表单就会通过调用名为 search.py 的 CGI 脚本来执行操作。Netscape 加载的文档的外观如图 3 所示。
HTML 表单中的每个项目都必须在数据库中描述。为此,我创建了一个新数据库来存储表。这是通过以 root 用户身份键入以下命令来完成的
myhost:~# mysqladmin create igo
其中 igo 是新数据库名称。但这还不够,因为只有 root 用户才能访问新数据库。要仅向所有用户授予对新数据库的 SELECT 权限,root 用户必须键入
myhost:~# mysql mysql mysql> insert into db values ( '%','igo','','Y','N', 'N', 'N', 'N','N','N', 'N', 'N', 'N');这通过为 igo 数据库添加条目来更改 MySQL 内部数据库中的 table 数据库。然后,必须重新启动 MySQL 守护程序,或者必须键入命令 mysql> flush privileges;,以便 MySQL 守护程序意识到权限更改。
现在,所有用户都应该能够访问数据库 igo 中的数据,但只有 root 用户可以更改数据。
创建表(每个锦标赛一个)很容易。例如,使用以下命令为锦标赛 Gosei 创建了一个名为 gosei 的表
myhost:~# mysql igo mysql> create table gosei ( black varchar(30), white varchar(30), dt date, rez varchar(30), fname varchar(30), ; Query OK, 0 rows affected, (0.00 sec) mysql>
该表有五列:黑棋手姓名、白棋手姓名、游戏日期、结果以及相应的 SGF 文件名。
将数据加载到表中是另一回事。我想一种方法是键入
mysql> insert into gosei values ('Cho Chikun', 'Kato Masao', '1987-07-03', 'B+3.5', 'gosei87_1.sgf' );
但我宁愿学习一门新的编程语言,也不愿输入一千次。幸运的是,还有另一种将数据加载到 MySQL 表中的方法——从文本文件加载。文件中的每一行都与表中的一行匹配,字段用空格分隔,如表 1 所示。
假设此文件也名为 gosei。要将数据传递到 MySQL 表,可以编写
mysql> load data infile "gosei" into table gosei;
然后,查询结果应如表 2 所示。
读者可能会问“好吧,但是制作一个巨大的文本文件难道不是另一个负担吗?”实际上,对于另一个小的 Python 脚本来说,这是一项简单的任务(我不会展示,因为它与我们的主题没有直接关系)。
创建了 igo 数据库并将数据加载到表中后,只剩下一件事要做:编写 Python CGI 脚本,该脚本将通过 HTML 表单从用户处获取输入,查询数据库并生成匹配的游戏文件列表。
此程序名为 search.py,在清单 2 中给出 [在 LJ ftp 站点]。它使用了第三行和第四行中导入的两个很棒的模块。CGI 模块几乎像魔法一样:它只是将 HTML 表单提交的数据作为 Python 字典获取。程序员无需关心诸如用于将表单数据发送到 CGI 脚本的方法(GET 或 POST)之类的细节。你不喜欢 Python 吗?
MySQL 模块也易于使用。通过四个简单的语句,它可以打开与所需数据库的连接,发送查询,获取结果并将结果存储在行列表列表中(一个 Python 列表,其成员每个都包含一行)。
该脚本具有以下结构:获取 HTML 表单数据,根据表单数据创建数据库查询字符串,查询表并打印匹配的结果。
由于每个围棋锦标赛都有一个表,因此步骤三根据需要重复多次。Python 代码和附加的注释是相当不言自明的,所以我只评论以下几行
print '<li><a href="http://myhost' + \ '/cgi-bin/getsgf.py?file=' + e[0] + '">' print e[0] + '</a>'
在这里,e[0] 是 SGF 文件名。打印语句没有仅仅打印文件名(这对用户的帮助有限),而是创建了一个 HTML 锚点,该锚点将文件名发送到 CGI 脚本 getsgf.py。最后一个脚本(为简单起见,这里不显示)搜索 SGF 文件的实际路径,并启动一个 Java applet 以图形化的方式显示文件内容。
当然,search.py 和 getsgf.py 都必须设为可执行文件并移动到 cgi-bin 目录。此外,要避免的最后一个陷阱:所有 SGF 游戏文件都必须放在 DocumentRoot 目录(在 httpd 配置文件中定义)中的某个位置,以便 CGI 脚本可以找到它们。
运行 search.py 的结果如图 4 所示,用户可以看到。单击每个文件名都会启动图 5 中所示的 applet,该 applet 会逐步显示游戏的步骤。
Python 是一门很棒的语言。MySQL 模块使创建小程序以从 MySQL 数据库检索数据变得容易。Python 也非常适合 CGI 脚本。因此,只需几行代码即可通过 Web 浏览器访问数据库进行研究。
我在这里展示的应用程序相当有限。用户只能搜索五个变量,并且以固定的方式进行搜索,这是由 HTML 表单预先确定的。但是,用户可以想象通过 textarea 输入字段编写自己的数据库查询,并在网上查看查询结果。事实上,可能性仅受程序员的想象力限制。
