第04章 Scrapy 入门

 

 

序言

1. 内容介绍

本章详细介绍了Scrapy框架的安装,通过Scrapy框架架构,讲解了Scrapy各个组件的作用。

2. 理论目标

  • 掌握Scrapy安装
  • 了解Scrapy组件结构和工作流程

3. 实践目标

  • 实现一个简单爬虫项目

4. 实践案例

5. 内容目录

  • 1.Scrapy 简介与安装

  • 2.Scrapy 项目创建

  • 3.Scrapy 进阶


第1节 Scrapy 简介与安装

1. Scrapy 简介

Scrapy是用python实现的一个为了爬取网站数据,提取结构性数据而编写的应用框架;用于抓取web站点并从页面中提取结构化的数据,用于数据挖掘、监测和自动化测试。

0addc2f5ca1274117f2d3b24b1927756.png

2. Scrapy 说明

架构组件构成

Scrapy框架主要由五大组件组成,它们分别是:
调度器(Scheduler)、下载器(Downloader)、爬虫(Spider)和实体管道(Item Pipeline)、Scrapy引擎(Scrapy Engine)。
下面我们分别介绍各个组件的作用。

1419981be4e2338b5312313ed21ec7fd.png

组件 作用说明
Spiders爬虫脚本定义了爬取的逻辑和网页内容的解析规则,主要负责解析响应并生成结果和新的请求
Engine引擎处理整个系统的数据流处理,出发事物,框架的核心。
Scheduler调度器接受引擎发过来的请求,并将其加入队列中,在引擎再次请求时将请求提供给引擎
Downloader下载器下载网页内容,并将下载内容返回给spider
ItemPipeline项目管道负责处理spider从网页中抽取的数据,主要是负责清洗,验证和向数据库中存储数据
Downloader Middlewares下载中间件是处于Scrapy的Request和Requesponse之间的处理模块
Spider Middlewaresspider中间件位于引擎和spider之间的框架,主要处理spider输入的响应和输出的结果及新的请求middlewares.py里实现

架构组件关系

67549ca9352c1f39ca8816a443f83b86.png

流程解析

  1. spiders的yeild将request发送给engin
  2. engine对request不做任何处理发送给scheduler
  3. scheduler( url调度器),生成request交给engin
  4. engine拿到request,通过middleware进行层层过滤发送给downloader
  5. downloader在网上获取到response数据之后,又经过middleware进行层层过滤发送给engin
  6. engine获取到response数据之后,返回给spiders,spiders的parse()方法对获取到的response数据进行处理,解析出items或者requests
  7. 将解析出来的items或者requests(下一个请求)发送给engin
  8. engin获取到items或者requests,将items发送给itempipelines,将requests发送给scheduler

3. Scrapy 安装

安装语法:pip install scrapy -i https://pypi.tuna.tsinghua.edu.cn/simple/

9454114859cebeb8c1e741c27c627eae.png

查看版本语法:scrapy version

7f7c090ddc58a1068d181dc3b1ff56d4.png


第2节 Scrapy 项目创建

1. 创建爬虫项目

命令:

scrapy startproject scrapytest

e55c9d1322c1f7da2ffea377f348fcba.png

a2bbc4785d3b8d146149ab54ffc12fac.png

文件作用说明

  1. 目录 spider 放置爬虫代码,放置目标url、response解析
  2. 文件 items.py 用于保存所抓取的数据的容器,其存储方式类似于 Python 的字典
  3. 文件 middlewares.py 提供一种简便的机制,通过允许插入自定义代码来拓展 Scrapy 的功能(少用)
  4. 文件 pipelines.py 用于把解析好的数据 items 输出到数据库或指定文件
  5. 文件 settings.py 用于配置爬虫信息(如头部信息、并行数、优先级等)
  6. 文件 scrapy.cfg 用于项目级别的配置(少用)

2. 创建爬虫应用

进入项目目录 cd scrapytest

创建爬虫 scrapy genspider douban “https://movie.douban.com/top250”

5c83ff5ab148825afa0656ccbc9656bf.png

b55730869d5bdb6bb0a15740ed39da46.png

3. 配置爬虫信息

获取头部信息

 

配置头部信息

文件:settings.py

  1. 填写头部信息
  2. ROBOTSTXT_OBEY 设置为False,即不遵守robots.txt,否则好多网站都爬不到

