高级爬虫( 二):Scrapy爬虫框架初探

2018-10-22 14:11:48 浏览数 (1)

写在前面 最近公众号更新的比较少,有几方面原因: 一 . 随着公众号的深入,需要把文章质量进行提升,不会为了更新而更新 二 . 最近在为公众号做了一个小程序,花了点时间,

先确保你已经在电脑上安装好了Scrapy模块,说一下Scrapy安装的问题,网上大部分安装办法已经失效了,主要是因为 网站:https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted 中 twised资源已经被移除 这导致安装scrapy不能愉快的时行了. 好在我已经给了scrapy 安装的办法,见文章: 高级爬虫(一):Scrapy爬虫框架的安装 当然如果你想用Anaconda 方式来安装也行,只是个人觉得杀鸡用牛刀,哈哈,随意吧!

创建爬虫项目

在电脑上新建一个Scrapy项目,在命令行中切换到要存储的位置. D:workmy_pythonpython_scrapy 这是我要创建Scrapy项目的地址,然后运行命令 scrapy startproject csdnSpider 即可创建一个名为csdnSpider的项目. 这个时候csdnSpider文件夹下面会有如下文件: 效果图:

简单介绍一下各文件的功能

scrapy.cfg 项目部署文件 csdnSpider/: csdnSpider/:items.py 这里主要是做爬虫提取字段 csdnSpider/:pipelines.py 对爬虫字段的进一步处理,如去重,清洗,入库 csdnSpider/:settings.py 项目的配置文件 csdnSpider/:spiders.py 这里主要做爬虫操作

创建爬虫模块

爬虫模块的代码都放置于spiders文件夹中,用于从单个或者多个网站爬取数据的类,其应该包含初始页面的URL,以及跟进网页的链接,分析页内容与提取数据的函数,创建一个Spider类,需要继承scrapy.Spider类,并且定义三个属性:

name: 用于区别Spider,必须是唯一的 start_urls: 启动时爬取入口的URL列表,后续的URL则从初始的URL的响应中主动提取 parse(): 这是Spider的一个方法,被调用时,每个初始URL响应后返回的Response对象,会作为唯一的参数传递给该方法,该方法负责解析返回的数据(reponse data),提取数据(生成item) 以及生成需要进一步处理的URL的Request对象

用Pycharm打开我们刚创建的csdnspider项目,编写爬虫模块代码:

代码语言:javascript复制
import scrapy

class csdnspider(scrapy.Spider): # 必须继承scrapy.Spider
    name = "csdn" #爬虫名称,这个名称必须是唯一的
    allowed_domains=["csdn.net"] #允许的域名
    start_urls = [
        "https://www.csdn.net/nav/ai"
    ]

    def parse(self, response):
        # 实现网页的解析
     pass

然后调用 在命令行中 进入目录csdnspider中,注意这里目录应该是于scrapy.cfg 同级, 运行命令: scrapy cralw csdn 其中csdn是我刚刚在爬虫模块定义的name.

效果图:

这样就创建成功了!

解析Html字段(提取爬虫字段)

之前的xpath与css已经讲过,这里说一下Selector用法,Selector对象有四个基本方法 :

  1. xpath(query) 返回表达式所对应的所有人节点的selector list列表
  2. css(query) 返回表达式所对应的所有人节点的selector list列表
  3. extract() 序列化该节点为Unicode字符串并返回列表
  4. re(regex) 根据传入的正则表达式对数据进行提取,返回一个unicode字符串列表。

在csdnspider类的parse()方法中,其中一个参数是response,将response传入的Selector(response)中就可以构造出一个Selector对象。

小技巧: 我们在爬虫的时候,更多的是对爬取字段的表达式构造。Scrapy提供了一种简便的方式来查看表达式是否正确有效.

新打开一个命令窗口:输入D:workmy_pythonpython_scrapy>scrapy shell "https://www.csdn.net/nav/ai"

效果图:

接着直接输入:response.xpath("//*[@id='feedlist_id']/li[1]/div/div[2]/h2/a/text()").extract() 可以查看自己提取的字段是否正确:

效果图如下:

还可以查看自己爬取的网页,接着输入命令view(response) 可以查看整个网页,效果图如下:

提取爬虫字段:

代码语言:javascript复制
import scrapy

class csdnspider(scrapy.Spider): # 必须继承scrapy.Spider
    name = "csdn" #爬虫名称,这个名称必须是唯一的
    allowed_domains=["csdn.net"] #允许的域名
    start_urls = [
        "https://www.csdn.net/nav/ai"
    ]

    def parse(self, response):
        # 实现网页的解析
        datas = response.xpath('//*[@id="feedlist_id"]/li/div')
        for data in datas:
            read_count = data.xpath('./div[2]/h2/a/text()').extract()[0]
            title = data.xpath('./div[1]/p/text()').extract()[0]
            print(read_count,title)

        pass

