在途中

Good Luck To You!

Python系列,网络爬虫Scrapy框架入门教程(要点清晰,分步讲解)

Scrapy 功能非常强大,爬取效率高,相关扩展组件多,可配置和可扩展程度非常高,它几乎可以应对所有反爬网站,是目前 Python 中使用最广泛的爬虫框架。本教程主要从以下几个章节进行讲解:

Scrapy教程框架图

一、Scrapy简介

Scrapy是适用于Python的一个快速、高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据。Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试。

Scrapy吸引人的地方在于它是一个框架,任何人都可以根据需求方便的修改。它也提供了多种类型爬虫的基类,如BaseSpider、sitemap爬虫等,最新版本又提供了web2.0爬虫的支持。

Scrapy 是用 Python 实现的一个为了爬取网站数据、提取结构性数据而编写的应用框架。 Scrapy 常应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。 通常我们可以很简单的通过 Scrapy 框架实现一个爬虫,抓取指定网站的内容或图片。

二、Scrapy安装

Scrapy 是一个十分强大的爬虫框架 ,依赖的库比较多 ,至少需要依赖的库有 Twistedlxml和 pyOpenSSL。 在不同的平台环境下,它所依赖的库也各不相同,所以在安装之前,最好确保把一些基本库安装好。本节就来介绍 Scrapy在不同windows平台下的安装方法 。

2.1安装Scrapy依赖库

在Windows环境下,如果你的 Python 不是使用 Anaconda 安装的,可以参考如下方式来一步步安装 Scrapy。

2.1.1安装lxml

lxml是 Python的一个解析库,支持 HTML 和 XML 的解析,支持 XPath 解析方式,而且解析效率非常高。

相关链接:

在Windows下,打开cmd命令控制窗口,直接执行如下命令即可:

pip install lxml

如果没有任何报锚,则证明安装成功。

如果出现报错,比如提示缺少libxml2库等信息,可以采用wheel方式安装。 推荐直接到这里(链接为:lfd.uci.edu/~gohlke/pyt )下载对应的 wheel文件,找到本地安装 Python 版本和系统对应的lxml版本,例如 Windows 64 位、 Python3.6,就选lxml-3.8.0-cp36-cp36m-win_amd64.whl,将其下载到本地。然后打开cmd命令窗口,进入wheel所在文件目录下,利用pip安装即可,命令如下:

pip install lxml 3.8.0-cp36-cp36m-win_amd64.whl

这样我们就可以成功安装 lxml 了。

2.1.2安装pyOpenSSL

在官方网站下载wheel文件(pypi.python.org/pypi/py),打开cmd命令控制窗口,进入wheel所在文件目录下,利用pip安装即可:

pip install pyOpenSSL-17.2.0-py2.py3-none-any.whl

2.1.3安装pyOpenSSL

在官方网站下载wheel文件(lfd.uci.edu/~gohlke/pyt),比如对于Python 3.6 版本、 Windows64 位系统,则当前最新版本为pywin32-227-cp36-cp36m-win_amd64.whl,直接下载,利用pip安装即可(同上)。

pip install pywin32-227-cp36-cp36m-win_amd64.whl

验证安装。安装完成之后,可以在cmd中输入以下命令进行验证:

where python  # 进入pythonpip list  # 查看库安装列表中,列表中存在上述库,则说明安装成功

2.2安装Scrapy库

安装好了以上的依赖库后,安装 Scrapy 就非常简单了,这里依然使用 pip ,命令如下:

pip install Scrapy

验证安装。安装完成之后,可以在cmd中输入以下命令进行验证:

where python  # 进入pythonpip list  # 查看库安装列表中,列表中存在scrapy库,则说明安装成功

三、Scrapy框架

Scrapy 是一个基于 Twisted 的异步处理框架,是纯Python实现的爬虫框架,其架构清晰, 榄块之间的耦合程度低,可扩展性极强,可以灵活完成各种需求。我们只需要定制开发几个模块就可以轻松实现一个爬虫。

接下来,主要介绍一下Scrapy框架数据流以及项目结构

3.1框架结构

首先我们看看Scrapy框架的架构,如下图所示。

