Scrapy框架:入门介绍

Scrapy框架介绍

Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。其可以应用在数据挖掘,信息处理或存储历史数据等一系列的程序中。其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的,也可以应用在获取API所返回的数据(例如 Amazon Associates Web Services)或者通用的网络爬虫。
Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试。

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

Scrapy使用了Twisted异步网络库来处理网络通讯。

Scrapy整体架构图

在这里插入图片描述

Scrapy框架各个模块功能

Scrapy主要包括了以下组件:

  • 引擎(Scrapy)

    用来处理整个系统的数据流,触发事务(框架核心)

    Scrapy Engine(引擎):Scrapy框架的核心部分。负责在Spider和ItemPipeline、Downloader、Scheduler、中间通信、传递数据等

  • 爬虫(Spider)

    爬虫是主要干活的,用于从特定的网页中提取自己需要的信息,即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面

    Spider(爬虫):发送需要爬取的链接给引擎,最后引擎把其他模块请求回来的数据再发送给爬虫,爬虫就去解析想要的数据。这个部分是我们开发者自己写的,因为要爬取哪些链接,页面中的哪些数据是我们需要的,都是由程序员自己决定

  • 项目管道(Pipeline)

    负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据

    Item Pipeline(管道):负责将Spider(爬虫)传递过来的数据进行保存。具体保存在哪里,取决于开发者自己的需求

  • 下载器(Downloader)

    用于下载网页内容,并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)

    Downloader(下载器):负责接收引擎传过来的下载请求,然后去网络上下载对应的数据再交还给引擎

  • 调度器(Scheduler)

    用来接受引擎发过来的请求,压入队列中,并在引擎再次请求的时候返回。可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列,由它来决定下一个要抓取的网址是什么,同时去除重复的网址

    Scheduler(调度器):负责接收引擎发送过来的请求,并按照一定的方式进行排列和整理,负责调度请求的顺序等

  • 下载器中间件(Downloader Middlewares)

    位于Scrapy引擎和下载器之间的框架,主要是处理Scrapy引擎与下载器之间的请求及响应

    Downloader Middlewares(下载中间件):可以扩展下载器和引擎之间通信功能的中间件

  • 爬虫中间件(Spider Middlewares)

    介于Scrapy引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出

    Spider Middlewares(爬虫中间件):可以扩展引擎和爬虫之间通信功能的中间件

  • 调度中间件(Scheduler Middewares)

    介于Scrapy引擎和调度之间的中间件,从Scrapy引擎发送到调度的请求和响应

Scrapy运行流程大概如下:

  1. 引擎从调度器中取出一个链接(URL)用于接下来的抓取
  2. 引擎把URL封装成一个请求(Request)传给下载器
  3. 下载器把资源下载下来,并封装成应答包(Response)
  4. 爬虫解析Response
  5. 解析出实体(Item),则交给实体管道进行进一步的处理
  6. 解析出的是链接(URL),则把URL交给调度器等待抓取

安装和文档

Scrapy官方文档:http://doc.scrapy.org/en/latest
Scrapy中文文档:http://scrapy-chs.readthedocs.io/zh_CN/latest/index.html

安装:通过pip install scrapy即可安装

安装过程中经常会遇到错误,因为Scrapy需要很多其他依赖,直接使用pip install scrapy安装的时候,会同时安装上这些依赖,所以会遇到很多库安装失败的情况

在windows系统下,运行Scrapy项目时,如果提示ModuleNotFoundError: No module named 'win32api'这个错误,使用命令pip install pypiwin32,安装pypiwin32这个库即可
pypiwin32和pywin32的关系:
pypiwin32是pywin32的旧版经过重新封装的,安装pywin32通过命令pip install pywin32
相关地址:
https://sourceforge.net/projects/pywin32/
https://blog.csdn.net/chennudt/article/details/76726580

在ubuntu上安装scrapy之前,需要先安装以下依赖:
sudo apt-get install python3-dev build-essential python3-pip libxml2-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev
然后再通过pip install scrapy安装

各种错误解决办法:
https://blog.csdn.net/saucyj/article/details/79043443
https://www.cnblogs.com/baxianhua/p/8996715.html
https://blog.csdn.net/sinat_37986079/article/details/80166630
https://blog.csdn.net/weixin_42057852/article/details/80857948

一篇关于Scrapy的文章:
https://www.cnblogs.com/kongzhagen/p/6549053.html

快速入门

创建项目

Scrapy框架创建项目需要通过命令来创建,首先进入到你想把这个项目存放的目录,然后使用命令scrapy startproject [项目名称]创建