运行命令行: scrapy crawl csdn 效果图如下:

定义爬取字段(定义Item)

爬取的主要目标是从非结构性的数据源提取结构性数据. csdnspider类的parse()方法解析出了read_count,title等数据,但是如何将这些数据包装成结构化数据呢,scrapy提供了Item类来满足这样的需求. Item对象是一种简单的容器,用来保存爬取到的数据,Item使用简单的class定义语法以及Field对象来声明.

在我们创建Scrapy项目的时候,这个类已经给我们创建好了. 在项目中找到items文件 可以看到 CsdnspiderItem

类,在这里我们声明两个字段 read_count,title

代码语言:javascript复制
class CsdnspiderItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    read_count = scrapy.Field()
    title = scrapy.Field()
    pass

在csdnspider中使用,注意需要导入CsdnspiderItem

代码语言:javascript复制
    def parse(self, response):
        # 实现网页的解析
        datas = response.xpath('//*[@id="feedlist_id"]/li/div')
        for data in datas:
            read_count = data.xpath('./div[2]/h2/a/text()').extract()[0]
            title = data.xpath('./div[1]/p/text()').extract()[0]
            print(read_count, title)
            item = CsdnspiderItem(read_count=read_count, title=title) # 封装成Item对象
            yield item

代码最后使用了yield关键字来提交item ,将parse方法打造成一个生成器.

构建 Item Pipeline

前面说了网页的下载,解析和数据item,现在我们需要把数据进行持久化存储,这就要用到Item Pipeline,当Item在Spider中被收集之后,它就会被传递到Item Pipeline,一些组件会按照一定的顺序执行对Item处理.

Item pipeline主要有以下应用

  1. 清理HTML数据
  2. 验证爬取数据的合法性,检查Item是否包含某些字段
  3. 查重并丢弃
  4. 将爬取的结果保存到文件或数据库中.
定制Item Pipeline

每个Item Pipeline 组件是一个独立的Python类,必须实现process_item方法,方法原型如下:

process_item(self,item,spider)

每个Item Pipelime组件都需要调用这个方法,这个方法必须返回一一个Item对象,或者抛出DropItem异常,被抛弃的Item将不会被之后的Pipeline组件所处理.

参数说明:

Item对象是被爬取的对象 Spider对象代表着爬取该Item的Spider

我们需要将ccsdn爬虫爬取的Item存储到本地,定制的Item Pipeline位于csdnspider/pipelimes.py 声明csdnspiderPipeline类,完整内容如下:

代码语言:javascript复制
import json
from scrapy.exceptions import DropItem

class CsdnspiderPipeline(object):

    def __init__(self):
        self.file = open('data.json','wb')


    def process_item(self, item, spider):
        if item['title']:
            line = json.dumps(dict(item)) "n"
            self.file.write(line)
            return item
        else:
            raise DropItem("Missing title in %s" %item)

process_item方法中,先判断title,如果存在就存下来,否则就抛弃。 这里有多种存储方式,你也可以把数据处处到execl,数据库中.

激活Item Pipeline

定制完Item Pipeline,它是无法工作的,需要时行激活,要启动一个Item Pipeline组件,必须将它的类添加到settings.py中的ITEM_PIPELINES变量中,代码如下:

代码语言:javascript复制
ITEM_PIPELINES = {
   'csdnSpider.pipelines.CsdnspiderPipeline': 300,
}

ITEM_PIPELINES变量中可以配置很多个Item Pipeline组件,分配给每个类的整型值确定了它们运行的顺序,item按数字从低到高的顺序通过Item Pipeline,通常数字定义范围是0-1000

激活完成后,执行命令行scrapy crawl csdn, 就可以把数据存到data.json文件中

效果图:

内置数据存储

除了使用Item Pipeline实现存储功能,Scrapy内置了一些简单的存储方式,生成一个带有爬取数据的输出文件,通过叫输出(feed),并支持多种序列化格式,自带的支持类型有

json jsonlines csv xml pickle marsha1

调用的时候直接输入命令行 scrapy crawl csdn -o data.csv 注意后面的文件类型csv可以变化的,你也可以输入json,jsonlines等不同格式,可以得到不同文件. 效果如图:

Scrapy爬虫调试

调试方法

