使用 Python 通过网络查询 MySQL

作者:Mihai Bisca

最近,我拥有了一千多条专业或业余棋手下过的围棋(一种古老的东方战略棋盘游戏)棋谱记录。所有棋谱都以 Smart Game Format (SGF) 格式存储,这是一种基于文本的格式,旨在保存两位棋手的棋盘游戏记录。自然地,我通过我的网页向其他棋手提供了这些记录。

为了使搜索存档更容易,我首先使用了一个只有一个文本输入字段的 HTML 表单。用户可以输入一个字符串(例如,棋手姓名),该字符串被传递给 Python CGI 脚本,该脚本反过来调用了古老的 grep 来查找匹配的文件,如图 1 所示。但这是一种粗略的查找信息的方式。例如,它可以用来查找某个棋手的所有棋局,但不能查找更复杂的内容,例如该棋手使用黑子下的所有棋局,或者该棋手在 1995 年赢得的所有棋局。

Using Python to Query MySQL over the Net

图 1. पुराने, 粗略的搜索方法

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

Using Python to Query MySQL over the Net

图 2. 更优雅的搜索方法

工具

我选择了 MySQL 3.22.32 作为数据库,Python 1.5.2 用于 CGI 脚本。我玩过 Perl,但更喜欢 Python 的感觉。这些程序的安装过程已经在许多文章中解释过了,这里不再赘述。读者应查看“资源”中的网站,以获取有关安装的更多详细信息。MySQL 和 Python 之间的通信由一个贡献模块处理,如下所示。

MySQL模块

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 模块很可能已正确安装并正在工作。

HTML 表单

用户应该能够搜索数据库中的多个项目,例如锦标赛名称、黑白棋手姓名、游戏日期(至少年份)以及游戏的获胜者。所有这些信息都可以在 SGF 文件以及实际的游戏记录中找到。

因此,我编写了清单 1 中所示的 HTML 文档。是的,我喜欢手工编写 HTML,而且不需要更复杂的东西。当然,“myhost”必须替换为 Web 服务器的实际主机名。读者还会注意到,一旦单击提交按钮,HTML 表单就会通过调用名为 search.py 的 CGI 脚本来执行操作。Netscape 加载的文档的外观如图 3 所示。

清单 1. 存档搜索

Using Python to Query MySQL over the Net

图 3. 在 Netscape 中加载的文档

Igo 数据库

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 用户可以更改数据。

MySQL 表

创建表(每个锦标赛一个)很容易。例如,使用以下命令为锦标赛 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 所示。

表 1. 要加载到 MySQL 表的文本

假设此文件也名为 gosei。要将数据传递到 MySQL 表,可以编写

mysql> load data infile "gosei" into table gosei;

然后,查询结果应如表 2 所示。

表 2. 从文本文件加载

读者可能会问“好吧,但是制作一个巨大的文本文件难道不是另一个负担吗?”实际上,对于另一个小的 Python 脚本来说,这是一项简单的任务(我不会展示,因为它与我们的主题没有直接关系)。

创建了 igo 数据库并将数据加载到表中后,只剩下一件事要做:编写 Python CGI 脚本,该脚本将通过 HTML 表单从用户处获取输入,查询数据库并生成匹配的游戏文件列表。

Python CGI 脚本

此程序名为 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 会逐步显示游戏的步骤。

Using Python to Query MySQL over the Net

图 4. search.py 结果

Using Python to Query MySQL over the Net

图 5. gose:80_3.sgf Applet

结论

Python 是一门很棒的语言。MySQL 模块使创建小程序以从 MySQL 数据库检索数据变得容易。Python 也非常适合 CGI 脚本。因此,只需几行代码即可通过 Web 浏览器访问数据库进行研究。

我在这里展示的应用程序相当有限。用户只能搜索五个变量,并且以固定的方式进行搜索,这是由 HTML 表单预先确定的。但是,用户可以想象通过 textarea 输入字段编写自己的数据库查询,并在网上查看查询结果。事实上,可能性仅受程序员的想象力限制。

资源

Using Python to Query MySQL over the Net
Mihai Bisca,(又名 5dan),是罗马尼亚围棋锦标赛的前冠军。他花费无数时间玩他的 Slackware Linux,梦想着在家通过互联网工作。在他的另一个白天生活中,他是一名眼科医生。
加载 Disqus 评论