47b757e4f64860b71a41f5301fd92591.png

64cf928b1a646a24148a376e9cabecee.png

配置爬虫脚本

文件:douban.py

  1. 检查 start_urls 的值是否目标网站url,因为 Scrapy 有可能会进行修改
  2. 删除 allowed_domains,此处为限制爬取的范围,最好注释
 

import scrapy class DoubanSpider(scrapy.Spider): name = 'douban' # allowed_domains = ['https://movie.douban.com/top250'] start_urls = ['https://movie.douban.com/top250'] movieN = 1 def parse(self, response): pass

4. 提取爬虫信息

页面分析

网页模型按盒子模型组成

uploading.4e448015.gif正在上传…重新上传取消

分析页面模块层级、元素构成

754bfc6ebae56789267fd7d27a5522c6.png

提取路径测试

启动shell测试:scrapy shell https://movie.douban.com/top250 下载目标网站页面

06b6767d63d2f5ebdfb410f1affed565.png

查看下载到的页面内容

cac444617175a2aa21f0cd4689c94f44.png

获取xpath 路径

 

获取到的xpath路径:

 

/html/body/div[3]/div[1]/div/div[1]/ol/li[1]/div/div[2]/div[1]/a

根据xpath 获取文本语法,在后面补充成:

/html/body/div[3]/div[1]/div/div[1]/ol/li[1]/div/div[2]/div[1]/a//text()

cmd 窗口获取相应信息语句:

response.xpath("/html/body/div[3]/div[1]/div/div[1]/ol/li[1]/div/div[2]/div[1]/a//text()").getall()

81693a5de11326d4738ed3f09fb3a58b.png

依次类推,获取所有信息路径:

  1. 电影名称:/html/body/div[3]/div[1]/div/div[1]/ol/li[1]/div/div[2]/div[1]/a
  2. 电影创作人员:/html/body/div[3]/div[1]/div/div[1]/ol/li[1]/div/div[2]/div[2]/p[1]
  3. 电影年份、国家、类型:/html/body/div[3]/div[1]/div/div[1]/ol/li[1]/div/div[2]/div[2]/p[1]
  4. 电影评价-星数:/html/body/div[3]/div[1]/div/div[1]/ol/li[4]/div/div[2]/div[2]/div/span[1]
  5. 电影评价-评分:/html/body/div[3]/div[1]/div/div[1]/ol/li[4]/div/div[2]/div[2]/div/span[2]
  6. 电影评价-评价数:/html/body/div[3]/div[1]/div/div[1]/ol/li[4]/div/div[2]/div[2]/div/span[4]
  7. 电影介绍:/html/body/div[3]/div[1]/div/div[1]/ol/li[3]/div/div[2]/div[2]/p[2]/span

完成测试,根据xpath 语法更新如下:

  1. 电影名称
 

name = response.xpath("/html/body/div[3]/div[1]/div/div[1]/ol/li[1]/div/div[2]/div[1]/a//text()").getall()

  1. 电影创作人员
 

movieMakers = response.xpath("/html/body/div[3]/div[1]/div/div[1]/ol/li[1]/div/div[2]/div[2]/p[1]/text()[1]").getall()

  1. 电影年份、国家、类型
 

baseInfo = response.xpath("/html/body/div[3]/div[1]/div/div[1]/ol/li[1]/div/div[2]/div[2]/p[1]/text()[2]").getall()

  1. 电影评价
 

star = response.xpath("/html/body/div[3]/div[1]/div/div[1]/ol/li[4]/div/div[2]/div[2]/div/span[1]/@class").getall() score = response.xpath("/html/body/div[3]/div[1]/div/div[1]/ol/li[4]/div/div[2]/div[2]/div/span[2]/text()").getall() comments = response.xpath("/html/body/div[3]/div[1]/div/div[1]/ol/li[4]/div/div[2]/div[2]/div/span[4]/text()").getall()

  1. 电影介绍
 

desc = response.xpath("/html/body/div[3]/div[1]/div/div[1]/ol/li[3]/div/div[2]/div[2]/p[2]/span/text()").getall()

更新爬虫文件

将测试通过的信息提取代码更新到 douban.py,并通过print()打印出来

 

