Scrapy爬虫框架视频学习

Scrapy爬虫框架

1. 框架架构

1.1 Scrapy框架介绍

  • 写一个爬虫,需要做很多的事情。比如:发送网络请求、数据解析、数据存储、反反爬虫机制(更换ip代理、设置请求头等)、异步请求等。这些工作如果每次都要自己从零开始写的话,比较浪费时间。因此Scrapy把一些基础的东西封装好了,在他上面写爬虫可以变的更加的高效(爬取效率和开发效率)。因此真正在公司里,一些上了量的爬虫,都是使用Scrapy框架来解决。

1.2 Scrapy架构图

在这里插入图片描述

1.3 Scrapy框架模块功能

  • Scrapy Engine(引擎): 负责SpiderItemPipelineDownloaderScheduler中间的通讯,信号、数据传递等。
  • Scheduler(调度器): 它负责接受引擎发送过来的Request请求,并按照一定的方式进行整理排列,入队,当引擎需要时,交还给引擎
  • Downloader(下载器):负责下载Scrapy Engine(引擎)发送的所有Requests请求,并将其获取到的Responses交还给Scrapy Engine(引擎),由引擎交给Spider来处理,
  • Spider(爬虫):它负责处理所有Responses,从中分析提取数据,获取Item字段需要的数据,并将需要跟进的URL提交给引擎,再次进入Scheduler(调度器)
  • Item Pipeline(管道):它负责处理Spider中获取到的Item,并进行进行后期处理(详细分析、过滤、存储等)的地方.
  • Downloader Middlewares(下载中间件):你可以当作是一个可以自定义扩展下载功能的组件。
  • Spider Middlewares(Spider中间件):你可以理解为是一个可以自定扩展和操作引擎Spider中间通信的功能组件(比如进入Spider的Responses;和从Spider出去的Requests)

1.4 Scrapy的运作流程(容易理解的介绍)

  1. 引擎:Hi!Spider, 你要处理哪一个网站?
  2. Spider:老大要我处理xxxx.com
  3. 引擎:你把第一个需要处理的URL给我吧。
  4. Spider:给你,第一个URL是xxxxxxx.com
  5. 引擎:Hi!调度器,我这有request请求你帮我排序入队一下。
  6. 调度器:好的,正在处理你等一下。
  7. 引擎:Hi!调度器,把你处理好的request请求给我。
  8. 调度器:给你,这是我处理好的request
  9. 引擎:Hi!下载器,你按照老大的下载中间件的设置帮我下载一下这个request请求
  10. 下载器:好的!给你,这是下载好的东西。(如果失败:sorry,这个request下载失败了。然后引擎告诉调度器,这个request下载失败了,你记录一下,我们待会儿再下载)
  11. 引擎:Hi!Spider,这是下载好的东西,并且已经按照老大的下载中间件处理过了,你自己处理一下(注意!这儿responses默认是交给def parse()这个函数处理的)
  12. Spider:(处理完毕数据之后对于需要跟进的URL),Hi!引擎,我这里有两个结果,这个是我需要跟进的URL,还有这个是我获取到的Item数据。
  13. 引擎:Hi !管道 我这儿有个item你帮我处理一下!调度器!这是需要跟进URL你帮我处理下。然后从第四步开始循环,直到获取完老大需要全部信息。
  14. 管道``调度器:好的,现在就做!

2. Scrapy入门

2.1 创建项目

  • 要使用Scrapy框架创建项目,需要在命令行通过命令来创建。打开cmd进入到你想把这个项目存放的目录。然后使用以下命令scrapy startproject [项目名称]创建。

2.2 项目的目录结构

​ 创建完成以后,使用pycharm打开项目。
在这里插入图片描述

主要文件的作用:

  1. items.py:用来存放爬虫爬取下来数据的模型。
  2. middlewares.py:用来存放各种中间件的文件。
  3. pipelines.py:用来将items的模型存储到本地磁盘中。
  4. settings.py:本爬虫的一些配置信息(比如请求头、多久发送一次请求、ip代理池等)。
  5. scrapy.cfg:项目的配置文件。
  6. spiders包:以后所有的爬虫,都是存放到这个里面。

