返回介绍

9.2 与标准 Python 客户端建立数据库接口

发布于 2024-01-30 22:48:37 字数 4729 浏览 0 评论 0 收藏 0

有很多重要的数据库遵从Python数据库API规范2.0版本,包括MySQL、PostgreSQL、Oracle、Microsoft SQL Server和SQLite。它们的驱动一般都比较复杂且久经考验,如果为Twisted重新实现的话则是巨大的浪费。人们可以在Twisted应用中使用这些数据库客户端,比如在Scrapy使用twisted.enterprise.adbapi库。我们将使用MySQL作为示例演示其使用,不过对于任何其他兼容的数据库来说,也可以应用相同的原则。

9.2.1 用于写入MySQL的管道

MySQL是一个非常强大且流行的数据库。我们将编写一个管道,将item写入到其中。我们已经在虚拟环境中运行了一个MySQL实例。现在只需使用MySQL命令行工具执行一些基本管理即可,同样该工具也已经在开发机中预安装好了,下面执行如下操作打开MySQL控制台。

$ mysql -h mysql -uroot -ppass

这将会得到MySQL的提示符,即mysq>,现在可以创建一个简单的数据库表,其中包含一些字段,如下所示。

mysql> create database properties;
mysql> use properties
mysql> CREATE TABLE properties (
 url varchar(100) NOT NULL,
 title varchar(30),
 price DOUBLE,
 description varchar(30),
 PRIMARY KEY (url)
);
mysql> SELECT * FROM properties LIMIT 10;
Empty set (0.00 sec)

非常好,现在拥有了一个MySQL数据库,以及一张名为properties的表,其中包含了一些字段,此时可以准备创建管道了。请保持MySQL的控制台为开启状态,因为之后还会回来检查是否正确插入了值。如果想退出控制台,只需要输入exit即可。

在本节,我们将会向MySQL数据库中插入房产信息。如果你想擦除它们,可以使用如下命令:

mysql> DELETE FROM properties;

我们将使用Python的MySQL客户端。我们还将安装一个名为dj-database-url的小工具模块,帮助我们解析连接的URL(仅用于为我们在IP、端口、密码等不同设置中切换节省时间)。可以使用pip install dj-database-url MySQL-python安装这两个库,不过我们已经在开发环境中安装好它们了。我们的MySQL管道非常简单,如下所示。

from twisted.enterprise import adbapi
...
class MysqlWriter(object):
  ...
  def __init__(self, mysql_url):
    conn_kwargs = MysqlWriter.parse_mysql_url(mysql_url)
    self.dbpool = adbapi.ConnectionPool('MySQLdb',
                      charset='utf8',
                      use_unicode=True,
                      connect_timeout=5,
                      **conn_kwargs)

  def close_spider(self, spider):
    self.dbpool.close()

  @defer.inlineCallbacks
  def process_item(self, item, spider):
    try:
      yield self.dbpool.runInteraction(self.do_replace, item)
    except:
      print traceback.format_exc()

    defer.returnValue(item)

  @staticmethod
  def do_replace(tx, item):
    sql = """REPLACE INTO properties (url, title, price,
    description) VALUES (%s,%s,%s,%s)"""

    args = (
      item["url"][0][:100],
      item["title"][0][:30],
      item["price"][0],
      item["description"][0].replace("\r\n", " ")[:30]
    )

    tx.execute(sql, args)

本示例的完整代码地址为ch09/properties/properties/pipeline/mysql.py。

本质上,大部分代码仍然是模板化的爬虫代码。我们省略的代码用于将MYSQL_PIPELINE_URL设置中包含的mysql://user:pass@ip/database格式的URL解析为独立参数。在爬虫的__init__()中,我们将这些参数传给adbapi.ConnectionPool(),使用adbapi的基础功能初始化MySQL连接池。第一个参数是想要导入的模块名称。在该MySQL示例中,为MySQLdb。我们还为MySQL客户端设置了一些额外的参数,用于处理Unicode和超时。所有这些参数会在每次adbapi需要打开新连接时,前往底层的MySQLdb.connect()函数。当爬虫关闭时,我们为该连接池调用close()方法。

我们的process_item()方法实际上包装了dbpool.runInteraction()。该方法将稍后调用的回调方法放入队列,当来自连接池的某个连接的Transaction对象变为可用时,调用该回调方法。Transaction对象的API与DB-API游标相似。在本例中,回调方法为do_replace(),该方法在后面几行进行了定义。@staticmethod意味着该方法指向的是类,而不是具体的类实例,因此,可以省略平时使用的self参数。当不使用任何成员时,将方法静态化是个好习惯,不过即使忘记这么做,也没有问题。该方法准备了一个SQL字符串和几个参数,调用Transaction的execute()方法执行插入。我们的SQL语句使用了REPLACE INTO来替换已经存在的条目,而不是更常见的INSERT INTO,原因是如果条目已经存在,可以使用相同的主键。在本例中这种方式非常便捷。如果想使用SQL返回数据,如SELECT语句,可以使用dbpool.runQuery()。如果想要修改默认游标,可以通过设置adbapi.ConnectionPool()的cursorclass参数来实现,比如设置cursorclass=MySQLdb.cursors.DictCursor,可以让数据获取更加便捷。

要想使用该管道,需要在settings.py文件的ITEM_PIPELINES字典中添加它,另外还需要设置MYSQL_PIPELINE_URL属性。

ITEM_PIPELINES = { ...
  'properties.pipelines.mysql.MysqlWriter': 700,
...
MYSQL_PIPELINE_URL = 'mysql://root:pass@mysql/properties'

执行如下命令。

scrapy crawl easy -s CLOSESPIDER_ITEMCOUNT=1000

该命令运行后,可以回到MySQL提示符下,按如下方式查看数据库中的记录。

mysql> SELECT COUNT(*) FROM properties;
+----------+
| 1006 |
+----------+
mysql> SELECT * FROM properties LIMIT 4;
+------------------+--------------------------+--------+-----------+
| url       | title          | price | description
+------------------+--------------------------+--------+-----------+
| http://...0.html | Set Unique Family Well  | 334.39 | website c
| http://...1.html | Belsize Marylebone Shopp | 388.03 | features
| http://...2.html | Bathroom Fully Jubilee S | 365.85 | vibrant own
| http://...3.html | Residential Brentford Ot | 238.71 | go court
+------------------+--------------------------+--------+-----------+
4 rows in set (0.00 sec)

延时和吞吐量等性能和之前保持相同,相当不错。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文