import scrapy class DoubanSpider(scrapy.Spider): name = 'douban' start_urls = ['https://movie.douban.com/top250'] movieN = 1 def parse(self, response): ## 电影名称 name = response.xpath("/html/body/div[3]/div[1]/div/div[1]/ol/li[1]/div/div[2]/div[1]/a//text()").getall() ## 电影创作人员 movieMakers = response.xpath("/html/body/div[3]/div[1]/div/div[1]/ol/li[1]/div/div[2]/div[2]/p[1]/text()[1]").getall() ## 电影年份、国家、类型 baseInfo = response.xpath("/html/body/div[3]/div[1]/div/div[1]/ol/li[1]/div/div[2]/div[2]/p[1]/text()[2]").getall() ## 电影评价 star = response.xpath("/html/body/div[3]/div[1]/div/div[1]/ol/li[4]/div/div[2]/div[2]/div/span[1]/@class").getall() score = response.xpath("/html/body/div[3]/div[1]/div/div[1]/ol/li[4]/div/div[2]/div[2]/div/span[2]/text()").getall() comments = response.xpath("/html/body/div[3]/div[1]/div/div[1]/ol/li[4]/div/div[2]/div[2]/div/span[4]/text()").getall() ## 电影介绍 desc = response.xpath("/html/body/div[3]/div[1]/div/div[1]/ol/li[3]/div/div[2]/div[2]/p[2]/span/text()").getall() ## 打印电影信息 print(name, movieMakers, baseInfo, star, score, comments, desc)

尝试启动爬虫

scrapy crawl douban

终端尝试启动爬虫,观察输出结果

cec9e2ab31ddf14a205d6770713084b8.png

debe2ec8111742eccebca420a5eeddfc.png

爬虫脚本优化

优化1

整理信息格式,douban.py 文件 parse() 函数内最后增加如下脚本:

 

## 电影名称整理 nameNew = '' for namei in name: namei = namei.replace("\n", "").replace("\xa0", "").strip() nameNew = nameNew + namei # print(nameNew) # ## 电影导演及演员整理 movieMakers = movieMakers[0].strip().replace("\n", "").replace("/...", "") ## 电影基本信息整理:年份、产地、类型 baseInfoList = baseInfo[0].strip().replace("\n", "").replace("\t", "").split("/") Myear = baseInfoList[0].strip() Mcountry = baseInfoList[1].strip() Mtype = baseInfoList[2].strip() # print(Myear, Mcountry, Mtype) ## 其他信息整理 star = star[0] score = score[0] comments = comments[0] if len(desc) != 0: desc = desc[0] else: desc = "" ## 整合所有信息 movieInfoList = [str(DoubanSpider.movieN), nameNew, movieMakers, Myear, Mcountry, Mtype, star, score, comments, desc] movieInfoStr = "^".join(movieInfoList) print(movieInfoStr) DoubanSpider.movieN = DoubanSpider.movieN + 1

再次启动爬虫 scrapy crawl douban,输出结果如下:

0244de22924f8f62a495ebf2f8e630af.png

优化2

以 for 循环提取当前页面所有电影信息

 

import scrapy class DoubanSpider(scrapy.Spider): name = 'douban' start_urls = ['https://movie.douban.com/top250'] movieN = 1 def parse(self, response): # pass movieList = response.xpath("/html/body/div[3]/div[1]/div/div[1]/ol/li") for movieListi in movieList: ## 电影名称 name = movieListi.xpath(".//div/div[2]/div[1]/a//text()").getall() ## 电影创作人员 movieMakers = movieListi.xpath("./div/div[2]/div[2]/p[1]/text()[1]").getall() ## 电影年份、国家、类型 baseInfo = movieListi.xpath("./div/div[2]/div[2]/p[1]/text()[2]").getall() ## 电影评价 star = movieListi.xpath("./div/div[2]/div[2]/div/span[1]/@class").getall() score = movieListi.xpath("./div/div[2]/div[2]/div/span[2]/text()").getall() comments = movieListi.xpath("./div/div[2]/div[2]/div/span[4]/text()").getall() ## 电影介绍 desc = movieListi.xpath("./div/div[2]/div[2]/p[2]/span/text()").getall() # print(name) # print(movieMakers) # print(baseInfo) # print(star) # print(score) # print(comments) # print(desc) ## 电影名称整理 nameNew = '' for namei in name: namei = namei.replace("\n", "").replace("\xa0", "").strip() nameNew = nameNew + namei # print(nameNew) # ## 电影导演及演员整理 movieMakers = movieMakers[0].strip().replace("\n", "").replace("/...", "") ## 电影基本信息整理:年份、产地、类型 baseInfoList = baseInfo[0].strip().replace("\n", "").replace("\t", "").split("/") Myear = baseInfoList[0].strip() Mcountry = baseInfoList[1].strip() Mtype = baseInfoList[2].strip() # print(Myear, Mcountry, Mtype) ## 其他信息整理 star = star[0] score = score[0] comments = comments[0] if len(desc) != 0: desc = desc[0] else: desc = "" ## 整合所有信息 movieInfoList = [str(DoubanSpider.movieN), nameNew, movieMakers, Myear, Mcountry, Mtype, star, score, comments, desc] movieInfoStr = "^".join(movieInfoList) print(movieInfoStr) DoubanSpider.movieN = DoubanSpider.movieN + 1

