目录
介绍
Scrapy是Python中最强大、最流行的网络爬虫框架之一,专为网页抓取、数据挖掘和处理大规模网站数据而设计。它不仅功能强大,而且高度灵活,允许开发者高效地构建复杂的爬虫程序。下面是对Scrapy框架的一些核心特点和组件的简单介绍:
核心特性
-
基于Twisted异步处理模型:Scrapy利用Twisted这一高性能的事件驱动网络编程库,实现了非阻塞的异步IO操作,大大提高了爬虫的效率和速度。
-
多层次的架构:Scrapy采用模块化设计,其核心组件包括引擎、调度器、下载器、爬虫和项目管道,每个部分各司其职,易于扩展和定制。
-
强大的选择器:内置了基于XPath和CSS选择器的选择器库(Selectors),使得从HTML或XML源码中提取数据变得非常直观和简单。
-
请求与响应的中间件:提供了请求和响应的中间件机制,允许开发者在发送请求前后或接收响应前后执行自定义代码,实现如请求重定向、修改响应内容等功能。
-
项目管道:用于处理爬取到的数据,清洗、验证并存储到文件、数据库或其他系统中。支持多个管道串联,实现复杂的数据处理流程。
-
日志和错误处理:内置了详尽的日志记录和错误处理机制,便于调试和监控爬虫运行状态。
-
扩展性:Scrapy具有良好的插件生态系统,支持通过安装第三方扩展来增强功能,如分布式爬虫、自动限速、用户代理池等。
安装
设置镜像地址
在安装scrapy之前先将python的安装镜像源调整为国内镜像地址,这里设置为清华源为例:
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
查看是否设置成功
安装scrapy
sudo apt-get install scrapy
或者
pip/pip3 install scrapy
查看scrapy包是否安装成功
初步使用scrapy
爬取页面html
scrapy内置了一系列命令进行项目的初始化搭建
创建项目:
scrapy startproject 项目名
来到要创建项目的目录文件夹下,打开终端行
目录结构如下
进入到刚才创建的项目目录下
生成一个爬虫入口,这里以传智教育页面为例:https://www.itcast.cn/
scrapy genspider 爬虫名 爬取的网页主地址
scrapy genspider itcast itcast.cn
尝试简单爬取传智教育页面 的html文件体
运行scrapy
在项目目录下终端中执行启动命令
scrapy crawl 爬虫名
爬取教研团队信息
爬取教研团队页面
itcast.cn页面有所变化,点击后页面进行跳转
注意调整前面的域名调整
启动测试
然后遍历节点数据取名字,职称,介绍等信息
注意观察页面html结构进行书写css选择器.当然也可以使用xpath,这里主要以css选择器为例
拿取各节点属性
可以看到各个老师节点的属性值也可以根据css节点拿取
注意点
可以看到数据产出,但是代码中没有进行print打印,但是爬取的数据还是有打印日志,说明yield起的产出效果
启用管道
scrapy爬取中使用yield进行数据产出后需要进行管道的配置,可以配置多个管道从而进行数据不同的产出逻辑,比如redis管道,数据保存在redis中,mysql管道,数据保存在mysql中,文件管道,数据直接导出到文件中等
配置自定义管道 ,目录中pipelines就是管道文件
书写模拟管道逻辑
激活管道
来到settings.py文件中进行管道的激活
启动测试
配置输出文件管道
新建filepipelines文件
设置文件中激活文件管道
启动测试:
可以看到通过文件管道输出数据到json文件中了,两个管道分别执行了不同的数据处理逻辑
数据建模
之前爬取数据时采用字典方式进行数据爬取,可以使用数据建模方式规定好要爬取的字段属性来进行爬取
scarpy也给我们指定好了items文件中进行建模的使用
重启测试:
递归爬取网易招聘数据
Request
Scrapy的Request模块用于表示HTTP请求,通常在Spider中创建并在Downloader中执行。以下是一些关键参数及其作用:
- url:这是请求的目标URL,是发起请求的基本属性。
- method:请求的方法,默认为'GET',但也可以是'POST'、'PUT'等其他HTTP方法。
- headers:一个字典,包含请求的HTTP头部信息,如User-Agent、Cookies等。
- body:请求的主体内容,通常用于POST或PUT请求。
- meta:一个字典,用于传递额外的数据。这些数据会被传递给后续的请求和响应处理过程,常用于多层爬虫之间的数据传递。
- callback:一个可调用对象,当请求完成并返回响应时,将调用此函数。它接收一个Response对象作为参数。
- errback:当请求失败时调用的函数,用于处理错误情况。
- cookies:一个字典,包含要发送的cookies。
- encoding:请求的编码方式。
- dont_filter:布尔值,指示是否对重复url路径进行去重过滤,默认False去重,如果是post请求,且是body体里进行传参,这里必须设置为True,否则会被过滤,从而只请求一次
- replace:布尔值,指示是否应替换具有相同URL的现有请求。
分析数据
scrapy内置Request模块可以进行接口发送数据进行页面爬取
有些网页的数据是由api接口进行获取数据动态渲染的,此时拿取html数据可能会失败,此时就需要使用Requests请求方式进行数据的爬取
f12进行页面抓包后找到接口地址
查看载荷查看接口所需参数
书写爬取
新建爬虫项目不再展示
博主新建wy爬虫进行书写
新建数据模型
查看接口相应数据
分析载荷参数
所以我们在发送请求时只需要调整currentPage这个参数的值即可,使用apipost进行接口测试
观察响应体发现响应体中并没有当前页的参数显示,在结尾处有一个lastPage(最后一页):false,由此可以得知其判断是否可以翻页的参数就是这个
再测试到最后一页看下该参数是否有变化
可以看到,当currentPage参数足够大,或者真的是最后一页的页数码时lastPage会显示为True
不同网页这里逻辑可能不一样,有的地方显示的是当前页数等,所以我们这里可以进行数据翻页的判断,我们不断发起请求,每次请求都将currentPage参数进行+1,从而调整页数,然后再去判断lastPage是否不为true,不为true时可以一直发送请求,此处可以使用递归函数进行逻辑书写
逻辑比较绕,直接贴代码可以自行尝试
import scrapy
import json
from wangyi.items import WangyiItem
class WySpider(scrapy.Spider):
name = "wy"
allowed_domains = ["163.com"]
start_urls = ["https://hr.163.com/job-list.html"]
def __init__(self, *args, **kwargs):
super(WySpider, self).__init__(*args, **kwargs)
self.currentPage = 1
self.pageSize = 100
def parse(self, response):
data = {
"currentPage": self.currentPage,
"pageSize": self.pageSize
}
url = "https://hr.163.com/api/hr163/position/queryPage"
headers = {'Content-Type': 'application/json'}
print('当前爬取的是===>', self.currentPage)
yield scrapy.Request(url=url, method='POST', body=json.dumps(data), headers=headers, callback=self.parse_detail,dont_filter=True)
def parse_detail(self, response):
if response.status == 200:
body = response.json()
lastPage = body.get('data', {}).get('lastPage', False)
print('当前是否是最后一页', lastPage)
for item in body.get('data').get('list'):
zhaopin = WangyiItem()
# 职位名称
zhaopin['name'] = item.get('name')
# 工作城市
zhaopin['workPlace'] = item.get('workPlaceNameList')[0]
# 职位招收人数
zhaopin['recruitNum'] = item.get('recruitNum')
# 职位学历要求
zhaopin['reqEducationName'] = item.get('reqEducationName')
# 职位所需经验
zhaopin['reqWorkYearsName'] = item.get('reqWorkYearsName')
# 职位所属项目
zhaopin['productName'] = item.get('productName')
# 职位要求
zhaopin['requirement'] = item.get('requirement')
# 职位描述
zhaopin['description'] = item.get('description')
yield zhaopin
# yield body.get('data')
# print(body.get('data'))
# 检查是否还有下一页,如果有则递归调用parse以获取下一页数据
if lastPage != True:
self.currentPage += 1
print('当前参数是:', {"currentPage": self.currentPage, "pageSize": self.pageSize})
yield scrapy.Request(url="https://hr.163.com/api/hr163/position/queryPage",
method='POST',
body=json.dumps({"currentPage": self.currentPage, "pageSize": self.pageSize}),
headers={'Content-Type': 'application/json'},
dont_filter=True,
callback=self.parse)
else:
return
else:
print("请求失败,状态码:", response.status)
管道进行数据处理
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
# useful for handling different item types with a single interface
import json
class WangyiPipeline:
# 定义file对象
def __init__(self):
self.items = [] # 初始化一个列表用于存储所有item的字典形式
self.file = open('wangyi_clean.json', 'w')
def process_item(self, item, spider):
print('开始处理item')
# 直接将item的字典形式添加到items列表中,无需转换为字符串
self.items.append(dict(item))
return item
def close_spider(self, spider):
print('爬虫结束,开始写入文件')
# 使用json.dump()一次性写入整个items列表,items中每个元素已经是字典形式,这样会直接构成一个标准的JSON数组
json.dump(self.items, self.file, ensure_ascii=False, indent=4)
self.file.close() # 关闭文件操作
print('文件写入完成')
启动测试
爬取完成保存为json文件,当然这里也可以书写保存数据库的管道进行不同管道输出数据
分组管道
不区分爬虫分组
之前在使用管道进行时是进行创建了多个管道文件来进行不同管道的数据输出,其实可以只用一个管道文件从而对数据进行输出
在管道文件中再新建一个管道类
在设置文件中激活管道2
两个管道进行不同文件名的输出数据,启动测试下
可以看到通过一个管道文件两个管道也生成了两个文件
区分爬虫分组
前面测试使用的是不区分爬虫进行管道的使用,如果是多个爬虫文件时,此时就需要在管道中进行爬虫的筛选进行管道的输出
将wy爬虫中的逻辑代码粘贴过来,调整下爬取字段
调整爬虫wy2只爬取两个简单属性,wy爬虫不动,此时项目中就有两个爬虫入口了
管道使用注意点
进行管道调整
启动不同爬虫进行管道数据输出
wy爬虫
wy2爬虫
可以看到最后的效果:通过不同管道输出不同爬虫的数据
FormRequest
概念
FormRequest是Request的扩展类,具体常用的功能如下:
-
请求时,携带参数,如表单数据
-
从Response中获取表单的数据
FormRequest类可以携带参数主要原因是:增加了新的构造函数的参数formdata
。其余的参数与Request类相同.
- formdata参数类型为:dict
以360趋势图数据为例360趋势-大数据分享平台
查看接口
分析测试
crawlSpider爬取小说内容
以笔趣阁小说网为例
创建脚本
scrapy genspider -t crawl 爬虫名 网站域名
allow进行书写页面正则来进行抽取页面的链接
分析要爬取的页面坏蛋是怎样炼成的3六道无弹窗_坏蛋是怎样炼成的3六道最新章节列表_笔趣阁
观察html元素,发现每个章节的链接地址规律,然后进行正则书写进行抽取
启动测试:
可以看到每个章节的地址自动被正则匹配抽取出来了
打印响应体内容
可以看到小说每个章节的内容体也打印出来了,下面需要书写每个章节的正则匹配内容
章节标题内容提取
测试打印
可以看到章节标题和内容都拿取到了,但是内容是以dom元素进行拿取的,需要书写正则进行数据匹配
进行通道书写处理保存下载文本
设置文件中记得开启通道
启动测试
小说,各标题和章节都爬取到了
中间件
随机user-agent中间件
以爬取豆瓣网为例
创建豆瓣爬虫项目
分析页面元素
书写css选择器进行拿取元素
启动测试
可以看到拿取时失败.这并不是由于代码书写有问题,而是因为对方服务器检测出我们并不是合法浏览器请求从而拒绝了请求 而此时我们需要设置我们的请求ua来进行绕过对方的检测
具体ua如何拿取可以在浏览器任意请求中进行ua拿取
复制ua后在设置文件中进行ua设置
设置ua后再次重启爬虫
拿取成功
随机ua生成
但是在实际使用中,对方也有可能会对单个ua的访问次数进行限制,所以我们还需要进行随机ua生成,来进行每次请求使用不同的ua来规避这种情况
准备一个ua集合
直接使用gpt生成10个不同ua即可
书写中间件
设置文件中激活中间件
在爬虫入口打印请求头ua查看ua是否是随机的
启动测试.这里只打印ua,查看ua情况
ua随机打印
数据也是成功产出
中间件还有随机ip和seleunm动态渲染等,本次不再演示,后续再进行说明