它可以分为如下的几个部分。

  1. Engine(引擎)。 处理整个系统的数据流处理、触发事务,是整个框架的核心。

  2. Item(项目)。它定义了爬取结果的数据结构,爬取的数据会被赋值成该Item对象。

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

  4. Downloader(下载器)。下载网页内容,并将网页内容返回给蜘蛛。

  5. Spiders(爬虫)。其内定义了爬取的逻辑和网页的解析规则 ,它主要负责解析响应并生成提取结果和新的请求。

  6. Item Pipeline(项目管道)。负责处理由爬虫从网页中抽取的项目,它的主要任务是清洗、验证和存储数据。

  7. Downloader Middlewares(下载器中间件) 。位于引擎和下载器之间的钩子框架,主要处理引擎与下载器之间的请求及响应。

  8. Spider Middlewares(爬虫中间件)。位于引擎和爬虫之间的钩子框架,主要处理爬虫输入的响应和输出的结果及新的请求。

3.2数据流

Scrapy中的数据流由引擎控制,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 Pipeline,将新的Request给Scheduler。

  9. 重复第(2)步到第(8)步,直到Scheduler中没有更多的 Request,Engine 关闭该网站,爬取结束。

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

3.3项目结构

scrapy项目创建之后,项目文件结构如下所示:

scrapy.cfgproject/
    __init__.py
    items.py
    pipelines.py
    settings.py
    middlewares.py 
    spiders/
        __init__.py
        spider1.py
        spider2.py
        ...

这里各个文件的功能描述如下 。

  1. scrapy.cfg:scrapy 项目配置文件,其内定义了项目的配置文件路径、部署相关信息等内容 。

  2. items.py:它定义 Item 数据结构,所有的 Item 的定义都可以放这里 。

  3. pipelines.py:它定义 Item Pipeline 的实现,所有的 Item Pipeline 的实现都可以放这里。

  4. settings.py:它定义项目的全局配置。

  5. middlewares.py:它定义 Spider Middlewares 和 Downloader Middlewares 的实现。

  6. spiders:其内包含一个个 Spider 的实现,每个 Spider 都有一个文件。

四、Scrapy抓取流程

接下来,简单介绍一遍 Scrapy 抓取流程。通过这个过程,我们可以对 Scrapy 的基本用法和原理有大体了解,主要分为以下几个步骤:

  1. 创建项目

  2. 创建Spider

  3. 创建Item

  4. 编写Spider

  5. 运行Spider

  6. 保存数据

为了直观地掌握Scrapy 的基本用法,我们通过一个实例(抓取慕课网的课程信息)来讲解。

4.1创建项目

创建一个 Scrapy 项目,项目文件可以直接用 scrapy 命令生成,命令如下所示。

4.1.1首先打开cmd命令窗口,输入以下命令

# 进入待创建项目所在文件夹下,例如文件目录为D:\pycharm\PycharmProjectsC:\Users\sxm>D:D:\>cd pycharmD:\pycharm>cd PycharmProjectsD:\pycharm\PycharmProjects>

4.1.2使用 scrapy 命令,创建项目(scrapy startproject 项目名称)

D:\pycharm\PycharmProjects>scrapy startproject PythonDemo  # PythonDemo为项目名称

这个命令将会创建一个名为 PythonDemo 的文件夹,文件夹结构如下所示:

scrapy.cfg            # Scrapy 部署时的配置文件PythonDemo/           # 项目的模块 , 需要从这里引入
    __init__.py
    items.py          # Items 的定义,定义爬取的数据结构
    middlewares.py    # Middlewares的定义,定义爬取时的中间件
    pipelines.py      # Pipelines的定义,定义数据管道
    settings.py       # 配置文件
    spiders/          # 放置 Spiders 的文件夹
        __init__.py
        ...

在Pycharm中显示如下:

4.2创建Spider

Spider 是向己定义的类,Scrapy 用它来从网页里抓取内容,并解析抓取的结果。不过这个类必须继承 Scrapy 提供的 Spider 类 scrapy.Spider ,还要定义Spider的名称和起始请求,以及怎样处理爬取后的结果的方法。

可以使用命令行创建一个Spider。比如要生成 myspider 这个 Spider,可以执行如下命令。

4.2.1首先打开cmd命令窗口,输入以下命令

