Scrapy+爬取豆瓣电影Top250信息Scrapy爬虫

「这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战

Scrapy爬虫框架

scrapy是什么

它是一个快速功能强大的开源网络爬虫框架
Github地址:github.com/scrapy/scra…
官网地址:scrapy.org/

在这里插入图片描发

scrapy的安装

cmd上运行

pip install scrapy
测试: scrapy -h

一般直接pip install scrapy会出错,可参考:【转】
blog.csdn.net/qq_42543250…
安装成功后测试会(scrapy -h):
在这里插入图片描述

Scrapy爬虫框架结构

“5+2”结构
在这里插入图片描述

框架组件:

组件 作用
Scrapy Engine 引擎,处理整个框架的数据流
Scheduler 调度器,接收引擎发过来的请求,将其排至队列中,当引擎再次请求时返回
Downloader 下载器,下载所有引擎发送的请求,并将获取的源代码返回给引擎,之后由引擎交给爬虫处理
Spiders 爬虫,接收并处理所有引擎发送过来的源代码,从中分析并提取item字段所需要的数据,并将需要跟进的url提交给引擎,再次进入调度器
Item Pipeline 管道,负责处理从爬虫中获取的Item,并进行后期处理
Downloader Middlewares 下载中间件,可以理解为自定义扩展下载功能的组件
Spider Middlewares Spider中间件,自定义扩展和操作引擎与爬虫之间通信的功能组件

Scrapy爬虫的数据类型

  • Request类
  • Response类
  • Item类

Scrapy数据处理流程:

  1. 当需要打开一个域名时,爬虫开始获取第一个url,并返回给引擎
  2. 引擎把url作为一个请求交给调度器
  3. 引擎再次对调度器发出请求,并接收上一次让调度器处理的请求
  4. 引擎将请求交给下载器
  5. 下载器下载完成后,作为响应返回给引擎
  6. 引擎把响应交给爬虫,爬虫开始进一步处理,处理完成后有两个数据,一个是需要跟进的url,另一个是获取到的item数据,然后把结果返回给引擎
  7. 引擎把需要跟进的url给调度器,把获取的item数据给管道
  8. 然后从第2步开始循环,知道获取信息完毕。只有调度器中没有任何请求时,程序才会停止

Scrapy爬虫的基本使用

yield关键字的使用

  • 包含yield语句的函数是一个生成器
  • 生成器每次产生一个值(yield语句),函数被冻结,被唤醒后再产生一个值
  • 生成器是一个不断产生值的函数

Scrapy爬虫的常用命令

命令 说明 格式
startproject 创建一个新工程 scrapy startproject projectName
genspider 创建一个爬虫 scrapy genspider [options]name domain
settings 获得爬虫配置信息 scrapy settings [options]
crawl 运行一个爬虫 scrapy crawl spider
list 列出工程中所有爬虫 scrapy list
shell 启动URL调试命令行 scrapy shell [url]

Scrapy爬虫的使用步骤

  1. 新建项目 (scrapy startproject xxx):新建一个新的爬虫项目

创建工程:scrapy startproject mydemo

在这里插入图片描述

目录树:
在这里插入图片描述

工程目录下各个文件的作用

文件 作用
scrapy.cfg 配置文件
spiders 存放你Spider文件,也就是你爬取的py文件
items.py 相当于一个容器,和字典较像
middlewares.py 定义Downloader Middlewares(下载器中间件)和Spider Middlewares(蜘蛛中间件)的实现
pipelines.py 定义Item Pipeline的实现,实现数据的清洗,储存,验证。
settings.py 全局配置
  1. 明确目标 (编写items.py):明确你想要抓取的目标

items.py文件内容

在这里插入图片描述

  1. 制作爬虫 (spiders/xxspider.py):制作爬虫开始爬取网页

Spider爬虫模板:
在这里插入图片描述

  1. 存储内容 (pipelines.py):设计管道存储爬取内容

实例:豆瓣Top250信息-Scrapy爬虫

创建工程:

scrapy startproject douban
在douban目录下:
scrapy genspider douban_scrapy douban.com

明确目标

我们打算抓取movie.douban.com/top250
网站里的所有电影的序号、名称、介绍、评分、评论数、描述

打开 douban 目录下的 items.py。

Item 定义结构化数据字段,用来保存爬取到的数据,有点像 Python 中的 dict,但是提供了一些额外的保护减少错误。

可以通过创建一个 scrapy.Item 类, 并且定义类型为 scrapy.Field 的类属性来定义一个 Item(可以理解成类似于 ORM 的映射关系)。

接下来,创建一个 ItcastItem 类,和构建 item 模型(model)。
复制代码

items.py

