第四天,同步和异常数据存储到mysql,item loader方法

 

github对应代码:伯乐在线文章爬取

 
 
一. 普通插入方法
1. 连接到我的阿里云,用户名是test1,然后在navicat中新建数据库
 
2. 修改爬虫文件中日期的类型为date类型,原先是string
 
3. 参照items.py在navicat中创建表
注意:
1)content类型是longtext,不用设置长度,但是不是null那里不能勾选,否则运行代码时报错说没设置默认值
2)如果fav_nums不为空,那么需要设一个默认值0
3)url_object_id经过md5编码后,长度是固定的,并且可设为主键
 
 
4. 虚拟环境中安装mysql驱动模块
pip install -i https://pypi.douban.com/simple/ pymysql
 
 
5. pipeline.py中定义,找5个字段测试下
 
说明:这里charset正常情况下应该为charset="utf-8",但是可能是因为content字段中有特殊字符,需要改为charset="utf8mb4",同时navicat中修改content字段属性如下,否则会报错
 
 
6. 解决pycharm中写sql语句时的2个显示问题,不影响正常运行
问题1:sql dialect is not configured
问题2:no data source are configured to run this sql
 
 
 
 
二. 使用twisted下的连接池,使mysql插入实现异步操作,解决爬取速度远大于数据存储速度,造成阻塞的问题。
 
1. 数据库的信息其实可以写在settings.py中
 
 
2. pipelines.py中编写
class MysqlTwistedPipeline(object):
    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"],
            passwd=settings["MYSQL_PASSWORD"],
            charset='utf8',
            cursorclass=pymysql.cursors.DictCursor,
            use_unicode=True,
        )
        dbpool = adbapi.ConnectionPool("pymysql", **dbparms)
        return cls(dbpool)
 
    def process_item(self, item, spider):
        query = self.dbpool.runInteraction(self.do_insert, item)
        query.addErrback(self.handle_error, item, spider)  # 处理异常
 
    # 处理异步插入的异常情况
    def handle_error(self, failure, item, spider):
        print(failure)
 
    # 执行具体插入
    def do_insert(self, cursor, item):
        insert_sql = """
            insert into article(title, url, create_date, fav_nums, content)
            VALUES (%s, %s, %s, %s, %s)
        """
        cursor.execute(insert_sql, (item["title"], item["url"], item["create_date"], item["fav_nums"], item["content"]))

 

 
说明:
1)adbapi可以把mysql的操作变成异步操作,from twisted.enterprise import adbapi
2)@classmethod下的函数作用是先把字符串进行处理后,在用类的构造函数初始化,参考https://blog.csdn.net/dyh4201/article/details/78336529
3)**也用来解包字典,上面就用了解包的功能,用一个简单示例说明一下
4)使用的时候,只须修改do_insert中的代码,上面其余的代码直接复制就好
 
 
3. 修改settings.py
把'Article.pipelines.MysqlPipeline': 1,   中的MysqlPipeline修改为MysqlTwistedPipeline
 
 
4. 其实可以把django的ORM集成到这里, 用于简化操作。 搜索scrapy djangoitem,找到scrapy-plugin/scrapy diangoitem。有时间自行测试学习
 
 
 
 
三. item loader
这种方法我觉得并不好用,没有直接提取出来字段看起来方便直观。如果想要jobbole.py看起来简洁写用这种方法,但是加大了items.py的代码量
 
1. 在jobbole.py的def parse_detail()函数中添加如下代码
 
from scrapy.loader import ItemLoader
原先的代码,除了front_image_url = response.meta.get("front_image_url", ""),其他都可以注释掉。
 
 
2. 调试发现2个问题
1)所有字段都默认为list类型
2)有些数据并没有提取出来,比如收藏数,提取出来的是[' 收藏'],还需要进一步处理
 
 
3. items.py中增加处理函数来进一步处理数据
3.1 想在title后增加一些自定义字段和修改提取时间类型为date
 
注意:
1)如果取出的title有多个值,那么会在每个值后面都加上"-jobbole"
2)MapCompose()可以有多个函数作为参数,会依次执行多个参数,如MapCompose(lambda x: x+"-jobbole", add_jobbole),得到的结果是在titile后面加上2个"-jobbole"字符串
 
 
4. 把默认生成的list结构改为取第一个元素,可以使用TakeFirst函数,比如在上面的基础上提取日期,修改如下
4.1 同样修改items.py
 
此时debug可以看到create_date的类型已经变成date类型,title后面加了-jobbole
 
 
4.2 如果想所有字段都提取第一个元素,每个字段内都加这行代码有点麻烦,可以在items.py中自定义ItemLoader
然后在jobbole.py中把加载item的ItemLoader类修改为自定义的ArticleItemLoader
这样默认所有字段都会取列表中的第一个值,类型为str
 
 
 
5. 修改收藏数
items.py中
debug验证
 
 
 
6. 修改tags
这个比较麻烦,我们只需要提取职场,面试字样,类型为字符串。使用下面方法得到的是有多个元素的list
>>> response.css("p.entry-meta-hide-on-mobile a::text").extract()
['职场', ' 9 评论 ', '面试'] 
如果想取得里面的元素,不能用之前的TakeFirst()方法,因为我们需要取得列表中有多个元素,那么可以使用scrapy中的Join方法
这样就可以取得上面各元素以逗号连接的字符串形式,但是我们需要去掉评论标签,怎么办呢?定义一个去掉评论字段的函数就好
 
 
 
7. front_image_url
由于这个字段我们必须用list的类型,所以不能用TakeFirst函数转为string类型,并且需要保持原数据不变
 
debug后发现front_image_url已变成list
 
同时可以完善一下pipiline.py中的ArticleImagePipeline,加一个判断条件。
 
 
posted @ 2018-09-14 14:08  坚强的小蚂蚁  阅读(438)  评论(0编辑  收藏  举报