# 进入刚才创建的 PythonDemo 文件夹,例如文件目录为D:\pycharm\PycharmProjects\PythonDemoC:\Users\sxm>D:D:\>cd pycharmD:\pycharm>cd PycharmProjectsD:\pycharm\PycharmProjects>cd PythonDemo

4.2.2使用 scrapy 命令,创建 Spider(scrapy genspider Spider名称 网站域名)

# myspider是我们要创建的Spider名称,www.imooc.com是允许爬取的域名D:\pycharm\PycharmProjects\PythonDemo>scrapy genspider myspider www.imooc.com

执行完毕之后,spiders文件夹中多了一个 myspider.py,它就是刚刚创建的Spider, 内容如下所示:

这里有三个属性,name、allowed_domains和start_urls,以及一个方法parse。

  • name:它是每个项目唯一的名字,用来区分不同的 Spider 。

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

  • start_urls:包含了Spider在启动时进行爬取的url列表。 因此,第一个被获取到的页面将是其中之一。 后续的URL则从初始的URL获取到的数据中提取。

  • parse:parse() 是spider的一个方法。 被调用时,每个初始URL完成下载后生成的 Response 对象将会作为唯一的参数传递给该函数。 该方法负责解析返回的数据(response data),提取数据(生成item)以及生成需要进一步处理的URL的 Request 对象。

4.3创建Item

创建完了Spider文件,先不急着编写爬取代码,我们先定义一个容器保存要爬取的数据。

这样我们就用到了Item 。为了定义常用的输出数据,Scrapy 提供了 Item 类。Item 对象是种简单的容器,保存了爬取到得数据。 其提供了类似于词典(dictionary-like)的API以及用于声明可用字段的简单语法。

我们在工程目录下可以看到一个 items 文件,我们可以更改这个文件或者创建一个新的文件来定义我们的 item

创建 Item 需要继承 scrapy.Item 类,并且定义类型为scrapy.Field的字段。例如我们要爬取慕课网站课程的信息,包括课程名称,课程URL,课程图片URL,课程描述,学习人数。

此时可以将item.py修改如下:

item.py文件未修改前如下:

对item.py文件进行修改。

import scrapyclass PythondemoItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    # 课程名称
    title = scrapy.Field()
    # 课程url
    url = scrapy.Field()
    # 课程标题图片url
    image_url = scrapy.Field()
    # 课程描述
    introduction = scrapy.Field()
    # 学习人数
    student = scrapy.Field()

根据如上的代码,我们创建了一个名为Item的容器,用来保存、抓取的信息, title->课程名称, url->课程url, image_url->课程标题图片url, introduction->课程描述, student->学习人数。

在创建完 item 文件后我们可以通过类似于词典(dictionary-like)的API以及用于声明可用字段的简单语法。 常用方法如下:

#定义一个itemcourse = PythondemoItem()#赋值course['title'] = "语文"#取值course['title']course.get('title')#获取全部键course.keys()#获取全部值course.items()

4.4编写Spider

创建了item后我们就能进行爬取部分的工作了。

我们在上文说过,爬取的部分在Myspider类的 parse() 方法中进行。

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

parse()方法负责处理 response 并返回处理的数据以及(/或)跟进的URL。 该方法及其他的 Request 回调函数必须返回一个包含 Request 及(或) Item 的可迭代的对象。

为了简单清晰,我们先抓取一个页面中的信息,页面就是慕课网-免费课程的第一页(网址:imooc.com/course/list),页面如下:

器的调试工具我们可以看到它们的结构,每一个课程对应一个div<@class="course-card-container">。

我们之前创建的Myspider.py在未修改前如下:

接下来对Myspider.py中的 parse() 方法进行修改,对我们要爬取慕课网站课程的信息,包括课程名称,课程URL,课程图片URL,课程描述,学习人数使用xpath来解析。