import scrapy  
class DoubanItem(scrapy.Item):  
    # define the fields for your item here like:
    # name = scrapy.Field()
    # 电影序号
    serial_number = scrapy.Field()
    # 电影名称
    movie_name = scrapy.Field()
    # 电影介绍
    introduce = scrapy.Field()
    # 电影星级
    star = scrapy.Field()
    # 电影评论数
    evaluate = scrapy.Field()
    # 电影描述
    describe = scrapy.Field()

复制代码

制作爬虫 (spiders/douban_scrapy.py)

在当前目录下输入命令,将在mySpider/spider目录下创建一个名为itcast的爬虫,并指定爬取域的范围:

scrapy genspider douban_scrapy movie.douban.com

打开 douban/spider目录里的 douban_scrapy.py,默认增加了下列代码:

#douban_scrapy.py
#-*- coding: utf-8 -*-  
import scrapy  
from douban.items import DoubanItem  

class DoubanScrapySpider(scrapy.Spider):  
    name = 'douban_scrapy'  
    allowed_domains = ['movie.douban.com']  
    start_urls = ['https://movie.douban.com/top250']  

    def parse(self, response):  # 解析的方法
        #  movie_list 的类型为<class 'scrapy.selector.unified.SelectorList'>
        movie_list = response.xpath("//ol[@class ='grid_view']/li")
        # 数据的查找
        # self.log('movie_list 的类型为{}
        for i_item in movie_list:
            # item文件的导入
            douban_item = DoubanItem()
            # 数据的筛选
            #extract():这个方法返回的是一个数组list,,里面包含了多个string,如果只有一个string,则返回['ABC']这样的形式。
            #extract_first():这个方法返回的是一个string字符串,是list数组里面的第一个字符串。
            douban_item['serial_number'] = i_item.xpath(".//div[@class='pic']/em/text()").extract_first()
            douban_item['movie_name'] = i_item.xpath(".//div[@class='hd']/a/span[1]/text()").extract_first()
            douban_item['introduce'] = i_item.xpath(".")
            content = i_item.xpath(".//div[@class='bd']/p[1]/text()").extract()
            for i_content  in content:
                contents = "".join(i_content.split())
                douban_item['introduce'] = contents
            douban_item['star'] = i_item.xpath(".//div[@class='star']/span[2]/text()").extract_first()
            douban_item['evaluate'] = i_item.xpath(".//div[@class='star']/span[4]/text()").extract_first()
            douban_item['describe'] = i_item.xpath(".//p[@class= 'quote']/span/text()").extract_first()
            #将数据返回到pipeline,用生成器
            yield douban_item
        next_link = response.xpath("//span[@class ='next']/link/@href").extract()
        # 解析下一页,规则,取后一页的xpath
        if next_link:
            next_link = next_link[0]
            #Spider产生下一页的Request请求
            yield scrapy.Request('https://movie.douban.com/top250'+next_link,callback=self.parse)
复制代码

保存数据 (pipeline.py)

一.scrapy保存信息的最简单的方法主要有四种,-o 输出指定格式的文件,命令如下:

scrapy crawl douban_scrapy -o douban.json

json lines格式,默认为Unicode编码

scrapy crawl douban_scrapy -o douban.jsonl

csv 逗号表达式,可用Excel打开

scrapy crawl douban_scrapy -o douban.csv

xml格式

scrapy crawl douban_scrapy -o douban.xml

二、通过pipeline存储进mysql

pipeline.py
import pymysql  
from twisted.enterprise import adbapi  
from scrapy  import log   


class DoubanPipeline(object):  
    #使用teisted异步存储
    def __init__(self, dbpool):
        self.dbpool = dbpool

    @classmethod
    def from_settings(cls, settings):
        dbparms = {
            'host': "localhost",
            'user': "root",
            'port': 3306,
            'passwd': "root",
            'db': "mystudy",
            'charset': 'utf8',
            'cursorclass': pymysql.cursors.DictCursor,
            'use_unicode': True
        }

        dbpool = adbapi.ConnectionPool('pymysql', **dbparms)
        return cls(dbpool)

    def process_item(self, item, spider):
        # 使用Twisted 将MYSQL 插入变成异步执行
        # runInteraction 第一个参数是一个函数
        query = self.dbpool.runInteraction(self.do_insert, item)
        query.addCallback(self.handle_error, item, spider)  # 处理异常
        return item

    def handle_error(self, failure, item, spider):
        # 处理异步插入的异常
        print(failure)

    def do_insert(self, cursor, item):
        # 执行具体的插入
        insert_sql = '''
            insert into douban (serial_number,movie_name,introduce,star, evaluate,Mdescribe) values (%s, %s, %s, %s, %s, %s);
            '''
        cursor.execute(insert_sql,
                       (item['serial_number'], item['movie_name'], item['introduce'], item['star'], item['evaluate'],item['describe']))

复制代码

一些配置

#settings.py  
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36'  
ROBOTSTXT_OBEY = False  
ITEM_PIPELINES = {   
   'douban.pipelines.DoubanPipeline':10  
}
复制代码