创建爬虫

创建项目后,在命令行中进入到项目所在的路径,使用命令scrapy genspider [爬虫名称] [域名]创建

注意:爬虫名称和项目名称不能一样

目录结构介绍

在命令行中创建爬虫项目后会生成对应的项目文件

在这里插入图片描述

主要文件的作用:

  1. scrapy.cfg:项目的配置文件,主要为Scrapy命令行工具提供一个基础的配置信息。(真正爬虫相关的配置信息在settings.py文件中)
  2. items.py:用来存放爬虫爬取下来数据的模型;设置数据存储模板,用于结构化数据,如:Django的Model
  3. middlewares.py:用来存放各种中间件的文件
  4. pipelines.py:用来将items的模型存储到本地磁盘中;数据处理行为,如:一般结构化的数据持久化
  5. settings.py:本爬虫的一些配置信息:递归的层数、并发数,延迟下载(请求头、多久发送一次请求、ip代理池等…)
  6. spiders包:爬虫目录,以后所有的爬虫,都是存放到这个里面,如:创建文件,编写爬虫规则

小案例

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

使用命令创建一个爬虫:

scrapy startproject qiushibk
cd qiushibk
scrapy gensipder qsbk "qiushibaike.com"

创建了一个名字叫做qsbk的爬虫,并且能爬取的网页只会限制在qiushibaike.com这个域名下

爬虫代码解析:

import scrapy

class QsbkSpider(scrapy.Spider):
    name = 'qsbk'
    allowed_domains = ['qiushibaike.com']
    start_urls = ['https://qiushibaike.com/text/page/1/']

    def parse(self, response):
        pass

其实这些代码我们完全可以自己手动去写,而不用命令行的方式去创建生成,但是自己写比较麻烦

要创建一个Spider,必须自定义一个类,继承自scrapy.Spider,然后在这个类中定义三个属性和一个方法:

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

其他说明:

  1. response是一个scrapy.http.response.html.HtmlResponse对象,可以执行xpath和css语法来进行数据提取
    提取出来的数据是一个Selector或SelectorList对象,如果想要获取其中的字符串,可以通过getall或get方法
    getall方法:获取Selector中所有的文本,返回一个列表
    get方法:获取Selector中的第一个文本,返回str类型
  2. 如果要将解析数据出来进行保存,需传给pipelines来处理,使用yield来返回。或者将所有的item数据放入一个列表,再统一用return返回这个列表
  3. item:建议在items.py中定义好模型,就不需要使用字典的方式返回了
  4. pipelines.py文件:是专门用来保存数据的,有三个方法是经常使用到的:
    open_spider(self, spider) 当爬虫被打开时执行此函数
    process_item(self, spider) 当爬虫中有item传过来的时候被调用
    close_spider(self, spider) 当爬虫关闭时会被调用
    要激活pipelines,应该在settings.py中设置ITEM_PIPELINES
    在这里插入图片描述

修改settings.py代码:

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

  1. ROBOTSTXT_OBEY设置为False。默认是True,即遵守机器协议,那么在爬虫的时候,scrapy首先去找网站中的robots.txt文件,如果没有找到,则直接停止爬取
  2. DEFAULT_REQUEST_HEADERS添加User-Agent、Referer等一些请求头,这个也是告诉服务器,我这个请求是一个正常的请求,不是一个爬虫

在这里插入图片描述

完整的爬虫代码:

完善糗事百科案例代码

爬虫部分代码:

spiders文件下的qsbk.py文件,qsbk是创建爬虫时的爬虫名字

import scrapy

# 导入items.py下的QiushibkItem类,此类是自己定义的爬虫传递数据的模型
# from ..items import QiushibkItem
from pa_chong.Scrapy.qiushibk.qiushibk.items import QiushibkItem

# from scrapy.http.response.html import HtmlResponse            # response返回类型
# from scrapy.selector.unified import ScrapyDeprecationWarning  # response.xpath返回类型


