Python——Scrapy初学

2018-01-08 12:04:49 浏览数 (1)

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

1 安装

简要说明下Scrapy的安装:

下载网址:http://www.lfd.uci.edu/~gohlke/pythonlibs/

下载后缀名为whl的scrapy文件,在cmd中进入Scripts所在的位置,输入pip install scrapy文件名.whl(可参考《Python初学基础》中的7.1 模块安装),注意scrapy依赖twiste,同样使用whl格式的包进行安装。安装完这两个模块后我在进行爬虫操作的时候提示没有win32api,该文件为exe,下载地址为https://sourceforge.net/projects/pywin32/files/pywin32/Build 220/。

在安装好模块后要注意环境变量的配置,以我自己的安装目录为例,应当将D:Program Files (x86)PythonScripts以及D:Program Files (x86)PythonLibsite-packages加入环境变量中,否则模块只能在安装目录下运行,在别的目录下运行时会提示不是内部或者外部命令。在cmd下输入scrapy查看是否安装成功。

上述简单介绍了scrapy的安装,在安装的过程中不要着急,如果安装出错,要注意查看错误信息,根据这些信息一个一个去解决。

2 Scrapy架构及组件介绍

使用Scrapy抓取一个网站一共需要四个步骤:

1. 创建一个Scrapy项目;

2. 定义Item容器;

3. 编写爬虫;

4. 存储内容

学习怎么使用Scrapy之前,我们需要先来了解一下Scrapy的架构以及组件之间的交互。下图展现的是Scrapy的架构,包括组件及在系统中发生的数据流(图中绿色箭头)。

下面对每个组件都做了简单介绍:

Scrapy Engine

Scrapy引擎是爬虫工作的核心,负责控制数据流在系统中所有组件中流动,并在相应动作发生时触发事件。

调度器(Scheduler)

调度器从引擎接受request并将他们入队,以便之后引擎请求他们时提供给引擎。

下载器(Downloader)

下载器负责获取页面数据并提供给引擎,而后提供给spider。

Spiders

Spider是Scrapy用户编写用于分析由下载器返回的response,并提取出item和额外跟进的URL的类。 Item Pipeline Item Pipeline负责处理被spider提取出来的item。典型的处理有清理、验证及持久化(例如存取到数据库中)。

接下来是两个中间件,它们用于提供一个简便的机制,通过插入自定义代码来扩展Scrapy的功能。

下载器中间件(Downloader middlewares)

下载器中间件是在引擎及下载器之间的特定钩子(specific hook),处理Downloader传递给引擎的response。

Spider中间件(Spider middlewares)

Spider中间件是在引擎及Spider之间的特定钩子(specific hook),处理spider的输入(就是接收来自下载器的response)和输出(就是发送items给item pipeline以及发送requests给调度器)。

3 Scrapy爬虫框架入门实例

例程参考《scrapy爬虫框架入门实例》,该例子是抓取慕课网(http://blog.csdn.net/zjiang1994/article/details/52779537)。慕课网的页面结构已经变了,所以说该案例实际上已经不能达到抓取目的。但是关于scrapy爬虫框架整体的使用方式和流程目前还是正确的,可以进行参考。根据慕课网现有的页面结构做了一些改动可以成功实现。

要抓取的内容是全部的课程名称,课程图片,课程人数,课程简介,课程URL:

右键审查元素查看

代码语言:javascript复制
#如果response是网页资源的话,下面的代码可以帮助我们获得div
divs = response.xpath('//div[@class="course-card-container"]')

所以如果div已经获得的话通过如下获得信息(详解介绍见下文):

代码语言:javascript复制
#获取每个div中的课程路径item['url'] = 'http://www.imooc.com'   box.xpath('.//@href').extract()[0]#获取div中的课程标题
item['title'] = box.xpath('.//h3[@class="course-card-name"]/text()').extract()[0].strip()
#获取div中的标题图片地址
item['image_url'] =  'http:'   box.xpath('.//@src').extract()[0]
#获取div中的学生人数
item['student'] = box.xpath('.//span/text()').extract()[1].strip()
#获取div中的课程简介
item['introduction'] = box.xpath('.//p/text()').extract()[0].strip()

工作流程

Scrapy框架抓取的基本流程是这样:

当然了,还有一些中间件等等,这里是入门例子,所以不涉及。

1)创建一个Scrapy项目