2.3 使用Scrapy框架爬取糗事百科段子

  • 创建项目:scrapy startproject qsbk

  • 进入qsbk目录中,使用命令创建一个爬虫:scrapy genspider qsbk_spider qiushibaike.com

    创建了一个名叫做qsbk_spider爬虫名不能和项目名称一样)的爬虫,并且能爬取的网页只会限制在qiushibaike.com这个域名下。

  • 爬虫代码解析:创建的qsbk_spider爬虫会在spiders目录中。

    在这里插入图片描述
    qsbk_spider.py

    # -*- coding: utf-8 -*-
    import scrapy
    
    class QsbkSpiderSpider(scrapy.Spider):
        name = 'qsbk_spider'
        allowed_domains = ['qiushibaike.com']
        start_urls = ['http://qiushibaike.com/']
    
        def parse(self, response):
            pass
    

其实这些代码我们完全可以自己手动去写,而不用命令。只不过是不用命令,自己写这些代码比较麻烦。
要创建一个Spider,那么必须自定义一个类,继承自scrapy.Spider,然后在这个类中定义三个属性和一个方法。

  1. name:这个爬虫的名字,名字必须是唯一的。
  2. allow_domains:允许的域名。爬虫只会爬取这个域名下的网页,其他不是这个域名下的网页会被自动忽略。
  3. start_urls:爬虫从这个变量中的url开始爬取。
  4. parse:引擎会把下载器下载回来的数据扔给爬虫解析,爬虫再把数据传给这个parse方法。这个是个固定的写法。这个方法的作用有两个,第一个是提取想要的数据。第二个是生成下一个请求的url
  • 修改settings.py代码:

    在做一个爬虫之前,一定要记得修改setttings.py中的设置。两个地方是强烈建议设置的。

    1. ROBOTSTXT_OBEY设置为False。默认是True。即遵守robots协议,那么在爬虫的时候,scrapy首先去找robots.txt文件,如果没有找到。则直接停止爬取。
    2. DEFAULT_REQUEST_HEADERS添加User-Agent
  • 完成的爬虫代码:

    items.py

    import scrapy
    
    class QsbkItem(scrapy.Item):
        # define the fields for your item here like:
        # name = scrapy.Field()
        author = scrapy.Field()
        content = scrapy.Field()
    

    pipelines.py :将数据保存到json文件中

    方式1

    import json
    
    class QsbkPipeline:
        def __init__(self):
            self.fp = open('duanzi.json','w',encoding='utf-8')
    
        def open_spider(self,spider):
            pass
    
        def process_item(self, item, spider)
        	#将item对象转换为字典,在转换为json字符串
            item_json = json.dumps(dict(item),ensure_ascii=False)
            self.fp.write(item_json+'\n')
            #pipeline可能有多个,如果不返回item,其他pipeline将不能获得item。
            return item
    
        def close_spider(self,spider):
            self.fp.close()
    

    保存json数据时,可以使用这两个类,让操作变得更简单。

    • JsonItemExporter:每次把数据添加到内存中,最后统一写入到磁盘中。好处是,存储的数据是一个满足json规则的数据。坏处是如果数据量比较大,那么比较耗内存。

    • JsonLinesItemExporter:每次调用export_item时就把item存储待硬盘中。坏处是每一个字典是一行,整个文件是一个不满足json格式的文件。好处是每次处理数据的时候就直接存储到了硬盘中,这样不会耗内存,数据也比较安全。

    方式2

    from scrapy.exporters import JsonItemExporter
    """
    这种方式先将每个item转换为字典,放到列表中。最后执行方法self.exporter.finish_exporting()统一将列表写到文件中去。
    """
    class QsbkPipeline:
        def __init__(self):
            #以二进制的方式打开文件
            self.fp = open('duanzi.json','wb')
            self.exporter = JsonItemExporter(self.fp,ensure_ascii=False,encoding='utf-8')
            self.exporter.start_exporting()
        
        def open_spider(self,spider):
            pass
    
        def process_item(self, item, spider):
            self.exporter.export_item(item)
            return item
    
        def close_spider(self,spider):
            self.exporter.finish_exporting()
            self.fp.close()
    

    方式3

    from scrapy.exporters import JsonLinesItemExporter
    
    class QsbkPipeline:
        def __init__(self):
            self.fp = open('duanzi.json','wb')
            self.exporter = JsonLinesItemExporter(self.fp,ensure_ascii=False,encoding='utf-8')
            
    	# open_spider:当爬虫被打开的时候执行。
        def open_spider(self,spider):
            pass
        
    	# process_item:当爬虫有item传过来的时候会被调用。
        def process_item(self, item, spider):
            self.exporter.export_item(item) #执行这个方法将item转换为字典写入到文件中
            return item
        
    	# close_spider:当爬虫关闭的时候会被调用。
        def close_spider(self,spider):
            self.fp.close()
    

    还要激活pipeline,才能使用。在setting.py中,设置ITEM_PIPLINES

    ITEM_PIPELINES = {
       'qsbk.pipelines.QsbkPipeline': 300,
    }
    

    qsbk_spider.py爬取多页的代码

    import scrapy
    from qsbk.items import QsbkItem
    
    class QsbkSpiderSpider(scrapy.Spider):
        name = 'qsbk_spider'
        allowed_domains = ['qiushibaike.com']
        start_urls = ['https://www.qiushibaike.com/text/']
    	
        """
        response是一个scrapy.http.response.html.HtmlResponse对象。可以执行xpath和css语法来提取数据。提取出来的数据是一个Selector或者是一个SelectorList对象,这两个对象有get和getall方法。getall方法:将每一个Selector对象转换为字符串,并放在列表中返回。get方法:将第一个Selector对象转换为字符串,直接返回。
        """
        def parse(self, response):
            duanzidivs = response.xpath("//div[@class='col1 old-style-col1']/div")
            #print("========")
            #print(duanzidivs.getall())#将SelectorList对象中的每个Selector转换为字符串,并放在列表中返回。
            #print("========")
            for duazidiv in duanzidivs:
                author = duazidiv.xpath(".//h2/text()").get().strip()
                content = duazidiv.xpath(".//div[@class='content']//text()").getall()
                content = "".join(content).strip()
                item = QsbkItem(author=author,content=content)
                yield item
            next_page_url = response.xpath("//ul[@class='pagination']/li[last()]/a/@href").get()
            #爬到最后一页时,next_page_url提取不到,为None,爬虫结束。
            if not next_page_url:
                return
            next_page_url = "https://www.qiushibaike.com" + next_page_url
            print(next_page_url)
            #请求下一页,当请求回来后执行callback指定的回调函数
            yield scrapy.Request(next_page_url, callback=self.parse)
    

    编写完爬取多页的代码后,在settings.py中设置下载延迟DOWNLOAD_DELAY = 1,每隔一秒请求一次

  • 运行scrapy项目:

    运行scrapy项目。需要在终端,进入项目所在的路径,然后scrapy crawl [爬虫名字]即可运行指定的爬虫。如果不想每次都在命令行中运行,那么可以把这个命令写在一个文件中。以后就在pycharm中执行运行这个文件就可以了。比如现在在项目的根目录下新创建一个文件叫做start.py,然后在这个文件中编写代码:

    from scrapy import cmdline
    
    cmdline.execute("scrapy crawl qsbk_spider".split())
    #cmdline.execute(['scrapy','crawl','qsbk_spider']) 上面的命令与这个等价
    

