zoukankan      html  css  js  c++  java
  • Scrapy框架全

    架构介绍

    Engine:引擎,处理整个系统的数据流处理,触发事物,是整个框架的核心

    Item:项目,他定义了爬取结果的数据结构,爬取的数据会被赋值成该Item对象

    Scheduler:调度器,接受引擎发过来的请求并将其加入到队列中,在引擎再次请求的时候将请求提供给引擎

    Downloader:下载器,下载网页内容,并将网页内容返回给spider

    Spider:蜘蛛,其内部定义了爬取的逻辑和网页的解析规则,他负责解析响应并生成提取结果和新的请求

    Item Pipline:项目管道,负责处理由蜘蛛从网页中抽取的项目,他的主要任务是清洗,验证和存储数据

    Downloader Middlewares:下载器中间件,位于引擎和下载器之间钩子框架,主要处理引擎和下载器之间的请求和响应

    Spider Middlewares:蜘蛛中间件,位于引擎和蜘蛛之间的钩子框架,主要处理蜘蛛输入的响应和输出的结果及新的请求

    数据流

    Scrapy中的数据流由引擎控制,数据流的过程如下:

    1.Engine首先打开一个网站,找到处理该网站的Spider,并向该Spider请求第一个要爬取的URL.

    2.Engine从Spider中获取到第一个要爬取的URL,并通过Scheduler以Request的形式调度.

    3.Engine向Scheduler请求下一个要爬取的URL

    4.Scheduler返回下一个要爬取的URL给Engine,Engine将URL通过Downloader Middlewares转发给Downloader下载

    5.一旦页面下载完毕,Downloader生成该页面的Response,并将其通过Downloader Middlewares发送给Engine

    6.Engine从下载器中接收到Response,并将其通过Spider Middlewares发送给 Spider处理

    7.Spider处理Response,并返回爬取到的Item及新的Request给Engine

    8.Engine将Spider返回的Item给Item Pipline,将新的Request给Scheduler

    9.重复2-8,知道Scheduler中没有更多的Request,Engine关闭该网站,爬取结束

    通过多个组件的相互协作,不同组件完成工作的不同,组件对异步处理的支持,Scrapy最大限度的利用了网络带宽,大大提高了数据爬取和处理的效率

     雀氏纸尿裤

    创建项目:

    scrapy startproject 项目名(可以在几乎任意文件夹下创建,创建的项目就在该文件夹下)在命令行中执行

    2:创建Spider:

    Spider是自己定义的类,Scrapy用它从网页中抓取内容,并解析抓取的结果.不过这个类必须继承Scrapy提供的Spider类scrapy.Spider,还要定义Spider的名称和起始请求,以及怎样处理爬取结果的方法:(不如命令行方便)

    也可以使用命令行创建一个Spider,比如要生成sb这个spider,(示例爬取校花网图片)

    执行如下命令:

    cd 项目名

    scrapy genspider sb news.daxues.cn

    进入刚才创建的项目文件夹,然后执行上诉命令,第一个参数是Spider的名称,第二个参数是网站域名,执行完毕之后,spiders文件夹中就多了一个sb.py,他就是刚刚创建的Spider,内容如下:

    import scrapy

    class SbSpider(scrapy.Spider):
    name = 'sb'
    allowed_domains = ['news.daxues.cn']
    start_urls = ['http://news.daxues.cn/xiaohua/ziliao/'](这里有所改动xiaohua/ziliao/为后来添加的方便爬取)
    def parse(self, response):
    pass

    这里有三个属性--name,allowed_domains和start_urls,还有一个parse方法

    name:他是项目中唯一的名字,用于区分不同Spider

    allowed_domains,他是允许爬取的域名,如果初始或后续的请求链接不是这个域名下的,则请求链接会被过滤掉

    start_urls:它包含了Spider在启动时爬取的url列表,初始请求是由他定义的

    parse:他是Spider的一个方法,默认情况下,被调用时start_url里面的链接构成的请求完成下载执行后,返回的响应就会作为唯一的参数传递给这个函数.该方法负责解析返回的响应,提取数据或者进一步生成要处理的请求.

     3.创建Item

    Item是保存爬取数据的容器,他的使用方法和字典类似,创建Item需要继承scrapy.Item类,并且定义类型为scrapy.Field的字段

    定义Item,此时将items.py修改如下:

    class NewsItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    name = scrapy.Field()
    info = scrapy.Field()
    img = scrapy.Field()

    这里定义了三个字段,接下来的爬取将会使用这个Item

    4.解析Response

    parse()方法的参数response是start_urls里面的链接爬取后的结果,所以在parse()方法中.我们可以直接对response变量包含的内容进行解析,比如浏览请求结果的源代码,或者进一步分析源代码内容,或找出结果中的链接,得到下一个请求

    有时网页中既有我们想要的结果又有下一页的链接,都需要进行处理.:

    可以在浏览器上查看网页结构:

    在类名为xh_list的div下有多个dl标签,里面包含我们想要的数据

    提取内容的方式可以是CSS选择器或Xpath选择器,这里使用CSS选择器进行选择

    parse()方法改写如下:

    class SbSpider(scrapy.Spider):
    name = 'sb'
    allowed_domains = ['news.daxues.cn']
    start_urls = ['http://news.daxues.cn/xiaohua/ziliao/']
    def parse(self, response):
    div_list = response.css('.xh_list').css('dl')
    url = 'https://news.daxues.cn'
    for div in div_list:
    name = div.css('dt').css('a::text').extract_first()
    info = div.css('.d::text').extract_first()
    img = url+div.css('img::src').extract_first()

    5.使用Item

    上文定义Item,接下来就要使用它.Item可以理解为一个字典,不过在声明的时候需要实例化,然后依次用刚才解析的结果赋值Item的每一个字段,最后将Item返回即可

    SbSpider的改写如下所示:

    import scrapy

    from news.items import NewsItem
    class SbSpider(scrapy.Spider):
    name = 'sb'
    allowed_domains = ['news.daxues.cn']
    start_urls = ['http://news.daxues.cn/xiaohua/ziliao/']
    def parse(self, response):
    div_list = response.css('.xh_list').css('dl')
    url = 'https://news.daxues.cn'
    for div in div_list:
    item = NewsItem()
    item['name'] = div.css('dt').css('a::text').extract_first()
    item['info'] = div.css('.d::text').extract_first()
    item['img'] = url+div.css('img::attr(src)').extract_first()
    yield item

    如此一来,首页的所有内容被解析出来,并被赋值成了一个个NewsItem

    6.后续Request

    上面的操作只实现了从初始页面抓取内容,那么下一页的内容该如何抓取,这需要我们从当前页面中找到信息来生成下一个请求,然后在下一个请求的页面里找到信息再构造再下一个请求,这样循环往复迭代,从而实现整站的爬取.

    将页面拉到底部,这里有一个下一页按钮,查看他的源代码发现,他的链接是/xiaohua/ziliao/index_2.html,全链接就是:

    http://news.daxues.cn/xiaohua/ziliao/index_2.html

    通过这个链接我们就可以构造下一个请求.

    构造请求时需要用到scrapy.Request.这里我们传入两个参数---url和callback,这两个参数的说明如下.

    url:他是请求链接

    callback:它是回调函数,当指定了该回调函数的请求完成之后,获取到响应,引擎会将该响应作为参数传递给这个回调函数.回调函数进行解析或生成下一个请求,回调函数如上文的parse()所示:

    由于parse()就是解析name,info,img的方法,而下一页的结构和刚才已经解析的页面结构是一样的,所以我们可以再次使用parse(0方法来做页面解析.

    接下来就是利用选择器得到下一页链接并生成请求,在parse()方法后追加如下的代码:

    next = response.css('.page a::attr(href)').extract_first()(选择器语法可能不正确,后续需要改动)
    url2 = url+next
    yield scrapy.Request(url=url2,callback=self.parse)

    第一句代码首先通过CSS选择器获取下一个链接,,即要获取a超链接中的href属性.这里用到了::attr(href)操作.然后再调用extract_first()方法获取内容

    第二句代码生成一个url路径

    第三句代码通过url和callback变量构造了一个新的请求,回调函数callback依然使用parse()方法,这个请求完成后,响应会重新经过parse方法处理,得到第二页的解析结果,然后生成第二页的下一页,也就是第三页的请求,这样爬虫就进入了一个循环,直到最后一页.

    改写后的整个Spider类如下所示:

    import scrapy

    from news.items import NewsItem
    class SbSpider(scrapy.Spider):
    name = 'sb'
    allowed_domains = ['news.daxues.cn']
    start_urls = ['http://news.daxues.cn/xiaohua/ziliao/']
    def parse(self, response):
    div_list = response.css('.xh_list').css('dl')
    url = 'https://news.daxues.cn'
    for div in div_list:
    item = NewsItem()
    item['name'] = div.css('dt').css('a::text').extract_first()
    item['info'] = div.css('.d::text').extract_first()
    item['img'] = url+div.css('img::attr(src)').extract_first()
    yield item
    next = response.css('.page a::attr(href)').extract_first()
    url2 = url+next
    yield scrapy.Request(url=url2,callback=self.parse)

    7运行:

    接下来进入目录,运行如下命令:

    scrapy crawl news

    就可以看到运行结果了

    首先Scrapy输出了当前的版本号以及正在启动的项目名称,接着输出了当前settings.py中一些重写后的配置,然后输出了当前所应用的Middlewares和Piplines.Middlewares默认是启用的,可以在settings中修改,Piplines默认为空,同样可以在settings.py中配置.

    接下来就是整个页面的抓取结果,可以看到爬虫一边解析,一边翻页,直至将所有内容抓取完毕,然后终止.

    最后Scrapy输出了整个抓取过程的统计信息,如请求的字节数,请求次数,响应次数,完成原因等等

    整个Scrapy程序成功运行.

    由于本人对于Scrapy的CSS选择器不熟悉,因此除了点bug,但问题不大

    8保存到文件

    Scrapy提供的Feed Exports可以轻松的抓取结果输出

    如果想要将输出结果保存成json文件,可以执行以下命令:

    scrapy crawl sb -o sb.jl

    scrapy crawl sb -o sb.jsonlines

    输出格式还支持很多种,例如csv,xml,pickle,marshal等,还支持ftp,s3等远程输出,另外还可以通过自定义ItemExporter来实现其他的输出.

    例如下面的命令对应的输出分别为csv,xml,,pickle,marshal格式以及ftp远程输出:

    scrapy crawl sb -o sb.csv

    scrapy crawl sb -o sb.xml

    scrapy crawl sb -o sb.pickle

    scrapy crawl sb -o sb.marshal

    scrapy crawl sb -o ftp://user:pass@ftp.example.com/path/to/sb.csv

    其中,ftp输出需要正确配置用户名,密码,地址,输出路径,否则会报错

    通过Scrapy提供的Feed Exports我们可以轻松的输出抓取结果到文件,如果想要更复杂的输出,我们可以使用Item Pipline来完成

    9使用Item Pipeline 

    如果想进行更复杂的操作,如将结果保存到MongoDB数据库,或者筛选某些有用的Item,我们可以定义Item Pipeline来实现

    Item Pipline为项目管道,当Item生成后,他会自动被送到Item Pipline进行处理,我们常用Item Pipline来做如下操作:

    清理HTML数据

    验证爬取数据,检查爬取字段

    查重并丢弃重复内容

    将爬取结果保存到数据库.

    要实现Item Pipeline很简单只需要定义一个类并实现process_item()方法即可,启用Item Pipeline后,Item Pipeline会自动调用这个方法,process_item()方法必须返回包含数据的字典或Item对象,或者抛出DropItem异常.

    process_item()方法有两个参数,一个参数是item,每次Spider生成的Item都会作为参数传递过来,另一个参数是spider,就是Spider的实例.

    接下来实现一个Item Pipeline,筛掉info长度大于200的Item,并将结果保存到MongoDB.

    修改项目里的pipelines.py文件,之前用命令行自动生成的文件内容可以删掉,增加一个TextPipeline类,内容如下所示:

    from scrapy.exceptions import DropItem
    class InfoPipeline():
    def __init__(self):
    self.limit = 200
    def process_item(self,item,spider):
    if item['info']:
    if len(item['info']) > self.limit:
    item['info'] = item['info'][0:self.limit].rstrip()+'...'
    return item
    else:
    return DropItem('Missing Info')

    这段代码在构造方法里定义了限制长度为200,实现了process_item()方法,其参数是item和spider,首先该方法判断item的info属性是否存在,不存在则抛出DropItem异常,存在则判断是否大于200,大于则删减,然后返回item

    接下来将处理后的item存入MongoDB,定义另外一个Pipeline,同样在pipelines.py中,我们实现零一个类MongoPipeline,内容如下所示:

    import pymongo
    class MongoPipeline():
    def __init__(self,mongo_uri,mongo_db):
    self.mongo_uri = mongo_uri
    self.mongo_db = mongo_db
    @classmethod
    def from_crawler(cls,crawler):
    return cls(mongo_uri=crawler.settings.get('MONGO_URI'),
    mongo_db=crawler.settings.get('MONGO_DB'))
    def open_spider(self,spider):
    self.client = pymongo.MongoClient(self.mongo_uri)
    self.db = self.client[self.mongo_db]
    def process_item(self,item,spider):
    name = item.__class__.__name__
    self.db[name].insert(dict(item))
    return item
    def close_spider(self,spider):
    self.client.close()

    MongoPipeline类实现了API定义的另外几个方法.

    from_crawler:他是一个类方法,用@classmethod标识,是一种依赖注入的方式,他的参数就是crawler,通过crawler我们可以拿到全局配置的每个配置信息,在全局配置settings.py中,我们定义MONGO_URI和MONGO_DB来指定MongoDB连接需要的地址和数据库名称,拿到配置信息后返回类对象即可,所以这个方法主要用于获取settings.py中的配置的

    open_spider:当Spider开启时,这个方法被调用,上文程序主要进行了一些初始化操作

    close_spider:当Spider关闭时,这个方法被调用,上文程序中将数据库连接关闭

    最主要的process_item()方法则执行了数据插入操作

    定义好InfoPipeline和MongoPipeline这两个类后,我们需要在settings.py中使用他们,MongoDB的连接信息还需要定义.

    我们在settings.py中加入如下内容:

    ITEM_PIPELINES={'sb.pipelines.InfoPipeline':300,

    'sb.pipelines.MongoPipeline':400,}

    MONGO_URI = 'localhost'

    MONGO_DB = 'sb'

    赋值ITEM_PIPELINES字典,键名是Pipeline的类名称,键值是调用优先级,是一个数字,数字越小则对应的Pipeline越先被调用.

    再重新爬取,命令如下所示:

    scrapy crawl sb

    爬取结束后,MongoDB中创建了一个news的数据库,sbitem的表,

  • 相关阅读:
    easyui里弹窗的两种表现形式
    如何获得 request, "request.getSession(true).setAttribute("a",a);"与“request.setAttribute("a",a);”区别
    JSTL和EL的区别
    windows下安装多个tomcat服务
    谷歌浏览器控制台使用
    递归和迭代有什么区别?
    left join ,right join ,inner join ,cross join 区别
    sql良好习惯
    添加了一个字段,查询速度突然变慢
    热点账户问题-转
  • 原文地址:https://www.cnblogs.com/suncunxu/p/10764365.html
Copyright © 2011-2022 走看看