• scrapy基础


    一、创建一个scrapy项目

    在开始爬取之前,我们首先要创建一个scrapy项目,在命令行输入一下命令即可创建。

    scrapy startproject xxx

     

    二、编写第一个scrapy蜘蛛

    创建第一个scrapy蜘蛛文件

    上面我们已经成功创建了一个scrapy 项目,在spiders目录下面,有一个scrapy 文档,下面来创造一只scrapy蜘蛛

     

    文件名这里我就取名为:cjj_spider.py  (保存在pachong/spiders目录下),已经成功创建了一个scrapy蜘蛛文件,我们要爬取哪个网站、爬取这个网站的神马数据,统统在这个文件里面编写。

     

     编写第一个蜘蛛

    首先介绍下scrapy遵守的规则:

    1、首先我们需要创建一个类,并继承scrapy的一个子类:scrapy.Spider  或者是其他蜘蛛类型,除了Spider还有很多牛X的蜘蛛类型;
    2、然后定义一个蜘蛛名,name=“”  后面我们运行的话需要用到;
    3、定义我们需要爬取的网址,没有网址蜘蛛肿么爬,所以这是必须的
    4、继承scrapy的一个方法:start_requests(self),这个方法的作用就是通过上面定义的链接去爬取页面,简单理解就是下载页面。

    import scrapy
    
    class test(scrapy.Spider): #需要继承scrapy.Spider类
        name = "pachong"  # 定义蜘蛛名
    
    def start_requests(self):  # 由此方法通过下面链接爬取页面
    
        # 定义爬取的链接
        urls = [
            'http://lab.scrapyd.cn/page/1/',
            'http://lab.scrapyd.cn/page/2/',
        ]
        for url in urls:
            yield scrapy.Request(url=url, callback=self.parse)  # 爬取到的页面如何处理?提交给parse方法处理
    
    def parse(self, response):
        '''
        start_requests已经爬取到页面,那如何提取我们想要的内容呢?那就可以在这个方法里面定义。
        这里的话,并木有定义,只是简单的把页面做了一个保存,并没有涉及提取我们想要的数据,后面会慢慢说到
        也就是用xpath、正则、或是css进行相应提取,这个例子就是让你看看scrapy运行的流程:
        1、定义链接;
        2、通过链接爬取(下载)页面;
        3、定义规则,然后提取数据;
        '''
    
        page = response.url.split("/")[-2]  # 根据上面的链接提取分页,如:/page/1/,提取到的就是:1
        filename = 'test-%s.html' % page  # 拼接文件名,如果是第一页,最终文件名便是:pachong-1.html
        with open(filename, 'wb') as f:  # python文件操作,不多说了;
            f.write(response.body)  # 刚才下载的页面去哪里了?response.body就代表了刚才下载的页面!
        self.log('保存文件: %s' % filename)  # 打个日志

    运行蜘蛛

    scrapy crawl  pachong

    我们一定要进入:pachong 这个目录,也就是我们创建的蜘蛛项目目录,以上命令才有效!还有crawl后面跟的是你类里面定义的蜘蛛名,也就是:name,并不是项目名、也不是类名,这些细节希注意!

    三、scrapy start_url(初始链接)简写

    上面的项目中还有另外一种玩法,也就是可以简化start_requests()这么一个方法,也就是我们可以把这个方法简化掉,然后把初始链接放在一个常量里面,对比一下:简化前,我们需要定义一个方法:start_requests(self),然后经过这个方法不断循环发送请求:

    def start_requests(self): 
            urls = [ 
                'http://lab.scrapyd.cn/page/1/',
                'http://lab.scrapyd.cn/page/2/',
            ]
            for url in urls:
                yield scrapy.Request(url=url, callback=self.parse) 

    简化后,以上的链接可以写在:start_urls这个常量里面,是不是省了好多事。

     start_urls = [  # 另外一种写法,无需定义start_requests方法
            'http://lab.scrapyd.cn/page/1/',
            'http://lab.scrapyd.cn/page/2/',
        ]

    但是用简化的方法,我们必须定义一个方法为:def parse(self, response),方法名一定是:parse,这样的话用简写的方式就能愉快的工作了
    完整代码如下:

    import scrapy
    
    class cjj(scrapy.Spider): #需要继承scrapy.Spider类
        name = "pachong" #蜘蛛名
    
        start_urls = [  
            'http://lab.scrapyd.cn/page/1/',
            'http://lab.scrapyd.cn/page/2/',
        ]
    
        def parse(self, response):
            page = response.url.split("/")[-2]
            filename = 'cjj-%s.html' % page
            with open(filename, 'wb') as f:
                f.write(response.body)
            self.log('保存文件: %s' % filename)

    四、scrapy调试工具:scrapy shell使用方法

    scrapy提取数据的几种方式:CSS、XPATH、RE(正则),验证scrapy到底有木有提取到数据的工具,其实说白了就是scrapy调试工具,如果木有它你根本不知道你写的规则到底有木有提取到数据,所以这个工具是个:刚需!其实也很简单,就是在命令行输入下面一行代码而已:

    scrapy shell http://lab.scrapyd.cn

    scrapy shell 固定格式,后面的话跟的是你要调试的页面,如果是百度就:

    scrapy shell http://www.baidu.com

    就这样一个格式,其实这段代码就是一个下载的过程,一执行这么一段代码scrapy就立马把我们相应链接的相应页面给拿到了。

    比如我们想提取http://www.baidu.com的  title,我们可以在  In[1]:  后面输入:response.css('title')   ,然后回车, 立马就得到如下结果:

     

    五、scrapy css选择器使用

    crapy的第一种数据提取工具:css提取工具的用法。

    那我们要提取那个数据呢?就提取:http://lab.scrapyd.cn 这个页面的title里面的数据,我们来看一下他的html结构:

    <!DOCTYPE HTML>
    <html class="no-js">
    <head>
        ……
        <meta name="applicable-device" content="pc,mobile">
    
        <title>爬虫实验室 - SCRAPY中文网提供</title>
    
        ……

    我们要提取的就是上面:

    <title>爬虫实验室 - SCRAPY中文网提供</title>

    这个标签里面的数据,我们最终要得到的是:

    “爬虫实验室 - SCRAPY中文网提供”

    这么一段字符串,那我们就循序渐进的看看我们会怎么操作,会使用哪些函数。
    首先我们需要在命令行输入:

    scrapy shell http://lab.scrapyd.cn

    然后我们继续在命令行输入如下命令:response.css('title') ,这个格式是scrapy固定的格式照着写就行了;response.css('标签名'),标签名的话可以是html标签比如:title、body、div,也可以是你自定义的class标签,这里的话先看我们提取一下简单的,后面我们会讲解如何提取复杂的;
    那当我们输入以上命令之后,你会发现已经很给力的提取了一些数据:

    >>> response.css('title')
    
     [<Selector xpath='descendant-or-self::title' data='<title>爬虫实验室 - S
    
    CRAPY中文网提供</title>'>]

    那你会发现,我们使用这个命令提取的一个Selector的列表,并不是我们想要的数据;那我们再使用scrapy给我们准备的一些函数来进一步提取,那我们改变一下上面的写法,输入:

    >>> response.css('title').extract()
    
     ['<title>爬虫实验室 - SCRAPY中文网提供</title>']

    我们只是在后面加入了:extract() 这么一个函数你就提取到了我们标签的一个列表,更近一步了,那如果我们不要列表,只要title这个标签,要怎么处理呢,看我们的输入:

    >>>  response.css('title').extract()[0]
    
     '<title>爬虫实验室 - SCRAPY中文网提供</title>'

    这里的话,我们只需要在后面添加:[0],那代表提取这个列表中的第一个元素,那就得到了我们的title字符串;这里的话scrapy也给我提供了另外一个函数,可以这样来写,一样的效果:

    >>>  response.css('title').extract_first()
    
     '<title>爬虫实验室 - SCRAPY中文网提供</title>'

    extract_first()就代表提取第一个元素,和我们的:[0],一样的效果,只是更简洁些,至此我们已经成功提取到了我们的title,但是你会发现,肿么多了一个title标签,这并不是你需要的,那要肿么办呢,我们可以继续改变一下以上的输入:

    >>> response.css('title::text').extract_first()
    
    '爬虫实验室 - SCRAPY中文网提供'

    我们在title后面加上了 ::text ,这代表提取标签里面的数据,至此,我们已经成功提取到了我们需要的数据:

    '爬虫实验室 - SCRAPY中文网提供'

    总结一下,其实就这么一段代码:

    response.css('title::text').extract_first()

    六、scrapy提取一组数据

    上面的话我们只是在命令行使用,那经过我们的scrapy shell验证正确之后,我们怎么把它写进我们的蜘蛛文件?怎么让它提取数据?那接下来我们来看一下具体的操作。网站:http://lab.scrapyd.cn,们先试着提取里面一条名言的信息,然后进一步提取多条;那一条名言其实包含这么几个部分:名言、作者、标签,如下图:

     我们打开源代码,再来看一下对应的HTML标签:

    <div class="quote post">
        <span class="text">
            各种时代,各国诗人,个抓个的痒。scrapy中文网(http://www.scrapyd.cn)整理
        </span>
        <span>
            作者:<small class="author">木心</small>
            <a href="http://lab.scrapyd.cn/archives/29.html">【详情】</a>
        </span>
        <p></p>
        <div class="tags">
            标签 :
            <a href="http://lab.scrapyd.cn/tag/木心/">木心</a><a href="http://lab.scrapyd.cn/tag/艺术/">艺术</a>  
        </div>
    </div>

    那我们要爬取的标签就是:
    名言,对应着 class=“text” 标签里面的内容;
    作者,对应着 class=“authou” 里面的内容;
    标签,对应着 class=“tags” 里面的内容;
    因为我们提取的是第一段名言里面的数据,所以我们需要先找到第一段名言,然后保存在一个变量里面,再进一步提取里面的以上数据,那我们要怎么找到第一段名言呢,我们先来看一下HTML结构:

    <div class="quote post">
        ……
    </div>
    
    <div class="quote post">
        ……
    </div>
    
    <div class="quote post">
        ……
    </div>
    ……

    我们可以看到,每一段名言都被一个 <div class="quote post">……</div> 包裹,那如果我们要找到第一段名言我们可以这样写:

     mingyan1 = response.css('div.quote')[0]

    这样的话,我们就把第一段名言保存在:mingyan1  这么一个变量里面了。为什么会有一个:[0] 这表示提取第一段,如果没有这个限制,那我们提取的是本页所有名言。接下来我们就可以来提取里面的:名言内容、作者、标签了。首先提取名言内容,可以这样写:

    >>> mingyan1.css('.text::text').extract_first()

     '各种时代,各国诗人,个抓个的痒。scrapy中文网(http://www.scrapyd.cn)

    整理'

    好了,这样我们已经得到了第一段里面的名言内容,上面的表达式里面,我们使用了:.text   这是class选择器,如果是id选择器的话:#text 这些都是HTML的只是,不多说。那接下来我们提取作者:

     >>> mingyan1.css('.author::text').extract_first()
    
     '木心'

    用的还是class选择器!接下来我们提取标签:

     >>> mingyan1.css('.tags .tag::text').extract()
    
     ['木心', '艺术']

    这里的话,大家可以发现我们用的并非是.extract_first()  而是 extract(),why?应为里面有多个标签,我们并非只是提取一个,而是要把所有标签都提取出来,因此就用了:.extract() 
    好了,所有内容都已经在scrapy shell 里面验证通过了,那接下来我们把它合并到我们蜘蛛里面,代码如下:

    import scrapy
    
    class itemSpider(scrapy.Spider):
        name = 'itemSpider'
        start_urls = ['http://lab.scrapyd.cn']
    
        def parse(self, response):
            mingyan = response.css('div.quote')[0]
    
            text = mingyan.css('.text::text').extract_first()  # 提取名言
            autor = mingyan.css('.author::text').extract_first()  # 提取作者
            tags = mingyan.css('.tags .tag::text').extract()  # 提取标签
            tags = ','.join(tags)  # 数组转换为字符串
    
            fileName = '%s-语录.txt' % autor  # 爬取的内容存入文件,文件名为:作者-语录.txt
            f = open(fileName, "a+")  # 追加写入文件
            f.write(text)  # 写入名言内容
            f.write('
    ')  # 换行
            f.write('标签:'+tags)  # 写入标签
            f.close()  # 关闭文件操作

    七、scrapy 爬取多条数据(scrapy 列表爬取)

    我们要爬取多条,也就是把首页里面的都爬了!我们把首页里面,每一个作家的名言都爬取了保存在一个txt里面,然后再用他的名字命名文件。

     

    这里的话,新的知识点木有什么,唯一多了个递归调用,我们来看一下关键变化,原先我们取出一条数据,用的是如下表达式:

    mingyan = response.css('div.quote')[0]

    我们在后面添加了游标 [0]  表示只取出第一条,那我们要取出全部,那我们就不用加了,直接:

    mingyan = response.css('div.quote')

    那现在的变量就是一个数据集,里面有多条数据了,那接下来我们要做的就是循环取出数据集里面的每一条数据,那我们看一下怎么做:

    mingyan = response.css('div.quote')  # 提取首页所有名言,保存至变量mingyan
    
            for v in mingyan:  # 循环获取每一条名言里面的:名言内容、作者、标签
    
                text = v.css('.text::text').extract_first()  # 提取名言
                autor = v.css('.author::text').extract_first()  # 提取作者
                tags = v.css('.tags .tag::text').extract()  # 提取标签
                tags = ','.join(tags)  # 数组转换为字符串
                # 接下来,进行保存

    好了,可以看到,关键是:

     for v in mingyan:

    表示把 mingyan 这个数据集里面的数据,循环赋值给:v ,第一次循环的话 v 就代表第一条数据,那      text = v.css('.text::text').extract_first()        就代表第一条数据的名言内容,以此类推,把所有数据都取了出来,最终进行保存,我们看一下完整的代码:

    import scrapy
    
    class itemSpider(scrapy.Spider):
    
        name = 'listSpider'
    
        start_urls = ['http://lab.scrapyd.cn']
    
        def parse(self, response):
            mingyan = response.css('div.quote')  # 提取首页所有名言,保存至变量mingyan
    
            for v in mingyan:  # 循环获取每一条名言里面的:名言内容、作者、标签
    
                text = v.css('.text::text').extract_first()  # 提取名言
                autor = v.css('.author::text').extract_first()  # 提取作者
                tags = v.css('.tags .tag::text').extract()  # 提取标签
                tags = ','.join(tags)  # 数组转换为字符串
    
                """
                接下来进行写文件操作,每个名人的名言储存在一个txt文档里面
                """
                fileName = '%s-语录.txt' % autor  # 定义文件名,如:木心-语录.txt
    
                with open(fileName, "a+") as f:  # 不同人的名言保存在不同的txt文档,“a+”以追加的形式
                    f.write(text)
                    f.write('
    ')  # ‘
    ’ 表示换行
                    f.write('标签:' + tags)
                    f.write('
    -------
    ')
                    f.close()

    八、scrapy 爬取下一页,scrapy整站爬取

    接下来,还是继续爬取:http://lab.scrapyd.cn (链接独白:为神马受伤的总是我?)!我们既然要爬取下一页,那我们首先要分析链接格式,找到下一页的链接,那爬取就简单了,我们先来看看,下一页链接长什么样?

     

    可以看到,下一页的链接如下:

    <li class="next">
        <a href="http://lab.scrapyd.cn/page/2/">下一页 »</a>
    </li>

    那到底如何让蜘蛛自动的判断、并爬取下一页、下一页的内容呢?我们可以这样来做,我们每爬一页就用css选择器来查询,是否存在下一页链接,存在:则爬取下一页链接:http://lab.scrapyd.cn/page/*/,然后把下一页链接提交给当前爬取的函数,继续爬取,继续查找下一页,知道找不到下一页,说明所有页面已经爬完,那结束爬虫。我们来看一下简单的流程图:

    爬取内容的代码和上一文档(listSpider)一模一样,唯一区别的是这么一个地方,我们在:listSpider 蜘蛛下面添加几段代码:

    next_page = response.css('li.next a::attr(href)').extract_first()  
            if next_page is not None: 
                next_page = response.urljoin(next_page)
                yield scrapy.Request(next_page, callback=self.parse)

    首先:我们使用:response.css('li.next a::attr(href)').extract_first()查看有木有存在下一页链接,如果存在的话,我们使用:urljoin(next_page)把相对路径,如:page/1转换为绝对路径,其实也就是加上网站域名,如:http://lab.scrapyd.cn/page/1;接下来就是爬取下一页或是内容页的秘诀所在,scrapy给我们提供了这么一个方法:scrapy.Request()  这个方法还有许多参数,后面我们慢慢说,这里我们只使用了两个参数,一个是:我们继续爬取的链接(next_page),这里是下一页链接,当然也可以是内容页;另一个是:我们要把链接提交给哪一个函数爬取,这里是parse函数,也就是本函数;当然,我们也可以在下面另写一个函数,比如:内容页,专门处理内容页的数据。经过这么一个函数,下一页链接又提交给了parse,那就可以不断的爬取了,直到不存在下一页。

    九、scrapy arguments:指定蜘蛛参数爬取

    我们要爬取http://lab.scrapyd.cn里面的数据,原先我们需要全站的,于是我们写呀、写呀,终于写了一个全站爬虫(其实就是上一文档的内容即可全站爬取);过了几天需求变了,我们只需要:“人生”这个标签下面的内容,那我们又需要更改爬虫;又过了几天,需求又变,我们又需要标签:“励志”下面的内容,那我们又得改爬虫……最终出现下面的情况:

    for{
      又过了几天……需求又变……又改……
    }

    如果爬虫量少还好,那如果有十个、一百个……那一天到晚我们只能不断的修改、不断的伺候这些爬虫了,于是性生活都没有了!那怎样才能让我们的爬虫更灵活呢?scrapy给我提供了可传参的爬虫,有了这么个功能,那人生就更加美丽了,上面不断变化的爬虫我们就可以这样来玩,首先按scrapy 参数格式定义好参数,如下:

    def start_requests(self):
            url = 'http://lab.scrapyd.cn/'
            tag = getattr(self, 'tag', None)  # 获取tag值,也就是爬取时传过来的参数
            if tag is not None:  # 判断是否存在tag,若存在,重新构造url
                url = url + 'tag/' + tag  # 构造url若tag=爱情,url= "http://lab.scrapyd.cn/tag/爱情"
            yield scrapy.Request(url, self.parse)  # 发送请求爬取参数内容

    可以看到   tag = getattr(self, 'tag', None)  就是获取传过来的参数,然后根据不同的参数,构造不同的url,然后进行不同的爬取,经过这么一个处理,我们的蜘蛛就灰常的灵活了,我们来看一下完整代码:

    # -*- coding: utf-8 -*-
    
    import scrapy
    
    
    class ArgsspiderSpider(scrapy.Spider):
    
        name = "argsSpider"
    
        def start_requests(self):
            url = 'http://lab.scrapyd.cn/'
            tag = getattr(self, 'tag', None)  # 获取tag值,也就是爬取时传过来的参数
            if tag is not None:  # 判断是否存在tag,若存在,重新构造url
                url = url + 'tag/' + tag  # 构造url若tag=爱情,url= "http://lab.scrapyd.cn/tag/爱情"
            yield scrapy.Request(url, self.parse)  # 发送请求爬取参数内容
    
        """
        以下内容为上一讲知识,若不清楚具体细节,请查看上一讲!
        """
    
        def parse(self, response):
            mingyan = response.css('div.quote')
            for v in mingyan:
                text = v.css('.text::text').extract_first()
                tags = v.css('.tags .tag::text').extract()
                tags = ','.join(tags)
                fileName = '%s-语录.txt' % tags
                with open(fileName, "a+") as f:
                    f.write(text)
                    f.write('
    ')
                    f.write('标签:' + tags)
                    f.write('
    -------
    ')
                    f.close()
            next_page = response.css('li.next a::attr(href)').extract_first()
            if next_page is not None:
                next_page = response.urljoin(next_page)
                yield scrapy.Request(next_page, callback=self.parse)

    代码写好之后,那我们要如何传参呢?如何运行呢?比如我们要爬取标签:爱情,我们可以这样:

    scrapy crawl argsSpider -a tag=爱情

    要爬取标签:励志,我们可以这样:

    scrapy crawl argsSpider -a tag=励志

    参数:tag=爱情、tag=励志就可以在爬取的时候传进去我们蜘蛛里面,我们就可以不修改蜘蛛,愉快的爬取了。

     

    GitHub:

    https://github.com/chenjj9527/scrapyTest.git

    实战scrapy中文网存入mongodb:

    http://www.scrapyd.cn/jiaocheng/171.html

    实战Scrapy中文网存入MySQL:

    http://www.scrapyd.cn/jiaocheng/170.html

  • 相关阅读:
    FFmpeg笔记-基本使用
    记一次下载大文件存在数据异常问题排查
    从零开始编写IntelliJ IDEA插件
    Guava LoadingCache不能缓存null值
    FFmpeg笔记--vcodec和-c:v,-acodec和-c:a的区别?
    说说maven依赖冲突,依赖调解,依赖传递和依赖范围
    记一次NoHttpResponseException问题排查
    JVM源码分析-类加载场景实例分析
    JVM源码分析-JVM源码编译与调试
    理解PHP的变量,值与引用的关系
  • 原文地址:https://www.cnblogs.com/chen8023miss/p/12162218.html
Copyright © 2020-2023  润新知