前言
爬虫过程中不可缺少的环节就是数据存储,一般来说这些数据首选是保存到数据库中。但是数据库写的方式是同步写入,能不能像 request 请求那样遇到这种 I/O 操作就去执行其他任务呢?答案是可以的。
Scrapy异步写入
Scrapy 是基于 Twisted 库实现的爬虫框架,而 Twisted 库已经为我们准备好了异步写入数据库的方法,配置也很简单,在 pipelines.py 里定义一个类,并实现这几个方法就好。
from twisted.enterprise import adbapi
import pymysql
import pymysql.cursors
# Twisted高并发的mysql插入
class MysqlTwistedPipeline(object):
def __init__(self, dbpool):
self.dbpool = dbpool
@classmethod
def from_settings(cls, settings):
# 数据库配置
pass
def process_item(self, item, spider):
# 使用Twisted将mysql插入变成异步执行
pass
def handle_error(self, failure, item, spider):
# 处理异步插入的异常
pass
def do_insert(self, cursor, item):
# 执行具体插入
pass
函数解释
from_settings, 类方法, 从字面意思也不难理解,它的功能就是定义一个连接池,传入两个参数: settings 和 cls 并返回一个连接池对象供数据库读写时调用。具体实现代码如下:
@classmethod
def from_settings(cls, settings):
# 数据库配置
dbparms = dict(
host=settings["MYSQL_HOST"], # 数据库Host
database=settings["MYSQL_DBNAME"], # 数据库名
user=settings["MYSQL_USER"], #数据库用户名
password=settings["MYSQL_PASSWORD"], #数据库密码
charset='utf8', # 使用的字符集
cursorclass=pymysql.cursors.DictCursor, # 将游标类型设置为字典形式
use_unicode=True # 使用unicode格式
)
dbpool = adbapi.ConnectionPool("pymysql", **dbparms)
return cls(dbpool) 返回一个
数据库配置的字段名不能随便乱起,在 pymysql.connections 有定义规则:
cls, cls 主要用在类方法定义,而 self 则是实例方法。
普通的实例方法,第一个参数需要是 self ,它表示一个具体的实例本身。
如果用了 staticmethod ,那么就可以无视这个 self ,而将这个方法当成一个普通的函数使用。
而对于 classmethod ,它的第一个参数不是self,是cls,它表示这个类本身。
settings 可以理解为我们工程的 settings.py 文件对象,我们在函数传入 settings 参数,这样才可以在其他文件里调用 settings.py 里的内容,通过字典的方式可以调用参数,比如下面这四个:
这是数据库的配置,我在 settings.py 的最下方有如下定义,下面这些参数名可以自己起,方便使用即可
MYSQL_HOST = "host"
MYSQL_DBNAME = "dbname"
MYSQL_USER = "username"
MYSQL_PASSWORD = "password"
process_item, 数据库的写入就是在该函数下操作的,异步逻辑就是在它下面实现的:
def process_item(self, item, spider):
# 使用Twisted将mysql插入变成异步执行
query = self.dbpool.runInteraction(self.do_insert, item) # 该方法会把我们的函数变为异步的,返回一个 query 对象,第一个就是执行函数,第
二个item就是我们要写入的数据
query.addErrback(self.handle_error, item, spider) # 异步处理出错时执行, 第一个是错误处理函数,
do_insert, 这个就是我定义的写入函数,名字随意,而且自动帮我们 commit
def do_insert(self, cursor, item):
# 执行具体插入
insert_sql = """
insert into table(column1,column2,column3) values ("%s", "%s", "%s")
"""
data = (item["字段一"], item["字段二"], item["字段三"])
cursor.execute(insert_sql % data)
handle_error, 异常处理,名字随意,4个参数,第一个是对象本身,第二个就是具体failure,第三个是item,第四个是当前spider,后面两个参数可以忽略,那上方 query.addErrback(self.handle_error)
就可以这样了
def handle_error(self, failure, item, spider):
# 处理异步插入的异常
print(failure)
最后我们把这个类在 settings.py 中声明,我的工程名是 douban 。
ITEM_PIPELINES = {
'douban.pipelines.MysqlTwistedPipeline': 1,
}