3. CrawlSpider

在糗事百科的爬虫案例中。我们是自己在解析完整个页面后获取下一页的url,然后重新发送一个请求。有时候我们想要这样做,只要满足某个条件的url,都给我进行爬取。那么这时候我们就可以通过CrawlSpider来帮我们完成了。CrawlSpider继承自Spider,只不过是在之前的基础之上增加了新的功能,可以定义爬取的url的规则,以后scrapy碰到满足条件的url都进行爬取,而不用手动的yield Request

3.1 创建CrawlSpider爬虫

之前创建爬虫的方式是通过scrapy genspider [爬虫名字] [域名]的方式创建的。如果想要创建CrawlSpider爬虫,那么应该进入你想要存放爬虫项目的目录,通过命令scrapy genspider -t crawl [爬虫名字] [域名]创建。

CrawlSpider需要使用LinkExtractors类和Rule类,实现对满足条件的url进行自动爬取。

3.2 LinkExtractors链接提取器

使用LinkExtractors可以不用程序员自己提取相应的url,然后发送请求。这些工作都可以交给LinkExtractors,它会在所有爬的页面中找到满足我们自己设置规则的url,实现自动的爬取。

创建该类的对象时需要的主要参数为allow

  • allow:允许的url。所有满足这个正则表达式的url都会被提取。

