Scrapy_pipelines管道文件详细教程保存csv,Mysql,Mongodb以及多个item返回pipelines的处理

文章目录

  • piplines的使用
    • pipelines介绍
    • pipelines常用方法
    • pipelines注意点
  • 保存为csv,Mysql,Mongodb
  • 多个item 返回pipeline的处理

piplines的使用

pipelines介绍

管道文件 pipelines.py 主要用来对抓取的数据进行处理:一般一个类即为一个管道,比如创建存入MySQL、MongoDB 的管道类。管道文件中 process_item() 方法即为处理所抓数据的具体方法。

pipelines常用方法

process_item(self,item,spider):

处理爬虫抓取的具体数据,在 process_item() 函数中 必须要 return item,因为存在多管道时,会把此函数的返回值继续交由下一个管道继续处理;
init():爬虫项目启动时只执行一次,一般用于数据库连接;
close_spider():爬虫项目结束时只执行一次,一般用于收尾工作,如数据库的关闭。

pipelines注意点

  1. pipeline对应的值越小优先级越高
  2. pipeline中的process_item方法的名字不能够修改为其他的名称

保存为csv,Mysql,Mongodb

settings.py文件

关于settings具体设置点击查看 Scrapy_settings配置文件设置

# 管道文件设置,保存为哪个,就打开对应的管道,可以同时保存
ITEM_PIPELINES = {
   # 300是最后一个
   "douban.pipelines.DoubanPipeline": 300,
    # Mysql管道
   "douban.pipelines.MysqlDoubanPipeline": 299,
    # mongodb管道
   "douban.pipelines.MongodbDoubanPipeline": 298,
}

pipelines.py文件

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html


# useful for handling different item types with a single interface
import csv
import pymongo
import pymysql
from itemadapter import ItemAdapter

# 保存csv
class DoubanPipeline:
    def __init__(self):
        headers = ('title', 'score')
        self.f = open('douban.csv', 'w+', encoding='utf-8', newline='')
        self.f_csv = csv.DictWriter(self.f, headers)
        self.f_csv.writeheader()  # 写入表头

    def process_item(self, item, spider):
        self.f_csv.writerow(item)
        return item

    def close_spider(self, spider):
        self.f.close()
        print('douban.csv文件写入完成')

# 保存Mysql
class MysqlDoubanPipeline:
    def __init__(self):
        # 创建链接
        self.db = pymysql.Connect(
            host="127.0.0.1",
            port=3306,
            user="root",
            password="密码",
            db="数据库"
        )
        # 创建游标,用于传递python给MySQL的命令和MySQL返回的内容
        self.cursor = self.db.cursor()

        # 不想手动创建数据库,也可以在init方法中实现
        # sql_1 = """
        #     create table xiangxue009.douban_top250
        #     (
        #             `title` text null,
        #             `score` text null
        #     );
        #         """
        # self.cursor.execute(sql_1)

    def process_item(self, item, spider):
        # SQL插入语句
        sql = "insert into `douban_Top250`(`title`, `score`) values (%s,%s)"
        # 执行SQL语句
        self.cursor.execute(sql,[item['title'], item['score']])  # 放值
        # 提交到数据库执行
        self.db.commit()
        return item

    def close_spider(self, spider):
        # 关闭游标
        self.cursor.close()
        # 关闭连接
        self.db.close()
        print('Mysql文件写入完成')

# 保存Mongodb
class MongodbDoubanPipeline:
    # 初始化方法
    def __init__(self):
        # 连接mongo
        self.myclient = pymongo.MongoClient("mongodb://localhost:27017")
        
        # 连接mydb数据库,账号密码认证
        # self.myclient = self.myclient.admin  # 连接系统默认数据库admin
        # self.myclient.authenticate("用户名", "密码")
          """
        如果出错了,报错:pymongo.errors.OperationFailure: auth failed,可以增加mechanism参数,因为mongoDB有不同的认证机制
        3.0版本以后采用的是'SCRAM-SHA-1', 之前的版本采用的是'MONGODB-CR'
        即:self.myclient.authenticate("用户名", "密码",mechanism='MONGODB-CR') 
        """
            
        # 连接数据库,没有则自动创建
        self.db = self.myclient.xiangxue009

    def process_item(self, item, spider):
        # print(item)
        # 连接集合,没有集合则自动创建,连接后导入单条数据(类型为字典)
        self.db.douban_Top250.insert_one(dict(item))
        return item
    
    # spider一定要加在参数里,不然会报错
    def close_spider(self, spider):
        print('Mongo文件写入完成')
        print('爬虫结束!!')
        print('当前爬虫的名字',spider)  # 当前爬虫的名字