# -*- coding: utf-8 -*-import scrapy# 引入容器from PythonDemo.items import PythondemoItemclass MyspiderSpider(scrapy.Spider):
    name = 'myspider'
    allowed_domains = ['www.imooc.com']
    start_urls = ['https://www.imooc.com/course/list']

    # 填写爬写方法
    def parse(self, response):
        # 实例化一个容器保存爬取信息
        item = PythondemoItem()
        # 这部分是爬取部分,使用xpath的方式选择信息,具体方法根据网页结构而定
        # 先获取每个课程的div
        for box in response.xpath('//div[@class="course-card-container"]/a[@target="_blank"]'):
            # 获取div中的课程标题
            # strip() 方法用于移除字符串头尾指定的字符(默认为空格)
            # extract()返回的所有数据,存在一个list里。extract_first()返回的是一个string,是extract()结果中第一个值。
            item['title'] = box.xpath('.//h3/text()').extract()[0].strip()
            # 获取每个div中的课程路径
            item['url'] = 'http://www.imooc.com' + box.xpath('.//@href').extract()[0]
            # 获取div中的标题图片地址
            item['image_url'] = 'http:'+box.xpath('.//img[@data-original]').extract()[0]
            # 获取div中的课程简介
            #item['introduction'] = box.xpath('.//p[@class="course-card-desc"]/text()').extract()[0].strip()
            item['introduction'] = box.xpath('.//p/text()').extract()[0].strip()
            # 获取div中的学生人数
            item['student'] = box.xpath('.//div[@class="course-card-info"]/span[2]/text()').extract()[0].strip()

            # 返回信息
            yield item

Myspider.py修改后如下:

注:这里用到了xpath方式来获取页面信息。

在 parse() 方法中 response 参数返回一个下载好的网页信息,我们然后通过xpath来寻找我们需要的信息。

在scrapy框架中,可以使用多种选择器来寻找信息,这里使用的是 xpath,同时我们也可以使用BeautifulSouplxml等扩展来选择,而且框架本身还提供了一套自己的机制来帮助用户获取信息,就是 Selectors 。

4.5运行Spider

在执行完以上步骤之后,我们可以运行一下爬虫,命令如下所示。

4.5.1首先打开cmd命令窗口,输入以下命令

# 进入刚才创建的 PythonDemo 文件夹,例如文件目录为D:\pycharm\PycharmProjects\PythonDemoC:\Users\sxm>D:D:\>cd pycharmD:\pycharm>cd PycharmProjectsD:\pycharm\PycharmProjects>cd PythonDemo

4.5.2使用 scrapy 命令,运行 Spider(scrapy crawl Spider名称)

# myspider是我们要创建的Spider名称D:\pycharm\PycharmProjects\PythonDemo>scrapy crawl myspider

执行完毕之后,如果操作正确会显示如下信息。

或者在Pycharm中执行 scrapy 命令,如下图:

执行完毕之后,显示以下信息:

4.6保存数据

运行完 Scrapy 后,我们只在控制台看到了输出结果。

Scrapy 提供的 Feed Exports 可以轻松将抓取结果输出。 例如,我们想将上面的结果保存成 csv 文件,可以在 Pycharm 的 Terminal 窗口中执行如下命令:

D:\pycharm\PycharmProjects\PythonDemo>scrapy crawl myspider -o myspider.csv

命令运行后,项目内多了一个 myspider.csv 文件,文件包含了刚才抓取的所有内容,内容是 csv 格式,如下图。

打开 myspider.csv 文件,内容如下。

输出格式还支持很多种,例如 json、 xml 、 pickle 、 marshal 等。 下面命令对应的输出分别为 json 、 xml 、 pickle 、 marshal 格式以及句远程输出。

scrapy crawl myspider -o myspider.jsonscrapy crawl myspider -o myspider.xml scrapy crawl myspider -o myspider.pickle scrapy crawl myspider -o myspider.marshal scrapy crawl myspider -o ftp://user:pass@ftp.example.com/path/to/myspider.csv

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

通过 Scrapy 提供的 Feed Exports,我们可以轻松地输出抓取结果到文件。对于一些小型项目来说,这应该足够了。不过如果想要更复杂的输出,如输出到数据库等,我们可以使用 ItemPileline 来完成。

五、Scrapy多页面爬取

5.1多页面爬取

在上面我们介绍了如何进行简单的单页面爬取,但是我们可以发现慕课网的课程是分布在去多个页面的,所以为了完整的爬取信息课程信息,我们需要进行url跟进。 为了完成这个目标需要对 MySpider.py 文件进行如下更改,增加以下代码:

# url跟进开始,获取下一页的url信息
        url = response.xpath("//a[contains(text(),'下一页')]/@href").extract()
        if url:
        # 将信息组合成下一页的url
            page = 'http://www.imooc.com' + url[0]
            yield scrapy.Request(page, callback=self.parse)