class QsbkSpider(scrapy.Spider):  # 继承自scrapy.Spider类
    name = 'qsbk'          # 爬虫名字 以后运行爬虫都根据这名字,所以名字必须要唯一性
    allowed_domains = ['qiushibaike.com']     # 允许的域名
    start_urls = ['https://www.qiushibaike.com/text/page/1/']  # 爬虫开始的url,这里放的是第一页的url

    def parse(self, response):
        outerbox = response.xpath("//div[@id='content-left']/div")
        # outerbox是个SelectorList
        items = []
        for box in outerbox:   # box是个Selector
            author = box.xpath(".//div[contains(@class,'author')]//h2/text()").extract_first().strip()
            content = box.xpath(".//div[@class='content']/span/text()").extract_first().strip()

            '''使用.get()和getall()'''
            # author = box.xpath('.//h2/text()').get().strip()
            # .get()可以把Selector转换成字符串并提取出来
            # content = box.xpath('.//div[@class="content"]//text()').getall()
            # 段子内容是在div下的span标签里的多个br标签里,不是直接在当前属性里的一个文本,
            # 所以text()用两个斜杠获取所有的,再用.getall()将所有内容提取出来转换成字符串
            # content = ''.join(content).strip()  .getall()返回的是列表,用join拼接成字符串

            # 实例化items.py的QiushibkItem类   推荐这种用方式传入数据给pipelines.py
            # item = QiushibkItem(author=author, content=content)
            # item叫什么名字无所谓,只是一个对象名,最终都会把解析下来的数据返回给pipelines.py里的item参数
            item = QiushibkItem()
            item["author"] = author
            item["content"] = content
            yield item

            # items.append(item)
        # return items   # 每次遍历的结果加入到列表 遍历完成后再把列表返回 不推荐

        '''判断是否还有下一页,生成下一页url,请求到下一页,返回给parse函数进行下一页的数据解析'''
        next_url = response.xpath('//ul[@class="pagination"]/li[last()]/a/@href').get()
        # 找到下一页按钮对应的url:class="pagination"的ur标签下面的最后一个li标签下的a标签的href属性

        if not next_url:
            return   # 如果没有找到下一页按钮说明是最后一页了,直接返回
        else:
            yield scrapy.Request('https://www.qiushibaike.com' + next_url, callback=self.parse)
            # 找到下一页按钮后,用yield返回给self.parse函数,然后会继续解析[类似递归或循环]
            # (用scrapy下的Request类传递下一页的url和回调函数,程序在发送请求后 会从此函数继续执行
            # 回调函数一般就是用来执行请求后需要做的事情的,如:继续请求另一个url或进行一些数据解析/提取)

定义一个列表每次把数据都append到列表里,最后再统一返回一个包含全部的内容的列表,它会一个一个传给pipelines.py里的item参数,进行解析
但是如果有多个页面数据需要返回的话,遇到return就代表函数执行完毕了,就不会再请求其他页面了,因此这种方式只会返回一页内容,所以推荐使用yield

按照传统的方式 手动创建字典表再返回也行,但是一般都用items.py里的模型,这个是Scrapy模块推荐使用的方式,处理数据的时候比较好

# 传统的方式
duanzi = {'作者': author, '段子内容': content}
yield duanzi

# 推荐的方式
item =QiushibkItem()
item['作者'] = author
item['段子内容'] = content
yield item

items.py部分代码:

import scrapy


class QiushibkItem(scrapy.Item):   # 定义爬虫传递数据的模型

    author = scrapy.Field()    # 固定写法,把需要传入item的数据这样定义一下就行了
    content = scrapy.Field()

pipeline部分代码:

pipelines.py文件

使用json模块导出数据

import json


class QiushibkPipeline(object):
    def __init__(self):   # 构造函数里的属性和方法 也可以放在下面open_spider()方法里,都会最先调用
        # self.items = []

        self.f = open('duanzi.json', 'w', encoding='utf-8')  # 存到json文件里,先打开一个文件

    def open_spider(self, spider):  # 爬虫打开后最先调用的函数
        print("="*40 + '爬虫开始执行.....')

    def process_item(self, item, spider):      # 存储数据的函数
        # self.items.append(dict(item))   # 先存到列表里

        item_json = json.dumps(dict(item), ensure_ascii=False)
        # 先将item dict成字典表,再dumps成字json符串,ensure_ascii=False是让它不对中文进行Unicode编码
        # 因为使用了数据模型yield返回的数据不像手动写的那样是个字典表,会变成其他对象类型
        self.f.write(item_json + '\n')
        return item          # 最后要把item返回给爬虫

    def close_spider(self,spider):    # 当爬虫关闭时会被调用
        # with open('duanzi.json', 'w', encoding='utf-8') as f:
            # 把前面列表里的 再存到json文件里
            # json.dump(self.items, f, ensure_ascii=False)

        self.f.close()
        print("="*40 + '爬虫结束......')

