Scrapy爬虫框架入门

1.Scrapy概述

Scrapy是Python开发的一个非常流行的网络爬虫框架,可以用来抓取Web站点并从页面中提取结构化的数据,被广泛的用于数据挖掘、数据监测和自动化测试等领域。下图展示了Scrapy的基本架构,其中包含了主要组件和系统的数据处理流程(图中带数字的红色箭头)。

2.组件

  1. Scrapy引擎(Engine):Scrapy引擎是用来控制整个系统的数据处理流程。
  2. 调度器(Scheduler):调度器从Scrapy引擎接受请求并排序列入队列,并在Scrapy引擎发出请求后返还给它们。
  3. 下载器(Downloader):下载器的主要职责是抓取网页并将网页内容返还给蜘蛛(Spiders)。
  4. 蜘蛛(Spiders):蜘蛛是有Scrapy用户自定义的用来解析网页并抓取特定URL返回的内容的类,每个蜘蛛都能处理一个域名或一组域名,简单的说就是用来定义特定网站的抓取和解析规则。
  5. 条目管道(Item Pipeline):条目管道的主要责任是负责处理有蜘蛛从网页中抽取的数据条目,它的主要任务是清理、验证和存储数据。当页面被蜘蛛解析后,将被发送到条目管道,并经过几个特定的次序处理数据。每个条目管道组件都是一个Python类,它们获取了数据条目并执行对数据条目进行处理的方法,同时还需要确定是否需要在条目管道中继续执行下一步或是直接丢弃掉不处理。条目管道通常执行的任务有:清理HTML数据、验证解析到的数据(检查条目是否包含必要的字段)、检查是不是重复数据(如果重复就丢弃)、将解析到的数据存储到数据库(关系型数据库或NoSQL数据库)中。
  6. 中间件(Middlewares):中间件是介于Scrapy引擎和其他组件之间的一个钩子框架,主要是为了提供自定义的代码来拓展Scrapy的功能,包括下载器中间件和蜘蛛中间件。
    Scrapy爬虫框架入门

3.数据处理流程

Scrapy的整个数据处理流程由Scrapy引擎进行控制,通常的运转流程包括以下的步骤:

  1. 引擎询问蜘蛛需要处理哪个网站,并让蜘蛛将第一个需要处理的URL交给它。
  2. 引擎让调度器将需要处理的URL放在队列中。
  3. 引擎从调度那获取接下来进行爬取的页面。
  4. 调度将下一个爬取的URL返回给引擎,引擎将它通过下载中间件发送到下载器。
  5. 当网页被下载器下载完成以后,响应内容通过下载中间件被发送到引擎;如果下载失败了,引擎会通知调度器记录这个URL,待会再重新下载。
  6. 引擎收到下载器的响应并将它通过蜘蛛中间件发送到蜘蛛进行处理。
  7. 蜘蛛处理响应并返回爬取到的数据条目,此外还要将需要跟进的新的URL发送给引擎。
  8. 引擎将抓取到的数据条目送入条目管道,把新的URL发送给调度器放入队列中。

上述操作中的2-8步会一直重复直到调度器中没有需要请求的URL,爬虫停止工作。

4.安装和使用Scrapy

pip install scrapy -i https://pypi.douban.com/simple
  1. 创建scrapy项目:

     		  终端输入   scrapy startproject  项目名称
     	   `scrapy startproject douban`
    
  2. 创建爬虫文件:

     	(1)跳转到spiders文件夹   cd 目录名字/目录名字/spiders
     	(2)scrapy genspider 爬虫名字 网页的域名
    
     		 `cd douban/douban/spiders`
    
     		 `scrapy genspider movie movie.douban.com`
    

scrapy genspider 创建的爬虫名字不能你 startproject 的项目重名

项目组成

➜  scrapydemo: tree             
.
└── douban
    ├── douban
    │   ├── __init__.py
    │	├── 自定义的爬虫文件.py       ---> 由我们自己创建,是实现爬虫核心功能的文件
    │   ├── items.py			---> 定义数据结构的地方,是一个继承自scrapy.Item的类
    │   ├── middlewares.py		---> 中间件   代理
    │   ├── pipelines.py		---> 管道文件,里面只有一个类,用于处理下载数据的后续处理
    │   ├── __pycache__
    │   ├── settings.py			 ---> 配置文件  比如:是否遵守robots协议,User-Agent定义等
    │   └── spiders
    │       ├── __init__.py
    │       └── __pycache__
    └── scrapy.cfg

说明:Windows系统的命令行提示符下有tree命令,但是Linux和MacOS的终端是没有tree命令的,可以用下面给出的命令来定义tree命令,其实是对find命令进行了定制并别名为tree。

alias tree="find . -print | sed -e 's;[^/]*/;|____;g;s;____|; |;g'"

Linux系统也可以通过yum或其他的包管理工具来安装tree。

yum install tree