5.2保存数据至MongoDB

如果需要将爬取下来的数据,储存到数据库,就需要使用Item Pipeline来实现。

Item Pipeline意为数据管道,当成Item后,它会自动被送到Item Pipeline进行处理。Item Pipeline主要有以下作用:

  1. 1.清理HTML数据。

  2. 2.验证爬取数据,检查爬取字段。

  3. 3.查重并丢弃重复内容。

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

实现 Item Pipeline 很简单,只需要定义一个类实现 process_item 方法即可,并且在 Setting.py 配置文件中启用ITEM_PIPELINES这个方法主要有两个参数,一个是 item ,每次spider 生成的 item 都会作为参数传递过来;另外一个是 spider ,就是 spider 的实例。

设置setting 打开ITEM_PIPELINES

ITEM_PIPELINES = {'PythonDemo.pipelines.PythondemoPipeline': 300,}

连接 MongoDB 数据库有两种方法,一种需要在settings.py配置相应的参数,一种是不需要配置,直接初始化的时候,传入相应的值即可。

5.2.1初始化的时候,直接传入相应的值

编写 pipelines.py 如下:

import pymongoclass PythondemoPipeline(object):
    def __init__(self):
        # 配置MongoDB数据库
        MONGO_HOST = "127.0.0.1"  # 主机IP
        MONGO_PORT = 27017  # 端口号
        MONGO_DB = "smn"  # 库名
        MONGO_COLL = "col_stu"  # collection名
        # 连接数据库
        self.client = pymongo.MongoClient(host = MONGO_HOST, port = MONGO_PORT)
        # 数据库登录需要帐号密码的话
        # self.client.admin.authenticate(settings['MINGO_USER'], settings['MONGO_PSW'])
        self.client = pymongo.MongoClient()
        # 获得数据库的句柄
        self.db = self.client[MONGO_DB]
        # 获得集合collection的句柄
        self.coll = self.db[MONGO_COLL]
    def process_item(self, item, spider):
        self.coll.insert(dict(item))  # 向数据库插入一条记录
        return item
    #该方法在spider被开启时被调用
    def open_spider(self, spider):
        pass
    #该方法在spider被关闭时被调用
    def close_spider(self, spider):
        pass

5.2.2在setting.py配置文件中设置参数,调用setting.py文件中的参数值

setting.py配置参数如下:

# 配置MongoDB数据库
        MONGO_HOST = "127.0.0.1"  # 主机IP
        MONGO_PORT = 27017  # 端口号
        MONGO_DB = "smn"  # 库名
        MONGO_COLL = "col_stu"  # collection名

pipelines.py 文件中,利用 from scrapy.conf import setting可以读取配置文件

import pymongofrom scrapy.conf import settingsclass PythondemoPipeline(object):
    def __init__(self):
        # 连接数据库
        self.client = pymongo.MongoClient(host=settings['MONGO_HOST'], port=settings['MONGO_PORT'])
        # 数据库登录需要帐号密码的话
        # self.client.admin.authenticate(settings['MINGO_USER'], settings['MONGO_PSW'])
        self.client = pymongo.MongoClient()
        # 获得数据库的句柄
        self.db = self.client[settings['MONGO_DB']]
        # 获得集合collection的句柄
        self.coll = self.db[settings['MONGO_COLL']]
    def process_item(self, item, spider):
        self.coll.insert(dict(item))  # 向数据库插入一条记录
        return item

    #该方法在spider被开启时被调用
    def open_spider(self, spider):
        pass
    #该方法在spider被关闭时被调用
    def close_spider(self, spider):
        pass

5.3执行爬虫

在 Pycharm 的 Terminal 窗口中执行如下命令:

D:\pycharm\PycharmProjects\PythonDemo>scrapy crawl myspider

爬虫结束后,在 smn 数据库中,会生成一个scrapy的collection。(类似mysql里面表的table概念)


这里要说明一点就是在使用 MongoDB 的时候不同于 MySql,不用事先定义好数据表和表结构。数据库需要先创建好,上述我们创建了名为“smn”数据库,collection 可以不要事先创建,用insert语句插入的时候,如果collection还不存在,他会自动被创建出来。


Powered By Z-BlogPHP 1.7.3

Copyright 11taotao.com Rights Reserved.