Scrapy
1、Scrapy介绍
安装依赖:pip install scrapy
1.1、Scrapy概述
Scrapy 是一个用Python编写的开源网络爬虫框架,它提供了一套简单而强大的工具,用于从网页中提取所需的信息。Scrapy不仅仅是一个简单的HTML解析器,它是一个完整的爬虫框架,具有高度的可扩展性和灵活性
1.2、Scrapy的主要特点
① 快速高效: Scrapy基于Twisted异步网络框架,具有高效的异步处理能力,可以并发地下载和处理多个页面
② 模块化设计: Scrapy的组件(如引擎、调度器、下载器、爬虫中间件、管道等)是松耦合的,允许用户根据需求灵活组合和定制
③ 内置支持: 提供了丰富的内置功能,如自动限速、HTTP缓存、User-Agent中间件、cookie管理等,减少了用户的重复工作
④ 扩展性强: 可以通过编写自定义扩展、中间件、管道等来扩展Scrapy的功能,满足各种爬取需求
⑤ 广泛应用: Scrapy广泛应用于各种场景,包括数据挖掘、搜索引擎、数据抓取、监控等
松耦合:是指软件系统中的组件之间关联程度较低,彼此之间的依赖性较小,修改一个组件不会对其他组件造成太大影响
1.3、Scrapy架构
① Spiders(爬虫):Spider是Scrapy中用于定义如何抓取网站信息的组件。即负责从调度器传来的Responses中分析提取数据,组装成Item对象。用户需要编写Spider类,并定义起始URL、如何跟踪链接、如何提取数据等规则
② Engine(引擎):引擎是Scrapy的核心组件,负责 协调 各个组件之间的数据流和控制流。它接收Spider产生的请求,并将它们分发给Scheduler、Downloader和Spider中间件。然后,它将下载器返回的响应发送回Spider进行处理,或将新的请求发送到调度器
③ Scheduler(调度器):调度器负责接收引擎发送的请求,并根据一定的调度策略(如FIFO队列、优先级队列等)将它们 排队 等待下载。调度器还负责去重(确保不会重复下载相同的URL)和调度请求的优先级
④ Downloader(下载器):下载器负责 下载 Spider发出的请求对应的网页内容,并将下载的响应返回给引擎。它处理HTTP请求、管理下载超时和重试、处理代理等网络操作
⑤ Item Pipeline(数据管道):数据管道负责 处理 Spider提取的数据,并执行各种处理操作,如数据清洗、验证、存储到数据库等。数据管道是一系列按顺序执行的组件,每个组件都可以对Item进行处理或丢弃
⑥ Downloader Middleware(下载中间件):下载中间件是Scrapy的一个可选组件,允许用户 修改 发出请求或接收到的响应。用户可以编写自定义的下载中间件来实现各种功能,如设置User-Agent、代理、HTTP缓存等
⑦ Spiders Middleware(爬虫中间件):爬虫中间件是另一个可选组件,用于处理Spider发出的请求和从Downloader返回的响应。它可以拦截请求或响应,修改它们,甚至完全阻止它们的传递
1.4、Scrapy架构运转流程
① Engine询问Spiders需要处理哪个网站,并让Spiders将第一个需要处理的URL交给它
② Engine将URL交给Scheduler,Scheduler将需要处理的URL生成请求对象放入到指定的队列当中
③ Engine从Schedulerf那里获取一个请求,并交给DownLoader进行处理
④ DownLoader抓取到页面之后,会将响应内容交给Engine;如果没有抓取到页面,则Engine会通知Scheduler记录这个URL,等待重新下载
⑤ Engine将响应内容交给Spiders,Spiders处理响应——通过xpath解析得到数据或者URL,然后返回给Engine
⑥ Engine判断解析结果是数据还是URL,如果是URL,则继续交给Scheduler,重复上面的步骤;如果是数据则交给PipeLines对数据进行加工处理、持久化处理
1.5、创建项目
命令:
scrapy startproject 项目名
项目名称/ # 根目录
├── 项目名称/ # 与根目录同名的Python包
│ ├── spiders/ # 存放Spider类的目录,用于存放爬虫文件
│ │ └── __init__.py # spiders目录的初始化文件
│ ├── __init__.py # 项目包的初始化文件
│ ├── items.py # 定义用于存储爬取数据的Item类
│ ├── middlewares.py # 定义下载中间件和爬虫中间件
│ ├── pipelines.py # 定义处理爬取数据的管道
│ └── settings.py # 项目的配置文件
├── scrapy.cfg # Scrapy项目的配置文件
└── venv/ # 虚拟环境(是自己配置的,不是命令生成的)
2、搭建项目
2.1、编写Item类 —— 定义要爬取的数据
现在要抓取豆瓣电影页面中每个电影的电影名、评分、短语信息,因此我们可以在items.py文件中构建一个MovieItem类,一个类实例对应每个爬取到的电影条目,类中有三个属性值分别用于存储每个电影条目的电影名、评分、短语,以将爬取到的数据结构化为一个统一的格式,方便在后续的数据处理和存储过程中使用
# items.py
import scrapy
# 定义了一个名为 MovieItem 的 Item 类,它继承自 Scrapy 的 Item 类
class MovieItem(scrapy.Item):
# 定义三个字段
title = scrapy.Field() # 电影名
score = scrapy.Field() # 评分
subject = scrapy.Field() # 短语
scrapy.Field():是Scrapy提供的字段类型,用于定义字段的类型,这些字段类型的属性值将被用于存储爬取到的信息
2.2、编写爬虫文件 —— 爬取豆瓣电影第一页
在spiders目录下创建一个爬虫文件douban.py
spiders/ # 存放Spider类的目录,用于存放爬虫文件
├── __init__.py # spiders目录的初始化文件
└── douban.py # 新建的爬虫文件
爬虫文件是用于① 定义爬取目标网站的规则:要在什么网站进行爬取、要从该网站的哪一个页面开始爬取等等,这些信息将提供给下载器,让下载器知道要下载什么页面下来 ② 定义数据提取逻辑:对下载器返回的响应进行所需数据的提取 ③ 等等
# spiders/douban.py
import scrapy
from scrapy import Selector
from ..items import MovieItem
# 定义一个名为DoubanSpider的Spider类,它继承自Scrapy的Spider类
class DoubanSpider(scrapy.Spider):
# 1、定义爬取网站的规则
# 定义了Spider的名称为'douban'。这个名称是唯一的,用于在运行Scrapy命令时标识Spider
name = 'douban'
# 指定了Spider允许爬取的域名,此时Spider只会爬取movie.douban.com域名下的网页
allowed_domains = ['movie.douban.com']
# 定义了Spider开始爬取的起始URL列表
start_urls = ['https://movie.douban.com/top250']
# 2、定义数据提取逻辑
# 定义了Spider的解析函数parse,用于处理下载器返回的响应
def parse(self, response):
# 使用response中的HTML内容来构建一个Selector对象,使得下面可以使用XPath或CSS选择器来定位和提取页面中的数据
sel = Selector(response)
# 选择页面中所有的电影条目列表,并将结果存储在list_items中
list_items = sel.css('#content > div > div.article > ol > li')
# 遍历每个电影条目
for item in list_items:
# 用于将电影条目中的信息封装成一个类
movie_item = MovieItem()
movie_item['title'] = item.css('span.title::text').extract_first() # 电影名
movie_item['score'] = item.css('span.rating_num::text').extract_first() # 评分
movie_item['subject'] = item.css('span.inq::text').extract_first() # 短语
# 将一个爬取到的电影条目(movie_item 对象)提交给 Scrapy 引擎
yield movie_item
Tip:
1、response 参数是 Scrapy 的响应对象,它包含了从网页服务器返回的所有信息,包括网页的 HTML 内容、HTTP 状态码、响应头等,而不仅仅是页面的 HTML 内容
2、::text 是一个XPath表达式,用于在HTML或XML文档中选择文本节点,即标签的文本内容
3、extract_first() 是 Scrapy Selector 对象的一个方法,用于从选择器匹配的结果中提取第一个匹配项的内容,并返回该内容
4、yield关键字:在 Scrapy 中,当 Spider 解析完一个页面并提取出需要的数据后,可以通过 yield 关键字将数据传递给 Scrapy 引擎,然后引擎会将这些数据发送到 Item Pipeline 进行处理。具体来说,yield movie_item 表示将一个 MovieItem 对象提交给 Scrapy 引擎,这个对象包含了爬取到的电影的电影名、评分和短语。引擎会将这个对象发送到 Spider 的下一个处理阶段,即 Item Pipeline,在 Item Pipeline 中便可以对这个对象进行各种处理,比如数据清洗、验证、存储等操作
2.3、修改settings.py文件 —— 浏览器伪装策略
首先,爬虫在爬取网页的时候需要将自己伪装成浏览器,以让爬虫的请求看起来更像普通用户的请求,从而降低被网站识别为爬虫并对其进行限制或封禁的风险。具体来讲,伪装成浏览器的原因有:
① 规避反爬虫策略: 很多网站会采取反爬虫策略,识别并限制或封禁爬虫的访问。通过伪装成浏览器,可以降低被网站识别为爬虫的概率,从而规避这些反爬虫策略
② 获取更多数据: 有些网站对普通用户开放了数据,但对爬虫进行了限制。通过伪装成浏览器,可以获取到更多的数据,包括一些只有通过浏览器才能访问到的内容
③ 避免被封禁: 如果爬虫被频繁封禁,将会导致爬取任务中断,并且需要花费额外的时间和精力来解决被封禁的问题。通过伪装成浏览器,可以减少被封禁的风险,从而提高爬取的稳定性和效率
总的来说,伪装成浏览器是一种常见的爬虫策略,可以帮助爬虫更好地与网站进行交互,降低被识别为爬虫并限制访问的风险,从而更有效地获取所需的数据。在scrapy框架中,只需要在settings 文件中设置 USER_AGENT 字段即可,作用是为爬虫设置一个默认的 User-Agent 头,这个设置将会在所有的请求中都包含指定的 User-Agent 头,从而让爬虫的请求看起来更像是来自浏览器的请求
# settings.py
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36"
2.4、运行Spider
命令
简单运行:scrapy crawl douban
( 可以用于调试 )
保存到文件+有输出日志:scrapy crawl douban -o douban.csv
保存到文件+不输出日志:scrapy crawl douban -o douban.csv -s LOG_ENABLED=False
在根目录下运行scrapy crawl douban -o douban.csv
之后,将在根目录中看到一个douban.csv文件,如下
score,subject,title
9.7,希望让人自由。,肖申克的救赎
9.6,风华绝代。,霸王别姬
9.5,一部美国近现代史。,阿甘正传
9.5,失去的才是永恒的。 ,泰坦尼克号
9.4,最好的宫崎骏,最好的久石让。 ,千与千寻
9.4,怪蜀黍和小萝莉不得不说的故事。,这个杀手不太冷
9.5,最美的谎言。,美丽人生
9.4,爱是一种力量,让我们超越时空感知它的存在。,星际穿越
9.4,诺兰给了我们一场无法盗取的梦。,盗梦空间
9.4,如果再也不能见到你,祝你早安,午安,晚安。,楚门的世界
9.5,拯救一个人,就是拯救整个世界。,辛德勒的名单
9.4,永远都不能忘记你所爱的人。,忠犬八公的故事
9.3,每个人都要走一条自己坚定了的路,就算是粉身碎骨。 ,海上钢琴师
9.2,英俊版憨豆,高情商版谢耳朵。,三傻大闹宝莱坞
9.3,天籁一般的童声,是最接近上帝的存在。 ,放牛班的春天
9.3,小瓦力,大人生。,机器人总动员
9.2,迪士尼给我们营造的乌托邦就是这样,永远善良勇敢,永远出乎意料。,疯狂动物城
9.3,香港电影史上永不过时的杰作。,无间道
9.6,比利·怀德满分作品。,控方证人
9.2,一生所爱。,大话西游之大圣娶亲
9.4,我们一路奋战不是为了改变世界,而是为了不让世界改变我们。,熔炉
9.3,千万不要记恨你的对手,这样会让你失去理智。,教父
9.3,满满温情的高雅喜剧。,触不可及
9.2,平民励志片。 ,当幸福来敲门
9.1,死亡不是真的逝去,遗忘才是永恒的消亡。,寻梦环游记
至此,便完成了对豆瓣电影第一个页面数据的爬取和数据的提取、保存操作
2.5、修改爬虫文件 —— 爬取豆瓣电影剩下的页面
修改douban.py代码如下
import scrapy
from scrapy import Selector
from scrapy.http import HtmlResponse,Request
from ..items import MovieItem
class DoubanSpider(scrapy.Spider):
name = 'douban'
allowed_domains = ['movie.douban.com']
start_urls = ['https://movie.douban.com/top250']
def parse(self, response: HtmlResponse): # 声明response是HtmlResponse类型,这样在使用urljoin方法的时候编译器就可以给出提示了
sel = Selector(response)
lis