在开始爬取之前,您必须创建一个新的Scrapy项目。

进入您打算存储代码的目录中,运行下列命令: scrapy startproject tutorial

该命令将会创建包含下列内容的tutorial目录:

代码语言:javascript复制
tutorial/
    scrapy.cfg
    tutorial/
        __init__.py
        items.py
        pipelines.py
        settings.py
        spiders/
            __init__.py
            ...

这些文件构成Scrapy爬虫框架,它们分别的作用是:

scrapy.cfg – 项目的配置文件

tutorial/ – 该项目的python模块,之后您将在此加入代码

tutorial/items.py – 项目中的item文件

tutorial/pipelines.py – 项目中的pipelines文件

tutorial/settings.py – 项目的设置文件

tutorial/spiders/ – 放置spider代码的目录

2)定义Item容器

Item是保存爬取到的数据的容器,其使用方法和python字典类似, 并且提供了额外保护机制来避免拼写错误导致的未定义字段错误。

首先根据需要获取到的数据对item进行建模。比如我们需要从慕课网中获取课程名称,课程图片,课程人数,课程简介,课程URL。对此,我们需要在item中定义相应的字段。

我们在工程目录下可以看到一个items文件,我们可以更改这个文件或者创建一个新的文件来定义我们的item。将item.py中的内容修改如下:

代码语言:javascript复制
#引入文件
import scrapy

class CourseItem(scrapy.Item):
    #课程标题
    title = scrapy.Field()
    #课程url
    url = scrapy.Field()
    #课程标题图片
    image_url = scrapy.Field()
    #课程描述
    introduction = scrapy.Field()
    #学习人数
    student = scrapy.Field()
    image_path = scrapy.Field()

根据如上的代码,我们创建了一个名为item的容器,用来保存、抓取的信息, title->课程标题, url->课程url, image_url->课程标题图片, introduction->课程描述, student->学习人数。在创建完item文件后我们可以通过类似于词典(dictionary-like)的API以及用于声明可用字段的简单语法。常用方法如下:

代码语言:javascript复制
#定义一个item
course = CourseItem()
#赋值
course['title'] = "语文"
#取值
course['title']
course.get('title')
#获取全部键
course.keys()
#获取全部值
course.items()

3) 创建一个爬虫

我们要编写爬虫,首先是创建一个Spider我们在tutorial/spiders/目录下创建一个文件MySpider.py

文件包含一个MySpider类,它必须继承scrapy.Spider类。

同时它必须定义一下三个属性:

-name: 用于区别Spider。 该名字必须是唯一的,您不可以为不同的Spider设定相同的名字。 -start_urls: 包含了Spider在启动时进行爬取的url列表。 因此,第一个被获取到的页面将是其中之一。 后续的URL则从初始的URL获取到的数据中提取。 -parse() 是spider的一个方法。 被调用时,每个初始URL完成下载后生成的 Response 对象将会作为唯一的参数传递给该函数。 该方法负责解析返回的数据(response data),提取数据(生成item)以及生成需要进一步处理的URL的 Request 对象。

创建完成后MySpider.py的代码如下

代码语言:javascript复制
#引入文件
import scrapy

class MySpider(scrapy.Spider):
    #用于区别Spider
    name = "MySpider"
    #允许访问的域
    allowed_domains = []
    #爬取的地址
    start_urls = []
    #爬取方法
    def parse(self, response):
        pass

为了简单清晰,我们先抓取一个页面中的信息。

首先我们编写爬取代码。我们在上文说过,爬取的部分在MySpider类的parse()方法中进行。 parse()方法负责处理response并返回处理的数据以及(/或)跟进的URL。该方法及其他的Request回调函数必须返回一个包含 Request 及(或) Item 的可迭代的对象。

在网页中提取我们所需要的数据,之前所学习的是根据正则表达式来获取,在Scrapy中是使用一种基于Xpath和CSS的表达式机制:Scrapy Selectors。

Selector是一个选择器,它有四个基本的方法:

xpath() – 传入xpath表达式,返回该表达式所对应的所有节点的selector list列表 。

css() – 传入CSS表达式,返回该表达式所对应的所有节点的selector list列表。

extract() – 序列化该节点为unicode字符串并返回list。

re() – 根据传入的正则表达式对数据进行提取,返回unicode字符串list列表。

在Shell中尝试Selector选择器

为了介绍Selector的使用方法,接下来我们将要使用内置的Scrapy shell。

你需要先进入项目的根目录,执行下列命令来启动Scrapy shell:

scrapy shell “http://www.imooc.com/course/list”

shell的输出类似:

在Shell载入后,你将获得response回应,存储在本地变量response中。

所以如果你输入response.body,你将会看到response的body部分,也就是抓取到的页面内容,或者输入response.headers 来查看它的 header部分。现在就像是一大堆沙子握在手里,里面有我们想要的金子,所以下一步我们就要用筛子把沙子去掉,淘出金子。selector选择器就是这样一个筛子,正如我们刚才讲到的,你可以使用response.selector.xpath()、response.selector.css()、response.selector.extract()和response.selector.re()这四个基本方法。

使用XPath

什么是XPath?XPath是一门在网页中查找特定信息的语言。所以用XPath来筛选数据,要比使用正则表达式容易些。

这里给出XPath表达式的例子及对应的含义:

/html/head/title – 选择HTML文档中<head>标签内的<title>元素

/html/head/title/text() – 选择上面提到的<title>元素的文字

//td – 选择所有的<td>元素

//div[@class=”mine”] – 选择所有具有class=”mine”属性的div元素

上边仅仅是几个简单的XPath例子,XPath实际上要比这远远强大的多。如果你想了解更多关于XPath的内容,推荐学习这篇文章http://www.w3school.com.cn/xpath/

值得一提的是,response.xpath()、response.css()已经被映射到response.selector.xpath()、response.selector.css(),所以直接使用response.xpath()即可。

在Python编写时,由于没有学习过Xpath,所以我先在cmd中编写试验得到正确的返回结果后再写入代码中,注意shell根据response的类型自动为我们初始化了变量sel,我们可以直接使用。

例如获取每个div中的课程路径:

此外,我们希望Spiders将爬取并筛选后的数据存放到item容器中,所以我们MySpider.py的代码应该是这样的:

代码语言:javascript复制
import scrapy
#引入容器
from tutorial.items import CourseItem


class MySpider(scrapy.Spider):
    #设置name
    name = "MySpider"
    #设定域名
    allowed_domains = ["imooc.com"]
    #填写爬取地址
    start_urls = ["http://www.imooc.com/course/list"]
    #编写爬取方法
    def parse(self, response):
        #实例一个容器保存爬取的信息
        item = CourseItem()  
        #这部分是爬取部分,使用xpath的方式选择信息,具体方法根据网页结构而定
        #先获取每个课程的div
        for box in response.xpath('//div[@class="course-card-container"]'):
            #获取每个div中的课程路径
            item['url'] = 'http://www.imooc.com'   box.xpath('.//@href').extract()[0]
            #获取div中的课程标题
            item['title'] = box.xpath('.//h3[@class="course-card-name"]/text()').extract()[0].strip()
            #获取div中的标题图片地址
            item['image_url'] =  'http:'   box.xpath('.//@src').extract()[0]
            #获取div中的学生人数
            item['student'] = box.xpath('.//span/text()').extract()[1].strip()
            #获取div中的课程简介
            item['introduction'] = box.xpath('.//p/text()').extract()[0].strip()
            #返回信息
            yield item

在parse()方法中response参数返回一个下载好的网页信息,我们然后通过xpath来寻找我们需要的信息。 在scrapy框架中,可以使用多种选择器来寻找信息,这里使用的是xpath,同时我们也可以使用BeautifulSoup,lxml等扩展来选择,而且框架本身还提供了一套自己的机制来帮助用户获取信息,就是Selectors。 

在执行完以上步骤之后,我们可以运行一下爬虫,看看是否出错。

在命令行下进入工程文件夹,然后运行:

代码语言:javascript复制
scrapy crawl MySpider