5. 启动爬虫

方式一、终端执行

命令:

scrapy crawl duoban

6c16cd0587f218dc820f30bbf0e0c3af.png

方式二、脚本执行

新建脚本文件 main.py,通过脚本提交 cmd 命令启动爬虫

4f1a4c7ef1c01cf6de90fc63e6a933fc.png

 

#!/usr/bin/python # -*- coding: utf-8 -*- ''' 需求说明: Scrapy 爬虫入口。 ''' from scrapy import cmdline if __name__ == '__main__': cmdline.execute('scrapy crawl douban'.split())


开始实验

第3节 Scrapy 进阶

1. 爬虫回调爬取下一页

基本思路:

  1. 获取下一页url
  2. 当下一页不是none时,就继续爬取

yield 的作用是边执行边返回,response.follow() 以新一页更新url 继续爬取,并回调解析函数 parse 提取爬取结果

 

import scrapy class DoubanSpider(scrapy.Spider): name = 'douban' start_urls = ['https://movie.douban.com/top250'] movieN = 1 def parse(self, response): # pass movieList = response.xpath("/html/body/div[3]/div[1]/div/div[1]/ol/li") for movieListi in movieList: ## 电影名称 name = movieListi.xpath(".//div/div[2]/div[1]/a//text()").getall() ## 电影创作人员 movieMakers = movieListi.xpath("./div/div[2]/div[2]/p[1]/text()[1]").getall() ## 电影年份、国家、类型 baseInfo = movieListi.xpath("./div/div[2]/div[2]/p[1]/text()[2]").getall() ## 电影评价 star = movieListi.xpath("./div/div[2]/div[2]/div/span[1]/@class").getall() score = movieListi.xpath("./div/div[2]/div[2]/div/span[2]/text()").getall() comments = movieListi.xpath("./div/div[2]/div[2]/div/span[4]/text()").getall() ## 电影介绍 desc = movieListi.xpath("./div/div[2]/div[2]/p[2]/span/text()").getall() # print(name) # print(movieMakers) # print(baseInfo) # print(star) # print(score) # print(comments) # print(desc) ## 电影名称整理 nameNew = '' for namei in name: namei = namei.replace("\n", "").replace("\xa0", "").strip() nameNew = nameNew + namei # print(nameNew) # ## 电影导演及演员整理 movieMakers = movieMakers[0].strip().replace("\n", "").replace("/...", "") ## 电影基本信息整理:年份、产地、类型 baseInfoList = baseInfo[0].strip().replace("\n", "").replace("\t", "").split("/") Myear = baseInfoList[0].strip() Mcountry = baseInfoList[1].strip() Mtype = baseInfoList[2].strip() # print(Myear, Mcountry, Mtype) ## 其他信息整理 star = star[0] score = score[0] comments = comments[0] if len(desc) != 0: desc = desc[0] else: desc = "" ## 整合所有信息 movieInfoList = [str(DoubanSpider.movieN), nameNew, movieMakers, Myear, Mcountry, Mtype, star, score, comments, desc] movieInfoStr = "^".join(movieInfoList) print(movieInfoStr) DoubanSpider.movieN = DoubanSpider.movieN + 1 nextPage = response.xpath('/html/body/div[3]/div[1]/div/div[1]/div[2]/span[3]/a/@href').get() if nextPage is not None: nextPage = DoubanSpider.start_urls[0] + nextPage yield response.follow(nextPage, callback=self.parse)

2. 爬虫结果写入文件

定义容器字段

文件items.py 新建类 DoubanItem,定义数据容器字段

 