scrapy有三种比较常用的调试方式:Parse命令,Scrapy Shell和Logging(使用起来不方便,不介绍)

  • Parse命令

检查spider输出的最基本方法是使用Parse命令,这能让你在函数层检查Spider各个部分效果,其十分灵活且易用

查看特定url爬取到的item 命令格式为

scrapy parse --spider=<spidername> -c <parse> -d 2 <item_url>

在命令行中切换到项目目录下输入

scrapy parse --spider=csdn -c parse -d 2 "https://www.csdn.net/nav/ai"

注意:spider=之间是没有空格的 ,切记,刚刚犯了错误

得到效果图如下:

  • Scrapy shell

尽管使用Parse命令对检查spider的效果十分有用,但除了显示收到的response及输出外,期对检查回调函数内部的过程并没有什么便利,这个时候可以通过scrapy.shell.inspect_response方法来查看spider的某个位置中被处理的response,以确认期望的response是否到达特定位置,需要在csdnspider 中 parse方法里添加代码 :

代码语言:javascript复制
    def parse(self, response):
        # 实现网页的解析
        datas = response.xpath('//*[@id="feedlist_id"]/li/div')

        # 检查代码是否达到特定位置
        from  scrapy.shell   import inspect_response
        inspect_response(response,self)

        for data in datas:
            read_count = data.xpath('./div[2]/h2/a/text()').extract()
            title = data.xpath('./div[1]/p/text()').extract()

            read_count = read_count[0] if len(read_count) > 0 else ''
            title = title[0] if len(title) > 0 else ''

            print(read_count, title)
            item = CsdnspiderItem(read_count=read_count, title=title)  # 封装成Item对象
            yield item

        pass

这里我们可以使用xpath来检验我们的提取方式是否正确,如果调试完了,可以使用输入exit()退出终端,恢复爬取,当程序再次运行到inspect_response方法时再次暂停,这样可以帮助我们了解每一个响应细节

效果图:

Pycharm中调试Scrapy

因为使用Pycharm我们可以更清楚的设置断点来爬虫,所以我比较推荐在Pycharm来调试.Scrapy提供了API让我们在程序中启动爬虫 下面给csdn爬虫添加启动脚本.在我们的爬虫模块类中添加代码, 为了让大家看得清楚一些,我放了完整代码,主要看最下面的main方法, 然后在代码中打断点,和我们平台调试代码一样就行,可以清晰看到我们的调试情况

代码语言:javascript复制
import scrapy

from csdnSpider.items import CsdnspiderItem
from scrapy.crawler import CrawlerProcess

class csdnspider(scrapy.Spider):  # 必须继承scrapy.Spider
    name = "csdn"  # 爬虫名称,这个名称必须是唯一的
    allowed_domains = ["csdn.net"]  # 允许的域名
    start_urls = [
        "https://www.csdn.net/nav/ai"
    ]

    def parse(self, response):
        # 实现网页的解析
        datas = response.xpath('//*[@id="feedlist_id"]/li/div')

        # # 检查代码是否达到特定位置
        # from  scrapy.shell   import inspect_response
        # inspect_response(response,self)


        for data in datas:
            read_count = data.xpath('./div[2]/h2/a/text()').extract()
            title = data.xpath('./div[1]/p/text()').extract()

            read_count = read_count[0] if len(read_count) > 0 else ''
            title = title[0] if len(title) > 0 else ''

            print(read_count, title)
            item = CsdnspiderItem(read_count=read_count, title=title)  # 封装成Item对象
            yield item

        pass


if __name__ == '__main__':
    process = CrawlerProcess({
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.104 Safari/537.36 Core/1.53.4620.400 QQBrowser/9.7.13014.400'
    })
    process.crawl(csdnspider)
    process.start()

Scrapy工作流程

我故意把这个架构图放在最后来说,因为刚开始看这个架构图,我也是一头雾水,随着深入了解,逐渐理解了这个架构图.

架构图如下:

再回头看:

  1. 首先在Spiders中进行调度Scheduler请求,
  2. 然后发起一系列请求Requests 到Downloader中,
  3. 然后再是Downloder响应 Response到Spiders中,
  4. 接着就是数据采集到Items中
  5. 然后Item Pipeline来处理数据,
  6. 接着再进行下一轮请求,直到没有更多的请求,引擎关闭该网站

这就是整个Scrapy的工作流程.

最后说一下:高级爬虫部分即将做完了,届时我的知识星球将会涨价,所以还没有加入星球的朋友们,抓紧时间了! 点击阅读原文加入星球,一起愉快的学习Python! 星球介绍:一起来Python项目实战,约吗?

0 人点赞