如果操作正确会显示如下信息:

上面信息表示,我们已经获取了信息,接下来我们开始进行信息的储存。

最简单存储爬取的数据的方式是使用Feed exports,主要可以导出四种格式:JSON,JSON lines,CSV和XML。

我们这里将结果导出为最常用的JSON格式:

scrapy crawl dmoz -o items.json -t json

-o 后边是导出的文件名,-t 指定导出类型 成功执行命令后,根目录出现了一个叫 items.json 的文件,内容如下:

或者使用Pipeline处理数据:

当我们成功获取信息后,要进行信息的验证、储存等工作,这里以储存为例。 当Item在Spider中被收集之后,它将会被传递到Pipeline,一些组件会按照一定的顺序执行对Item的处理。 Pipeline经常进行以下一些操作: 清理HTML数据 验证爬取的数据(检查item包含某些字段) 查重(并丢弃) 将爬取结果保存到数据库中

这里只进行简单的将数据储存在json文件的操作。

改写在tutorial/目录下文件pipelines.py的代码如下:

代码语言:javascript复制
# -*- coding: utf-8 -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html

#引入文件
from scrapy.exceptions import DropItem
import json

class MyPipeline(object):
    def __init__(self):
        #打开文件
        self.file = open('data.json', 'w', encoding='utf-8')
    #该方法用于处理数据
    def process_item(self, item, spider):
        #读取item中的数据
        line = json.dumps(dict(item), ensure_ascii=False)   "n"
        #写入文件
        self.file.write(line)
        #返回item
        return item
    #该方法在spider被开启时被调用。
    def open_spider(self, spider):
        pass
    #该方法在spider被关闭时被调用。
    def close_spider(self, spider):
        pass

要使用Pipeline,首先要注册Pipeline

找到settings.py文件,这个文件时爬虫的配置文件

在其中添加:

代码语言:javascript复制
ITEM_PIPELINES = {
    'tutorial.pipelines.MyPipeline': 1,
}

上面的代码用于注册Pipeline,其中'tutorial.pipelines.MyPipeline为你要注册的类,右侧的’1’为该Pipeline的优先级,范围1~1000,越小越先执行。

进行完以上操作,我们的一个最基本的爬取操作就完成了

这时我们再运行:

代码语言:javascript复制
scrapy crawl MySpider

就可以在项目根目录下发现data.json文件,里面存储着爬取的课程信息。

上面的代码只进行了比较简单的爬取,并没有完成爬取慕课网全部课程的目标。 下面进行一些简单的扩展完成我们的目标。

url跟进

在上面我们介绍了如何进行简单的单页面爬取,但是我们可以发现慕课网的课程是分布在去多个页面的,所以为了完整的爬取信息课程信息,我们需要进行url跟进。

为了完成这个目标需要对MySpider.py文件进行如下更改

代码语言:javascript复制
import scrapy
#引入容器
from tutorial.items import CourseItem


class MySpider(scrapy.Spider):
    #设置name
    name = "MySpider"
    #设定域名
    allowed_domains = ["imooc.com"]
    #填写爬取地址
    start_urls = ["http://www.imooc.com/course/list"]
    #编写爬取方法
    def parse(self, response):
        #实例一个容器保存爬取的信息
        item = CourseItem()  
        #这部分是爬取部分,使用xpath的方式选择信息,具体方法根据网页结构而定
        #先获取每个课程的div
        for box in response.xpath('//div[@class="course-card-container"]'):
            #获取每个div中的课程路径
            item['url'] = 'http://www.imooc.com'   box.xpath('.//@href').extract()[0]
            #获取div中的课程标题
            item['title'] = box.xpath('.//h3[@class="course-card-name"]/text()').extract()[0].strip()
            #获取div中的标题图片地址
            item['image_url'] =  'http:'   box.xpath('.//@src').extract()[0]
            #获取div中的学生人数
            item['student'] = box.xpath('.//span/text()').extract()[1].strip()
            #获取div中的课程简介
            item['introduction'] = box.xpath('.//p/text()').extract()[0].strip()
            #返回信息
            yield item

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

修改成功后就可以自动进行url跟进了。

0 人点赞