为了实现python与mysql的交互,我们首先需要在虚拟环境中安装python与mysql交互所需的第三方包mysqlclient:
pip install -i https://pypi.douban.com/simple mysqlclient
Windows下出错,参见:Python使用pip安装非官方包(例如mysqlclient)失败后的一个通用解决办法
Ubuntu下出错,可以尝试执行:
sudo apt-get install libmysqlclient-dev
CentOS下出错,可以尝试执行:
sudo yum install python-devel mysql-devel
然后我们就可以着手写我们的pipeline了,在pipelines.py中:
import MySQLdb
class MysqlPipeline(object):
"""
采用同步的机制写入mysql
"""
def __init__(self):
self.conn = MySQLdb.connect(
'127.0.0.1', # HOST
'root', # USER
'', # PASSWORD
'Spider', # DB_NAME
charset="utf8",
use_unicode=True)
self.cursor = self.conn.cursor()
def process_item(self, item, spider):
insert_sql = """
insert into cnblogs_article(url_id, title, create_date, url, front_image_url, front_image_path, content, tags, source)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
"""
self.cursor.execute(insert_sql, (item["url_id"],
item["title"],
item["create_date"],
item["url"],
item["front_image_url"][0],
item["front_image_path"][0],
item["content"],
item["tags"],
item["source"]))
self.conn.commit()
return item
然后配置这个pipeline:
ITEM_PIPELINES = {
'Spider.pipelines.SpiderPipeline': 300,
# 'scrapy.pipelines.images.ImagesPipeline': 200,
'Spider.pipelines.ArticleImagesPipeline': 200,
# 'Spider.pipelines.JsonSavePipeline': 210,
'Spider.pipelines.JsonExporterPipleline': 220,
'Spider.pipelines.MysqlPipeline': 230,
}
由于上一节将create_date转为了date类型,自定义的JsonSavePipeline已经不能使用了,故而注释了。
先删除掉所有的数据,再启动项目,发现都是成功的——图片储存、json格式存储以及mysql存储。
但对于scrapy与mysql的交互,我们的学习还没有结束。因为spider的解析速度是大于mysql的入库速度的,随着item越来越多,mysql的插入速度是跟不上spider的解析速度的,就会产生堵塞。
为了解决这个问题,Twisted提供了一个叫做连接池的工具,它可以将mysql的插入变成一个异步的操作。
在实现它之前,我们把mysql相关的配置写在settings.py中:
MYSQL_HOST = '127.0.0.1'
MYSQL_DBNAME = 'Spider'
MYSQL_USER = 'root'
MYSQL_PASSWORD = ''
我们在pipelines.py中实现mysql插入的异步化:
from twisted.enterprise import adbapi
import MySQLdb.cursors
class MysqlTwistedPipline(object):
"""
采用异步的机制写入mysql
"""
def __init__(self, dbpool):
self.dbpool = dbpool
@classmethod
def from_settings(cls, settings):
dbparms = dict(
host = settings["MYSQL_HOST"],
db = settings["MYSQL_DBNAME"],
user = settings["MYSQL_USER"],
password = settings["MYSQL_PASSWORD"],
charset='utf8',
cursorclass=MySQLdb.cursors.DictCursor,
use_unicode=True,
)
dbpool = adbapi.ConnectionPool("MySQLdb", **dbparms)
return cls(dbpool)
def process_item(self, item, spider):
# 使用twisted将mysql插入变成异步执行
query = self.dbpool.runInteraction(self.do_insert, item)
# 处理异常
query.addErrback(self.handle_error, item, spider)
return item
def handle_error(self, failure, item, spider):
# 处理异步插入的异常
print(failure)
def do_insert(self, cursor, item):
# 执行具体的插入
insert_sql = """
insert into cnblogs_article(url_id, title, create_date, url, front_image_url, front_image_path, content, tags, source)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
"""
cursor.execute(insert_sql, (item["url_id"],
item["title"],
item["create_date"],
item["url"],
item["front_image_url"][0],
item["front_image_path"][0],
item["content"],
item["tags"],
item["source"]))
配置它:
ITEM_PIPELINES = {
'Spider.pipelines.SpiderPipeline': 300,
# 'scrapy.pipelines.images.ImagesPipeline': 200,
'Spider.pipelines.ArticleImagesPipeline': 200,
# 'Spider.pipelines.JsonSavePipeline': 210,
'Spider.pipelines.JsonExporterPipleline': 220,
# 'Spider.pipelines.MysqlPipeline': 230,
'Spider.pipelines.MysqlTwistedPipline': 230,
}
有以下说明:
from_settings中传入的cls实际上是MysqlTwistedPipline。
dbparms中键的名称是要与MySQLdb中指定的一致的。
Twisted本身不提供连接,只提供一个异步的容器,我们指定使用MySQLdb.cursors.DictCursor进行连接。
from_settings的用法我们可以在scrapy默认提供的ImagesPipeline中看到,它是会被spider调用的。我们可以看看settings的内容:
Twisted为关系型数据库提供了一种异步的操作。
scrapy有一个插件或者说第三方扩展,名为scrapy-djangoitem,它可以实现将item采用django的model的形式写入到数据库中。所进行的数据库操作都是采用了ORM机制,而不是原生的sql语句。(https://github.com/scrapy-plugins/scrapy-djangoitem)
最后需要说明的是,上面提供了两种方法将item数据存放到mysql。第一种同步方式很好理解,第二种异步方式有一定难度,但python基础好的人看懂是没有问题的。同时,第二种方式中,唯一会动态变化的内容就是具体的插入逻辑,其余的逻辑基本是不会变的。