Python 分布式爬虫框架 Scrapy 4-12 通过pipeline保存数据到mysql

为了实现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基础好的人看懂是没有问题的。同时,第二种方式中,唯一会动态变化的内容就是具体的插入逻辑,其余的逻辑基本是不会变的。

发布了101 篇原创文章 · 获赞 26 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/liujh_990807/article/details/100058528
今日推荐