Scrapy+爬取豆瓣电影Top250信息

834 阅读5分钟

「这是我参与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 MiddlewaresSpider中间件,自定义扩展和操作引擎与爬虫之间通信的功能组件

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  
}