image-20231114085054214
关于spider参数
image-20231114085154690

多个item 返回pipeline的处理

我们有时候爬的数据不需要存在一张表里,但是如果需要爬两次,或者全部保存在一个item里在提取就不方便

所以我们会设置多个items,多个items的时候pipelines.py文件怎么保存呢

大致分为三步:

  1. 新建items类,创建管道
  2. 爬虫文件传输数据到对应的管道
  3. pipelines判断管道类型存入数据

items.py

# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html

import scrapy

# 第一个管道
class DoubanItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    # 创建标题字段
    title = scrapy.Field()
    # 创建评分字段
    score = scrapy.Field()

# 第二个管道
class DoubanItem2(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    # 创建标题字段
    abstract_name = scrapy.Field()
    # 创建导演字段
    abstract = scrapy.Field()

douban_data.py

import scrapy
# 导入两个管道
from ..items import DoubanItem,DoubanItem2

class DoubanDataSpider(scrapy.Spider):
    name = "douban_data"
    allowed_domains = ["www.douban.com"]
    # 把url换成我们需要爬取的
    start_urls = ["https://www.douban.com/doulist/124084417/?start=0&sort=seq&playable=0&sub_type="]

    # 爬虫函数 添加**kwargs就不会报黄
    def parse(self, response,**kwargs):
        # print(response)
        # print(type(response))
        # 获取25个大盒子
        data_divs = response.xpath('//*[@class="doulist-item"]')
        print(data_divs,len(data_divs))
        # 创建第一个管道实例
        items = DoubanItem()
        # 创建第二个管道实例
        items2 = DoubanItem2()
        for data in data_divs:
            # 电影标题
            title = ''.join(data.xpath('./div/div[2]/div[@class="title"]//text()').getall()).strip()
            # 电影评分
            score = ''.join(data.xpath('./div/div[2]/div[@class="rating"]//text()').getall()).replace('\n','').strip().replace(' ','')
            # 电影导演
            abstract = '-'.join(data.xpath('./div/div[2]/div[@class="abstract"]//text()').getall()).replace('\n','').strip().replace(' ','')
            print(title)
            print(score)
            print(abstract)
            # 第一个管道
            items['title'] = title
            items['score'] = score
            # 第二个管道
            items2['abstract_name'] = title
            items2['abstract'] = abstract
            print('items',items)
            print('items2',items2)
            # 返回数据
            yield items
            # 可以yield 很多次
            yield items2

pipelines.py文件,核心文件

# 导入管道
import csv
# 导入管道,注意要带. 不然管道的类型就和item的数据类型不一样
from .items import DoubanItem,DoubanItem2

class DoubanPipeline:

    def __init__(self):
        # 第一个管道保存文件
        headers = ('title', 'score')
        self.f = open('douban.csv', 'w+', encoding='utf-8', newline='')
        self.f_csv = csv.DictWriter(self.f, headers)
        self.f_csv.writeheader()  # 写入表头

        # 第二个管道保存文件
        headers = ('abstract_name', 'abstract')
        self.f2 = open('douban2.csv', 'w+', encoding='utf-8', newline='')
        self.f_csv2 = csv.DictWriter(self.f2, headers)
        self.f_csv2.writeheader()  # 写入表头

    def process_item(self, item, spider):
        
        #### 核心代码
        # 判断是不是第一个管道
        if isinstance(item,DoubanItem):
            self.f_csv.writerow(item)
            # 是的话保存过后直接返回,告诉引擎保存好了,可以下一个了,就不用在执行elif了
            return item
        # 判断是不是第二个管道
        elif isinstance(item,DoubanItem2):
            self.f_csv2.writerow(item)
            # 是的话保存过后直接返回,告诉引擎保存好了,可以下一个了
            return item

    def close_spider(self, spider):
        # 关闭文件
        self.f.close()
        print('douban.csv文件写入完成')
        self.f2.close()
        print('douban2.csv文件写入完成')

pipelines.py文件,核心文件解读

    def process_item(self, item, spider):
        # 看他们的类型
        print(item,type(item))

        print(DoubanItem)
        print('isinstance(item,DoubanItem)',isinstance(item,DoubanItem))

        print(DoubanItem2)
        print('isinstance(item,DoubanItem2)',isinstance(item,DoubanItem2))

image-20231114122832513

可以看到 <class ‘douban.items.DoubanItem’>和<class ‘douban.items.DoubanItem2’>两个类型

所有我们只需要判断item的类型就行,将相应的管道存入相应的数据就行

比如第一个管道的数据存csv,第二个管道存Mysql,这样就只需要在item加一个判断即可

操作性可大了

后面还会更新图片管道和视频管道教程