zoukankan      html  css  js  c++  java
  • Python——Scrapy初学

    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%20220/。

    在安装好模块后要注意环境变量的配置,以我自己的安装目录为例,应当将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:

    右键审查元素查看

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

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

    #获取每个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目录:

    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中的内容修改如下:

    #引入文件
    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以及用于声明可用字段的简单语法。常用方法如下:

    #定义一个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的代码如下

    #引入文件
    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的代码应该是这样的:

    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。 

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

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

    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的代码如下:

    # -*- 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) + "
    "
            #写入文件
            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文件,这个文件时爬虫的配置文件

    在其中添加:

    ITEM_PIPELINES = {
        'tutorial.pipelines.MyPipeline': 1,
    }

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

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

    这时我们再运行:

    scrapy crawl MySpider

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

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

    url跟进

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

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

    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跟进了。

     

     

     

  • 相关阅读:
    html5 桌面提醒:Notifycations
    windows 下 apache 的虚拟主机配置
    javascript 跨域
    javascript 类型数组读取二进制数据
    javascript parseInt() 函数的进制转换陷阱
    javascript 中几个与正则表达式相关的应用
    javascript 中的二进制运算
    一段小代码,发布网页时为js 、css 文件加上版本号
    base64 编码及解码
    PHP 的比较运算与逻辑运算
  • 原文地址:https://www.cnblogs.com/wwf828/p/7716410.html
Copyright © 2011-2022 走看看