JsonItemExporter, JsonLinesItemExporter:
保存json数据的时候,可以使用这两个类,让其操作变得更简单

  1. JsonItemExporter:每次把数据添加到内存中,最后统一以列表的方式写在磁盘中
    优点:存储的数据是一个满足json规则的数据;外面是列表(数组),里面包裹着一个一个的字典表(对象/键值对)
    缺点:如果数据量比较大,会比较耗内存。如果过程中出现存储失败,那么有可能所有数据都会存储失败

  2. JsonLinesItemExporter:每次调用export_item的时候直接把这个item存储到磁盘中
    优点:每次处理数据的时候直接存储到硬盘中,这样一条一条的存储json数据不会耗内存,数据也比较安全
    缺点:每一个字典表作为一行json数据存储,整个文件不是一个满足json格式的文件;一行一个字典表(对象/键值对)

这两种方式其实就相当于 在之前没有用scrapy写爬虫的时候,打印(或存储)每个字典表和打印(或存储)列表里包含各个字典表 这两种方式的关系

字典表是提取到的每组数据,比如:{‘user’:‘123’, name:‘456’},在遍历很多组数据时,如果遍历一次打印(或存储)一次此字典表,就是一条一条的打印(或存储)

列表是所有的数据每次遍历后,追加到列表里,最后再整体打印(或存储),比如:[{‘user’:‘123’, name:‘456’}, {‘user’:‘abc’, name:‘789’}, {‘user’:‘aaa’, name:‘bbb’}, …]

from scrapy.exporters import JsonItemExporter, JsonLinesItemExporter

使用scrapy.exporters下的JsonItemExporter

class QiushibkPipeline(object):
    def __init__(self):
        self.f = open('duanzi.json', 'wb')  # 二进制方式打开
        self.exporter = JsonItemExporter(self.f, ensure_ascii=False, encoding='utf-8')
        self.exporter.start_exporting()     # 定义好文件和编码格式后开始导入数据

    def open_spider(self, spider):  # 爬虫打开后最先调用的函数
        print('爬虫开始执行.....')

    def process_item(self, item, spider):      # 存储数据的函数
        self.exporter.export_item(item)    # 先把需要存储的数据(item)都放到一个列表里
        return item          # 最后要把item返回给爬虫

    def close_spider(self, spider):    # 当爬虫关闭时会被调用
        self.exporter.finish_exporting()   # 再统一导入到json文件里,然后结束导入
        # 这种方式数据比较大的话会浪费内存,可以使用JsonLinesItemExporter,一个一个json数据进行导入
        self.f.close()
        print('爬虫结束......')

使用scrapy.exporters下的JsonLinesItemExporter

class QiushibkPipeline(object):
    def __init__(self):
        self.f = open('duanzi.json', 'wb')  # 二进制方式打开
        self.exporter = JsonLinesItemExporter(self.f, ensure_ascii=False, encoding='utf-8')

    def open_spider(self, spider):  # 爬虫打开后最先调用的函数
        print('爬虫开始执行.....')

    def process_item(self, item, spider):      # 存储数据的函数
        self.exporter.export_item(item)    # 开始传入数据  这种方式每次直接将传进来的json数据传入json文件里保存
        return item          # 最后要把item返回给爬虫

    def close_spider(self, spider):    # 当爬虫关闭时会被调用
        self.f.close()
        print('爬虫结束......')

exporters还有其他很多写入文件的方式,具体可以查看模块

运行Scrapy项目:

运行scrapy项目需要在终端进入项目所在的路径,然后输入scrapy crawl [爬虫名字]回车即可运行指定的爬虫。


虚拟环境问题:

1、如果你的scrapy是安装在手动创建的虚拟环境下的,需要先进入到此虚拟环境中

在这里插入图片描述

python配置虚拟环境:
https://www.jianshu.com/p/ad2d8ee4a679
https://www.cnblogs.com/suke99/p/5355894.html

2、如何进入到pycharm所创建的虚拟环境中

在这里插入图片描述


如果不想每次都在命令行中运行,那么可以把这个命令写在一个文件中。以后就在pycharm中执行运行这个文件就可以了。比如现在新创建一个文件叫做start.py,然后在这个文件中填入以下代码:

from scrapy import cmdline  # 通过scrapy下的cmdline模块来执行爬虫

# 下面两个效果一样
# cmdline.execute('scrapy crawl qsbk'.split())  # 分割'scrapy crawl qsbk'这个字符串
cmdline.execute(['scrapy', 'crawl', 'qsbk'])    # 直接以列表方式传入scrapy crawl qsbk这三儿玩意
# qsbk是这个爬虫的爬虫文件的名字

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值