本章详细介绍了Scrapy框架的安装,通过Scrapy框架架构,讲解了Scrapy各个组件的作用。
无
1.Scrapy 简介与安装
2.Scrapy 项目创建
3.Scrapy 进阶
Scrapy是用python实现的一个为了爬取网站数据,提取结构性数据而编写的应用框架;用于抓取web站点并从页面中提取结构化的数据,用于数据挖掘、监测和自动化测试。
架构组件构成
Scrapy框架主要由五大组件组成,它们分别是:
调度器(Scheduler)、下载器(Downloader)、爬虫(Spider)和实体管道(Item Pipeline)、Scrapy引擎(Scrapy Engine)。
下面我们分别介绍各个组件的作用。
组件 | 作用说明 | |
---|---|---|
Spiders | 爬虫脚本 | 定义了爬取的逻辑和网页内容的解析规则,主要负责解析响应并生成结果和新的请求 |
Engine | 引擎 | 处理整个系统的数据流处理,出发事物,框架的核心。 |
Scheduler | 调度器 | 接受引擎发过来的请求,并将其加入队列中,在引擎再次请求时将请求提供给引擎 |
Downloader | 下载器 | 下载网页内容,并将下载内容返回给spider |
ItemPipeline | 项目管道 | 负责处理spider从网页中抽取的数据,主要是负责清洗,验证和向数据库中存储数据 |
Downloader Middlewares | 下载中间件 | 是处于Scrapy的Request和Requesponse之间的处理模块 |
Spider Middlewares | spider中间件 | 位于引擎和spider之间的框架,主要处理spider输入的响应和输出的结果及新的请求middlewares.py里实现 |
架构组件关系
流程解析
安装语法:pip install scrapy -i https://pypi.tuna.tsinghua.edu.cn/simple/
查看版本语法:scrapy version
命令:
scrapy startproject scrapytest
文件作用说明
进入项目目录 cd scrapytest
创建爬虫 scrapy genspider douban “https://movie.douban.com/top250”
获取头部信息
配置头部信息
文件:settings.py
配置爬虫脚本
文件:douban.py
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
页面分析
网页模型按盒子模型组成
正在上传…重新上传取消
分析页面模块层级、元素构成
提取路径测试
启动shell测试:scrapy shell https://movie.douban.com/top250 下载目标网站页面
查看下载到的页面内容
获取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()
依次类推,获取所有信息路径:
- 电影名称:/html/body/div[3]/div[1]/div/div[1]/ol/li[1]/div/div[2]/div[1]/a
- 电影创作人员:/html/body/div[3]/div[1]/div/div[1]/ol/li[1]/div/div[2]/div[2]/p[1]
- 电影年份、国家、类型:/html/body/div[3]/div[1]/div/div[1]/ol/li[1]/div/div[2]/div[2]/p[1]
- 电影评价-星数:/html/body/div[3]/div[1]/div/div[1]/ol/li[4]/div/div[2]/div[2]/div/span[1]
- 电影评价-评分:/html/body/div[3]/div[1]/div/div[1]/ol/li[4]/div/div[2]/div[2]/div/span[2]
- 电影评价-评价数:/html/body/div[3]/div[1]/div/div[1]/ol/li[4]/div/div[2]/div[2]/div/span[4]
- 电影介绍:/html/body/div[3]/div[1]/div/div[1]/ol/li[3]/div/div[2]/div[2]/p[2]/span
完成测试,根据xpath 语法更新如下:
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()
更新爬虫文件
将测试通过的信息提取代码更新到 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
终端尝试启动爬虫,观察输出结果
爬虫脚本优化
优化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,输出结果如下:
优化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
方式一、终端执行
命令:
scrapy crawl duoban
方式二、脚本执行
新建脚本文件 main.py,通过脚本提交 cmd 命令启动爬虫
#!/usr/bin/python # -*- coding: utf-8 -*- ''' 需求说明: Scrapy 爬虫入口。 ''' from scrapy import cmdline if __name__ == '__main__': cmdline.execute('scrapy crawl douban'.split())
开始实验
基本思路:
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)
定义容器字段
文件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()
更新容器数据
文件 douban.py,引入新建类 DoubanItem,新建类 DoubanItem 的实例对象 doubanItem,并更新对象的值
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())
安装第三方库
安装第三方库 pymysql
pip install pymysql -i https://pypi.tuna.tsinghua.edu.cn/simple/
创建数据库
创建数据表
更新管道文件
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()
更新配置文件
ITEM_PIPELINES = { 'scrapytest.pipelines.DoubanPipeline': 300, }
开始实验