爬虫文件的基本组成:
  		继承scrapy.Spider类
                name = 'movie'    --->   运行爬虫文件时使用的名字
                allowed_domains   --->  爬虫允许的域名,在爬取的时候,如果不是此域名之下的url,会被过滤掉
                start_urls	--->  声明了爬虫的起始地址,可以写多个url,一般是一个
                parse(self, response) ---> 解析数据的回调函数
                       response.text
                       response.body ---> 响应的是二进制文件
                       response.xpath()-> xpath方法的返回值类型是selector列表
                       extract()             ---> 提取的是selector对象的是data
                       extract_first()       ---> 提取的是selector列表中的第一个数据

5.开始爬虫

1. 在items.py文件中定义字段,这些字段用来保存数据,方便后续的操作。

# -*- coding: utf-8 -*-

# Define here the models for your scraped items
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/items.html

import scrapy


class DoubanItem(scrapy.Item):

    name = scrapy.Field()
    year = scrapy.Field()
    score = scrapy.Field()
    director = scrapy.Field()
    classification = scrapy.Field()
    actor = scrapy.Field()

2. 在spiders文件夹中编写自己的爬虫。

(venv) $ scrapy genspider movie movie.douban.com
# -*- coding: utf-8 -*-
import scrapy
from scrapy.selector import Selector
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule

from douban.items import DoubanItem


class MovieSpider(CrawlSpider):
    name = 'movie'
    allowed_domains = ['movie.douban.com']
    start_urls = ['https://movie.douban.com/top250']
    rules = (
        Rule(LinkExtractor(allow=(r'https://movie.douban.com/top250\?start=\d+.*'))),
        Rule(LinkExtractor(allow=(r'https://movie.douban.com/subject/\d+')), callback='parse_item'),
    )

    def parse_item(self, response):
        sel = Selector(response)
        item = DoubanItem()
        item['name']=sel.xpath('//*[@]/h1/span[1]/text()').extract()
        item['year']=sel.xpath('//*[@]/h1/span[2]/text()').re(r'\((\d+)\)')
        item['score']=sel.xpath('//*[@]/div/p[1]/strong/text()').extract()
        item['director']=sel.xpath('//*[@]/span[1]/a/text()').extract()
        item['classification']= sel.xpath('//span[@property="v:genre"]/text()').extract()
        item['actor']= sel.xpath('//*[@]/span[3]/a[1]/text()').extract()
        return item

说明:上面我们通过Scrapy提供的爬虫模板创建了Spider,其中的rules中的LinkExtractor对象会自动完成对新的链接的解析,该对象中有一个名为extract_link的回调方法。Scrapy支持用XPath语法和CSS选择器进行数据解析,对应的方法分别是xpath和css,上面我们使用了XPath语法对页面进行解析

3. 运行爬虫

venv)$ scrapy crawl movie

可以在控制台看到爬取到的数据,如果想将这些数据保存到文件中,可以通过-o参数来指定文件名,Scrapy支持我们将爬取到的数据导出成JSON、CSV、XML、pickle、marshal等格式。

(venv)$ scrapy crawl moive -o result.json

4. 在pipelines.py中完成对数据进行持久化的操作。

# -*- coding: utf-8 -*-

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

from scrapy.exceptions import DropItem
from scrapy.conf import settings
from scrapy import log


class DoubanPipeline(object):

    def __init__(self):
        connection = pymongo.MongoClient(settings['MONGODB_SERVER'], settings['MONGODB_PORT'])
        db = connection[settings['MONGODB_DB']]
        self.collection = db[settings['MONGODB_COLLECTION']]

    def process_item(self, item, spider):
        #Remove invalid data
        valid = True
        for data in item:
          if not data:
            valid = False
            raise DropItem("Missing %s of blogpost from %s" %(data, item['url']))
        if valid:
        #Insert data into database
            new_moive=[{
                "name":item['name'][0],
                "year":item['year'][0],
                "score":item['score'],
                "director":item['director'],
                "classification":item['classification'],
                "actor":item['actor']
            }]
            self.collection.insert(new_moive)
            log.msg("Item wrote to MongoDB database %s/%s" %
            (settings['MONGODB_DB'], settings['MONGODB_COLLECTION']),
            level=log.DEBUG, spider=spider) 
        return item

利用Pipeline我们可以完成以下操作:

  • 清理HTML数据,验证爬取的数据。
  • 丢弃重复的不必要的内容。
  • 将爬取的结果进行持久化操作。

5. 修改settings.py文件对项目进行配置

.......
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.54 Safari/536.5'

MONGODB_SERVER = 'xxx.xxx.xxx.xxx'
MONGODB_PORT = 27017
MONGODB_DB = 'douban'
MONGODB_COLLECTION = 'movie'

ITEM_PIPELINES = {
    'douban.pipelines.DoubanPipeline': 400,  # 优先级 数字越低优先级越高
}

ROBOTSTXT_OBEY = True    # 遵不遵守root协议