# Define here the models for your scraped items # # See documentation in: # https://docs.scrapy.org/en/latest/topics/items.html import scrapy class ScrapytestItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() pass class DoubanItem(scrapy.Item): Mid = scrapy.Field() name = scrapy.Field() movieMakers = scrapy.Field() Myear = scrapy.Field() Mcountry = scrapy.Field() Mtype = scrapy.Field() star = scrapy.Field() score = scrapy.Field() comments = scrapy.Field() desc = scrapy.Field()

8cf2cefd125504ffc40df9d2912afae5.png

更新容器数据

文件 douban.py,引入新建类 DoubanItem,新建类 DoubanItem 的实例对象 doubanItem,并更新对象的值

  • doubanItem 实例对象的键需与 items.py 中类 DoubanItem 的变量名一致,douban.py 增加如下代码:
 

from scrapytest.items import DoubanItem ## 电影信息保存至容器,用法类似Python 的字典 doubanItems = DoubanItem() doubanItems['Mid'] = str(DoubanSpider.movieN) doubanItems['name'] = nameNew doubanItems['movieMakers'] = movieMakers doubanItems['Myear'] = Myear doubanItems['Mcountry'] = Mcountry doubanItems['Mtype'] = Mtype doubanItems['star'] = star doubanItems['score'] = score doubanItems['comments'] = comments doubanItems['desc'] = desc yield doubanItems ##返回 doubanItems

写入数据

执行爬虫,数据导出到 json文件

方式一

命令:

scrapy crawl douban -O doubantop250.json

方式二

更新脚本文件 main.py,可以采用json 文件 或 json lines 文件,具体如下

 

#!/usr/bin/python # -*- coding: utf-8 -*- ''' 需求说明: Scrapy 爬虫入口。 ''' from scrapy import cmdline if __name__ == '__main__': # ## 提交启动爬虫命令行 # cmdline.execute('scrapy crawl douban'.split()) # ## 提交启动爬虫命令行:爬取信息导出到json 文件,参数:-O 覆盖任何现有文件,-o 将新内容附加到任何现有文件 # cmdline.execute('scrapy crawl douban -O doubantop250.json'.split()) ## 提交启动爬虫命令行:新内容附加到json 文件会是文件内容变为无效json。附加到文件时可以考虑json lines cmdline.execute('scrapy crawl douban -O doubantop250.jl'.split())

3. 爬虫结果写入数据库

安装第三方库

安装第三方库 pymysql

pip install pymysql -i https://pypi.tuna.tsinghua.edu.cn/simple/

创建数据库

  • 创建数据库 scrapytest,注意编码,因为爬取数据以utf-8保存

faadaf55bf2c6bb58e3c16ff47d57010.png

创建数据表

  • 创建数据表 quotes

52a863f02372f8d19bc60c922cd8093d.png

更新管道文件

  • 更新pipelines.py,增加插入类
 

import pymysql class ScrapytestPipeline: def process_item(self, item, spider): return item class DoubanPipeline(ScrapytestPipeline): def __init__(self): ## 创建数据库链接 hostid = "192.168.245.101" # 数据库主机地,本机MySQL的为localhost username = "root" # 数据库用户名 password = "123456" # 数据库密码 self.mydb = pymysql.connect(host=str(hostid), user=str(username), passwd=str(password)) ## 获得操作游标 self.mycursor = self.mydb.cursor() print("连接数据库成功") def process_item(self, item, spider): # sql语句 insert_sql = """ insert into scrapytest.doubantop250 (Mid, `name`, movieMakers, Myear, Mcountry, Mtype, star, score, comments, `desc`) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) """ # 执行插入数据到数据库操作 self.mycursor.execute(insert_sql, (item['Mid'], item['name'], item['movieMakers'], item['Myear'], item['Mcountry'], item['Mtype'], item['star'], item['score'], item['comments'], item['desc'])) # 提交,不进行提交无法保存到数据库 self.mydb.commit() def close_spider(self, spider): # 关闭游标和连接 self.cursor.close() self.connect.close()

更新配置文件

  • 更新配置文件 setting.py,ITEM_PIPELINES(项目管道),300为优先级,越低越爬取的优先度越高
 

ITEM_PIPELINES = { 'scrapytest.pipelines.DoubanPipeline': 300, }


开始实验

第4节 附录

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

撸码的xiao摩羯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值