美文网首页
Scrapy框架简介

Scrapy框架简介

作者: 咻咻咻滴赵大妞 | 来源:发表于2019-01-06 12:34 被阅读0次

    Scrapy架构图


    image.png
    image.png

    一、新建项目

    scrapy startproject myspider

    创建爬虫项目

    scrapy startproject jobboleproject

    新建爬虫文件

    scrapy genspider jobbole jobbole.com

    二、明确目标:

    1. 在items.py中添加字段
      根据目标网站分析需要提取的数据,在item.py文件中添加字段
      打开jobboleproject文件下的item.py文件
      可以通过创建一个 scrapy.Item 类, 并且定义类型为 scrapy.Field的类属性来定义一个Item(可以理解成类似于ORM的映射关系)。
      接下来,创建一个JobboleprojectItem 类,和构建item模型(model)。
    2. 制作爬虫
      打开 jobboleproject/spider目录里的 [jobbole.py]编写代码
    import scrapy
    class JobboleSpider(scrapy.Spider):
    
        name = 'jobbole'
        allowed_domains = ['jobbole.com']
        start_urls = ['http://blog.jobbole.com/all-posts/']
    
    def parse(self, response):
        pass
    
    • name = "" :这个爬虫的识别名称,必须是唯一的,在不同的爬虫必须定义不同的名字。
    • allow_domains = [] 是搜索的域名范围,也就是爬虫的约束区域,规定爬虫只爬取这个域名下的网页,不存在的URL会被忽略。
    • start_urls = () :爬取的URL元祖/列表。爬虫从这里开始抓取数据,所以,第一次下载的数据将会从这些urls开始。其他子URL将会从这些起始URL中继承性生成。
    • parse(self, response) :解析的方法,每个初始URL完成下载后将被调用,调用的时候传入从每一个URL传回的Response对象来作为唯一参数,主要作用如下:
      负责解析返回的网页数据(response.body),提取结构化数据(生成item)
      生成需要下一页的URL请求。
    1. 分析网站结构取数据,在parse方法中做数据的提取
    from jobboleproject.items import JobboleprojectItem
    
    1.获取图片和文章详情的链接
    def parse(self, response):
        
        # css选择器获取当前列表页面的所有的节点
        post_nodes = response.css("#archive .floated-thumb .post-thumb a")
    
        # 如果不是完整的域名 需要拼接完整 response.url + post_url
        # 获取到的URL可能不是一个域名,也可能是具体的文章需要使用parse函数from urllib import parse
        for post_node in post_nodes:
            image_url = post_node.css("img::attr(src)").extract_first("")
            post_url = post_node.css("::attr(href)").extract_first("")
            full_url = response.urljoin(post_url)
            #meta参数对应的是一个字典,用来传递数据
            yield scrapy.Request(url=full_url, meta={"front_image_url": image_url},
            callback=self.parse_detail)
    

    yield

    的作用就是把一个函数变成一个 generator(生成器),带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator,带有yeild的函数遇到yeild的时候就返回一个迭代值,下次迭代时, 代码从 yield 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行, 直到再次遇到 yield。

    通俗的讲就是:在一个函数中,程序执行到yield语句的时候,程序暂停,返回yield后面表达式的值,在下一次调用的时候,从yield语句暂停的地方继续执行,如此循环,直到函数执行完。

    Scrapy Item pipeline(管道文件)使用

    编写item pipeline,item pipiline组件是一个独立的Python类,其中process_item()方法必须实现:

    import something
    
    class SomethingPipeline(object):
        def __init__(self):
            # 可选实现,做参数初始化等
            # doing something
    
        def process_item(self, item, spider):
            # item (Item 对象) – 被爬取的item
            # spider (Spider 对象) – 爬取该item的spider
            # 这个方法必须实现,每个item pipeline组件都需要调用该方法,
            # 这个方法必须返回一个 Item 对象,被丢弃的item将不会被之后的pipeline组件所处理。
            return item
    
        def open_spider(self, spider):
            # spider (Spider 对象) – 被开启的spider
            # 可选实现,当spider被开启时,这个方法被调用。
    
        def close_spider(self, spider):
            # spider (Spider 对象) – 被关闭的spider
            # 可选实现,当spider被关闭时,这个方法被调用
    

    启用一个Item Pipeline组件 为了启用Item Pipeline组件,必须将它的类添加到 settings.py文件ITEM_PIPELINES 配置,(0-1000随意设置,数值越低,组件的优先级越高

    定制图片下载管道

    Scrapy提供了一个 item pipeline ,来下载属于某个特定项目的图片,这条管道,被称作图片管道,在 ImagesPipeline 类中实现,提供了一个方便并具有额外特性的方法,来下载并本地存储图片:

    Pillow 是用来生成缩略图,并将图片归一化为JPEG/RGB格式,因此为了使用图片管道,你需要安装这个库。

    使用图片管道 当使用 ImagesPipeline ,典型的工作流程如下所示:
    在settings.py中设置 IMAGES_STORE 设置为一个有效的文件夹,用来存储下载的图片

    IMAGES_STORE = '/xxx/xxx/xxx'

    from scrapy.contrib.pipeline.images import ImagesPipeline
    import scrapy
    from scrapy.exceptions import DropItem
    import os
    from scrapy.utils.project import get_project_settings
    
    #下载图片
    
    image_store = get_project_settings().get('IMAGES_STORE')
    
    class jobboleImagePipline(ImagesPipeline):
    
        def get_media_requests(self, item, info):
            #根据图片地址,构建请求
            yield scrapy.Request(item['coverImage'])
    
        def item_completed(self, results, item, info):
            print(results)
            #获取图片下载成功的请求路径
            image_paths = [x['path'] for ok, x in results if ok]
            #判断图片是否下载成功
            if not image_paths:
                #出现错误,例如没有图片,可以丢弃item
                raise DropItem("Item contains no images")
            else:
                #替换图片的名称,自定义图片名称
                os.rename(image_store+'/'+image_paths[0],
                        image_store+'/'+item['title']+'.jpg')
                #将图片地址赋值给item
                item['localImagePath'] = image_store+'/'+item['title']+'.jpg'
                return item
    

    爬虫数据持久化保存

    settings.py文件: 设置文件,在这里设置User-Agent,激活管道文件等...

    ITEM_PIPELINES = {
        'douban.pipelines.DoubanPipeline': 300,
    }
    
    MONGODB 主机名
    MONGODB_HOST = '127.0.0.1'
    MONGODB 端口号
    MONGODB_PORT= 27017
    数据库名称
    MONGODB_DBNAME = "Douban"
    存储数据的表名称
    MONGODB_SHEETNAME= "doubanmovies"
    

    MongDB插入数据

    import pymongo
    # from scrapy.utils.project import
    class ChinazprojectPipeline(object):
        def __init__(self,host,port,dbname):
            #创建mongdb数据库连接
            self.mongo_client = pymongo.MongoClient(
                host=host,port=port
            )
            #选择要操作的数据库
            self.db = self.mongo_client[dbname]
    
        @classmethod
        def from_crawler(cls,crawler):
            host = crawler.settings['MONGO_HOST']
            prot = crawler.settings['MONGO_PORT']
            db = crawler.settings['MONGO_DB']
    
            return cls(host,prot,db)
    
        def process_item(self, item, spider):
            """
            这个方法是必须实现的,爬虫文件中所有的item都会经过这个方法
            :param item: 爬虫文件传递过来的item对象
            :param spider: 爬虫文件实例化的对象
            :return:
            """
            #选择要操作的集合
            col_name = item.get_mongdb_collectionName()
            col = self.db[col_name]
            #插数据
            dict_data = dict(item)
    
            try:
                col.insert(dict_data)
                print('数据插入成功')
            except Exception as err:
                print('数据插入失败',err)
    
            return item
        def open_spider(self,spider):
            print(spider.name,'爬虫开始')
    
        def close_spider(self,spider):
            self.mongo_client.close()
            print(spider.name,'爬虫结束')
    

    Mysql数据库插入数据
    settings.py文件: 设置文件,在这里设置User-Agent,激活管道文件等...

    ITEM_PIPELINES = {
        'douban.pipelines.DoubanPipeline': 300,
    }
    
    #关于数据库的相关配置
    MYSQL_HOST = '127.0.0.1'
    MYSQL_PORT = 3306
    MYSQL_USER = ''
    MYSQL_PWD = ''
    MYSQL_DB = ''
    
    class ChinazprojectPipeline(object):
        def __init__(self):
            """
            初始化方法
            """
            # self.file = open('chinaz.json','a')
            # 创建数据库链接
            self.client = pymysql.Connect(
                '127.0.0.1', 'root', '1234', 'chinaz', 3306, charset='utf8'
            )
            # 创建游标
            self.cursor = self.client.cursor()
    
        def open_spider(self, spider):
            """
            爬虫启动的时候会调用一次
            :param spider:
            :return:
            """
            print('爬虫开启了')
    
        def process_item(self, item, spider):
            """
            这个方法是必须实现的,爬虫文件中所有的item都会经过这个方法
            :param item: 爬虫文件传递过来的item对象
            :param spider: 爬虫文件实例化的对象
            :return:
            """
            # 存储到本地json文件
            # import json
            # json_data = json.dumps(data_dict,ensure_ascii=False)
            # self.file.write(json_data+'\n')
            # if isinstance(item,ChinaprojectWebInfoItem):
            #     print('网站信息')
            #     tablename = 'webinfo'
            # elif isinstance(item,ChinazprojectTagItem):
            #     print('网站分类信息')
            #     tablename = 'tag'
    
            data_dict = dict(item)
    
            sql,data = item.get_insert_sql_data(data_dict)
            try:
                # self.cursor.execute(sql, list(data_dict.values()))
                # self.client.commit()
                self.cursor.execute(sql,data)
                self.client.commit()
            except Exception as err:
                self.client.rollback()
                print(err)
    
            # 如果有多个管道文件,一定要注意return item,否则下个管道无法接收
            print('经过了管道')
            return item
    
        def close_spider(self, spider):
            """
            爬虫结束的时候会调用一次
            :param spider:
            :return:
            """
            # self.file.close()
            self.cursor.close()
            self.client.close()
            print('爬虫结束')
    

    实现Mysql异步插入(数据量非常大时,采用异步插入)

    from twisted.enterprise import adbapi
    
    
    class ChinazprojectPipeline(object):
    
        def __init__(self,dbpool):
            self.dbpool = dbpool
    
    
        @classmethod
        def from_crawler(cls,crawler):
            db_parmars = {
                'host':crawler.settings['MYSQL_HOST'],
                'user':crawler.settings['MYSQL_USER'],
                'passwd':crawler.settings['MYSQL_PWD'],
                'db':crawler.settings['MYSQL_DB'],
                'port':crawler.settings['MYSQL_PORT'],
                'charset':crawler.settings['MYSQL_CHARSET']
            }
            dbpool = adbapi.ConnectionPool('pymysql',**db_parmars)
            return cls(dbpool)
    
        def process_item(self,item,spider):
            query = self.dbpool.runInteraction(
                self.insert_data_to_mysql,
                item
            )
            query.addErrback(
                self.insert_err,
                item
            )
            return item
    
        def insert_data_to_mysql(self,cursor,item):
            data_dict = dict(item)
            sql,data = item.get_insert_sql_data(data_dict)
            cursor.execute(sql,data)
    
    
        def insert_err(self,failure,item):
            print(failure,'插入失败')
    

    Scrapy Shell

    启动

    scrapy shell "http://hr.tencent.com/position.php?&start=0#a"
    
    scrapy shell -s USER_AGENT="Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36"  "http://hr.tencent.com/position.php?&start=0#a"
    

    相关文章

      网友评论

          本文标题:Scrapy框架简介

          本文链接:https://www.haomeiwen.com/subject/rgcsrqtx.html