3.3 Rule规则类

定义爬虫的规则类。

创建该类的对象时需要的主要参数

  • link_extractor:一个LinkExtractor对象,用于定义爬取规则。
  • callback:满足这个规则的url,应该要执行哪个回调函数。
  • follow:指定根据该规则从response中提取的链接是否需要跟进。

3.4 微信小程序社区CrawlSpider案例

主要代码如下:

items.py

import scrapy


class WxappItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    title = scrapy.Field()
    author = scrapy.Field()
    public_time = scrapy.Field()
    content = scrapy.Field()

wxapp_spider.py

# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from wxapp.items import WxappItem

class WxappSpiderSpider(CrawlSpider):
    name = 'wxapp_spider'
    allowed_domains = ['wxapp-union.com']
    start_urls = ['http://www.wxapp-union.com/portal.php?mod=list&catid=2&page=1']
	"""
	这里定义了两个Rule对象,第一个Rule对象中的爬取规则为 .+mod=list&catid=2&page=\d 即将每一页的url提取出来,进行爬取。这里的 follow 参数传入的是True,即根据定义的规则提取的链接,将它下载下来后,继续从它的response中提取满足该规则的url。我们只需要从每一页中获取每一个教程的url即可,不需要解析该页面的内容,所以不需要callback参数。
	
	第二个Rule对象中的爬取规则为 .+article-.+\.html 即提取每一个教程的url,将相应url下载下来后,执行回调函数 parse_item 提取教程的详细信息。follow参数设置为False,即提取出的每一个教程的url,下载下来后不需要再根据规则 .+article-.+\.html 提取url,因为每一页根据规则 .+article-.+\.html 就能将所有的教程url检索出来。
	"""
    rules = (
        Rule(LinkExtractor(allow=r'.+mod=list&catid=2&page=\d'), follow=True),
        Rule(LinkExtractor(allow=r'.+article-.+\.html'),callback="parse_item",follow=False)
    )

    def parse_item(self, response):
        title = response.xpath("//h1[@class='ph']/text()").get()
        author = response.xpath("//p[@class='authors']/a/text()").get()
        public_time = response.xpath("//span[@class='time']/text()").get()
        content = response.xpath("//td[@id='article_content']//text()").getall()
        content = "".join(content).strip()
        item = WxappItem(title=title,author=author,public_time=public_time,content=content)
        yield item

pipelines.py

from scrapy.exporters import JsonLinesItemExporter

class WxappPipeline:
    def __init__(self):
        self.fp = open('wxjc.json','wb')
        self.exporter = JsonLinesItemExporter(self.fp,ensure_ascii=False,encoding='utf-8')

    def process_item(self, item, spider):
        self.exporter.export_item(item)
        return item

    def close_spider(self,spider):
        self.fp.close()

3.5 总结

定义的各个Rule规则类的检索条件将作用于每一个下载下来的urlresponse

Rule对象中什么情况下使用follow:如果在爬取页面的时候,需要将满足条件的url再进行跟进(即将满足条件的url下载下来后,继续从它的response中提取满足定义规则的url),那么就设置为True,否则设置为False

什么情况下该指定callback:如果这个url对应的页面,只是为了获取更多的url,并不需要提取里面的数据,那么可以不指定callback。如果想要获取url对应页面中的数据,那么就需要指定一个callback。

视频链接:https://www.bilibili.com/video/BV124411A7Ep?p=1
文章如有错误,请指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值