Python从基础到Scrapy框架(纯概念总结)
爬虫的分类:
- 通用爬虫:
抓取系统的重要组成部分。抓取的是一整张页面数据。 - 聚焦爬虫:
是建立在通用爬虫的基础之上。爬虫特定的局部信息。 - 增量式爬虫:
检测网站中的数据更新情况。只会抓取网站中最新更新出来的数据
http协议
概念:就是服务器和客户机端进行数据交换的一种形式。(http/https协议的特性:无状态)
常用的请求头信息
- User-Agent:请求载体的身份标识
- Connection:请求完毕,是断开还是保持连接
常用的响应头信息
- Content-Type:服务器响应回客户端的数据类型
https协议:
- 安全的超文本传输协议
加密方式
- 对称密钥加密
- 非对称密钥加密
- 证书密钥加密
requests模块 (需要下载:pip install requests)
urllib模块
requests模块:python中原生的一款基于网络请求的模块,功能非常强大,简单便捷,效率极高
作用:模拟浏览器发起请求
timeout超时
如何使用:(requests的编码流程)- 制定url
发起请求(requests.get/requests.post)
- code = response.status_code(获取状态码)
- 获取响应数据(requests.get(url).text)
- 持久化存储(with open() as fp)
聚焦爬虫:爬取页面中指定的页面内容
编码流程:
- 指定url
- 发起请求
- 获取响应数据
- 数据解析
- 持久化存储
数据解析的分类:
正则
bs4(环境安装:pip install bs4 + pip install lxml)
bs4数据解析的原理:
- 实例化一个BeautifulSoup对象,并将数据源码数据加载到该对象中
- 通过调用BeautifulSoup对象中相关的属性或方法进行标签定位和数据提取
如何实例化BeautifulSoup对象:
- from bs4 import BeautifulSoup
对象的实例化
将本地的html文件加载到该对象中
- fp = open('xxx','r',encoding='utf-8')
- soup = BeautifulSoup(fp,'lxml')
将联网上获取的页面数据加载到该对象中
- page_text = response.text
- soup = BeautifulSoup(page_text,'lxml')
提供的用于数据解析的方法和属性:
- soup.tagName:返回的是文档中第一次出现的tagName对应的标签(tagName的标签名)
soup.find():
- find('tagName'):等同于soup.div
- 属性的定位:soup.find('div',class_/id/attr='song')
- soup.find_all('tagName'):返回符合要求的所有标签(列表)
select:
- select('某种选择器(id,class,标签...选择器)'),返回的是一个列表
层级选择器:
- soup.select('.tang > ul > li > a'): >表示一个层级
- soup.select('.tang > ul a'): 空格表示多个层级
获取标签间的文本数据:
- soup.a.text/string/get_text()
- text/get_text():可以获取某一个标签中所有的文本内容
- string:只可以获取该标签下直系的文本内容
- 获取标签中的属性:soup.a['href']
xpath解析:最常用且最便捷高效的一直解析方式。通用性
xpath解析原理:
- 实例化一个etree对象,且将需要解析的页面源码数据加载到该对象中。
- 环境的安装:pip install lxml
如何实例化一个etree对象:from lxml import etree
- 将本地的HTML文档中的源码加载到etree对象中:etree.parse(filPath)
- 可以从互联网中获取的源码数据加载到该对象中:etree.HTML(page_text)
xpath表达式:
- /:表示的是从跟节点开始定位。表示的是一个层级。
- //:表示的是多个层级。可以表示从任意位置开始定位。
- 属性定位://div[@class="song"] tag[@attrName="attrValue"]
- 索引定位://div[@class="song"]/p[3] 索引是从1开始的。
取文本:
- /text() 获取的是标签中直系的文本内容
- //text() 标签中非直系的文本内容(所有的文本内容)
取属性:
- /@attrName ==>img/@src
数据解析的原理概述:
- 解析局部的文本内容都会在标签之间或者标签对应的属性中进行存储
- 1.进行指定标签的定位
- 2.标签或者标签对应的属性中存储的数据进行提取(解析)
验证码识别方式:
Tesseract:
- 下载Tesseract
- 配置环境变量
- 验证安装(tesseract img.png result)Python环境中也要下载(pip install pytesseract)
使用:
- import pytesseract
- from PIL import Image
- image = Image.open('img.png')
- text = pytesseract.image_to_string(image,lang='eng')
- 云打码平台
- 人工肉眼识别
模拟登陆:爬取基于某些用户的用户信息
没有请求到对应页面数据的原因:
发起第二次基于个人主页页面请求的时候,服务器端并不知道该此请求是基于登录状态下的请求。
cookie:用来让服务器端记录客户端的相关状态
- 手动处理:通过抓包工具获取cookie值,将该值封装到headers中。(不建议)
自动处理:
- cookie值的来源是哪里?——模拟登陆post请求后,有服务器端创建。
session会话对象作用:
- 可以进行请求发送
- 如果请求过程中产生了cookie,则该cookie会被自动存储/携带在session对象中。
- 创建一个session对象:session = requests.Session()
- 使用session对象进行模拟登录post请求发送(cookie就会被存储在session对象中)
- session对象对个人主页对应的get请求进行发送(携带了cookie)
代理:破解封IP的这种反爬机制。
什么是代理:
- 代理服务器
代理的作用:
- 突破自身IP访问限制
- 隐藏自身真实IP
代理IP的类型:
- http:应用到http协议对应的url中
- https:应用到https协议对应的url中
代理IP的透明度:
- 透明:服务器知道该此请求使用了代理,也知道请求对应的真实IP
- 匿名:服务器知道该此请求使用了代理,但不知道请求对应的真实IP
- 高匿:不知道使用了代理,更不知道真实IP
高性能异步爬虫
目的:在爬虫中使用异步实现高性能的数据爬取操作。
异步爬虫的方法:
多线程、多进程(不建议):
- 好处:可以为相关阻塞的操作单独开启线程或进程,阻塞操作就可以异步执行。
- 弊端:无法限制的开启多线程或者多进程。
线程池、进程池(适当的使用):
- 好处:我们可以降低系统对线程或者进程创建或者销毁的一个频率,从而很好的降低系统的开销。
- 弊端:池中线程或者进程的数量是有上限。
单线程+异步协程(推荐):
- event_loop:事件循环,相当于一个无限循环,我们可以把一些函数注册到这个事件循环上,当满足某些条件的时候,函数就会被循环执行。
- coroutine:协程对象,我们可以将协程对象注册到事件循环中,他会被事件循环调用。
- 我们可以用 async 关键字来定义一个方法,这个方法在被调用时不会立即被执行,而是返回一个协程对象、
- task:任务,它是对协程对象的进一步封装,包涵了任务的各个状态。
- future:代表将来执行或还没执行的任务,实际上和 task 没有本质区别。
- async 定义一个协程。
- await 用来挂起阻塞方法的执行。(asyncio中遇到阻塞必须进行手动挂起)
- 任务列表:存放多个任务对象(运行任务列表需要使用asyncio.await())
aiohttp:
- text()返回字符串类型的响应数据
- read()返回二进制形式的响应数据(content.read())
- json()返回的就是json对象
- 代理IP:proxy:'http://ip:port'
selenium模块的基本使用
问题:selenium模块和爬虫之间具体怎样的联系?
- 便捷的获取网站中动态加载的数据
- 便捷实现模拟登陆
什么是selenium模块?
- 基于浏览器自动化的一个模块。
selenium的使用流程:(window.navigator.webdriver查看是否被浏览器检测出是自动化脚本)
- 环境安装:pip install selenium
下载一个浏览器驱动程序(谷歌浏览器)
- 下载的浏览器驱动程序得和浏览器对应
- 实例化一个浏览器对象
编写基于浏览器自动化的操作代码
- 发起请求:get(url)
- 标签定位:find系列的方法
- 标签交互:send_keys("xxx")
- 执行js程序:excute_script('jsCode')
- 前进,后退:back(),forward()
- 关闭浏览器:quit()
selenium处理iframe
- 如果定位标签存在于iframe标签之中,则必须要使用switch_to.frame(id)
动作链(拖动):from selenium.webdriver import ActionChains
- 实例化一个动作链对象:action = ActionChains(bro)
- click_and_hold(div):长按且点击操作
- move_by_offset(x,y):移动
- perform()让动作链立即执行
- action.release()释放动作链对象
无头浏览器与规避检测:
- from selenium.webdriver import ChromeOptions引入
- 实例化对象:chrome_options = ChromeOptions()
- chrome_options.add_experimental_option('excludeSwitches',['enable-automation'])
无头浏览器:
- 为Chrome配置无头模式:chrome_options.add_argument('--headless')
规避检测:
- 实现风险规避:chrome_options.add_argument('--disable-blink-features=AutomationControlled')
Scrapy:
什么是框架?
就是集成了很多功能并且具有很强通用性的一个项目模板
什么是scrapy?
爬虫封装好的一个明星框架。功能:高性能的持久化存储,异步的数据下载,高性能的数 据解析,分布式
scrapy框架的基本使用
- 创建工程:scrapy startproject XXX
- 创建爬虫:scrapy genspider xx www.xxx.com
- 运行工程:scrapy crawl xx
scrapy持久化存储
基于终端指令:
- 要求:只可以将parse方法的返回值存储到本地文本文件中
- 注意:这种持久化存储对应的文本文件类型只可以是:'json','jsolines','jl','csv','xml',marshal','pickle'
- 指令:scrapy crawl xxx -o filePath
- 好处:简单高效便捷
- 缺点:局限性比较强(数据只可以存储到指定后缀的文本文件中)
基于管道:
编码流程:
- 数据解析
- 在item类中定义相关的属性
- 将解析的数据封装存储到item类型的对象中
- 将item类型的对象提交给管道进行持久化存储的操作
- 将管道类的process_item中要将其接受到的item对象中存储的数据进行持久化存储操作
- 在配置文件中开启管道
- 好处:通用性强。
五大核心组件:
- 引擎(Scrapy):用来处理整个系统的数据处理,触发事务(框架核心)
- 调度器(Scheduler):用来接收引擎发过来的请求,压入对列中,并在引擎再次请求的时候返回。可以想象成一个URL(抓取网页的网址或者说是链接)的优先对列,由它来决定下一个要抓取的网址是什么,同时去除重复的网址
- 下载器(Downloader):用于下载网页内容,并将网页内容返回给蜘蛛(Scrapy下载器是建立在twister这个高效的异步模块上的)
- 爬虫(Spiders):爬虫是主要干活的,用于从特定网页中提取自己想要的信息,即所谓的实体(Item)。用户也可以从中提取链接,让scrapy继续抓取下一个页面。
- 项目管道(Pipeline):负责爬虫从网页中抽取的实体,主要功能是持久化实体、验证实体的有效性、清除不需要的信息。当前页面被爬虫解析后,将被发送到项目管道,并经历几个特定的次序处理数据。
图片数据爬取之ImagesPipeline
基于scrapy爬取字符串类型的数据和爬虫图片类型数据的区别?
- 字符串:只需要基于xpath进行解析且提交管道进行持久化存储
- 图片:xpath解析出图片的src属性值。单独对图片地址发起请求获取图片二进制类型的数据
ImagesPipeline:
- 只需要将img的src属性值进行解析,提交给管道,管道就会对图片的src进行请求发送获取图片的二进制类型的数据,且还会帮我们持久化存储
使用流程:
- 数据解析(图片的地址)
- 将存储图片地址的item提交到定制的管道类
在管道文件中自定制一个基于ImagesPipeline的一个管道类
- get_media_request
- file_path
- item_completed
在配置文件中:
- 指定图片存储的目录:IMAGES_STORE = './xxx'
- 指定开启管道:自定制的管道类
CrawlSpider:类,Spider的一个子类
全站数据的爬取方式
- 基于Spider:手动请求
- 基于CrawlSpider
CrawlSpider的使用:
- 创建一个工程
- cd xxx
创建一个爬虫文件(CrawlSpider):
- scrapy genspider -t crawl xxx www.xxx.com
- 链接提取器:
作用:根据指定的规则(allow)进行指定链接的提取 - 规则解析器:
作用:将链接提取器提取到的链接进行指定规则(callback)的解析
中间件:
下载中间件:
- 位置:引擎和下载器之间
- 作用:批量拦截到整个工程中所有的请求和响应
拦截请求:
UA伪装:
process_request:
- request.headers['User_Agent'] = random.choice(self.user_agents)(user_agents是自定义的UA池)
process_exception(拦截发生异常的请求):
- request.meta['proxy'] = 'http://145.40.73.102:9443'(代理)
- return request
- 代理IP
- 拦截响应:
篡改响应数据,响应对象
分布式爬虫
- 概念:我们需要搭建一个分布式的机群,让其对一组资源进行分布联合爬取
- 作用:提升爬取数据的效率
如何实现分布式?
- 安装一个scrapy-redis的组件
- 原生的scrapy是不可以实现分布式爬虫,必须让scrapy结合scrapy-redis组件一起实现分布式爬虫。
为什么原生scrapy不能实现分布式?
- 调度器不可以被分布式机群共享
- 管道不可以被分布式机群=共享
scrapy-redis组件作用:
- 可以给原生scrapy框架提供可以被共享的管道和调度器
实现流程
- 创建一个工程
- 创建一个CrawlSpider的爬虫文件
修改当前的爬虫文件
- 导包:from scrapy_redis.spiders import RedisCrawlSpider
- 将start_urls和allowed_domains进行注释
- 添加一个新的属性:redis_key = 'sun' 可以被共享调度器的队列名称
- 编写数据解析相关操作
- 将当前爬虫类的父类修改成RedisCrawlSpider
修改配置文件settings
指定可以被共享的管道:
# 将爬取到的items保存到Redis 以便进行后续处理 ITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline': 400, }
指定可以被共享的调度器:
# 确保所有的爬虫实例使用Redis进行重复过滤 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 调度器启用Redis存储Requests队列 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 将Requests队列持久化到Redis,可支持暂停或重启爬虫 SCHEDULER_PERSIST = True
- 指定redis服务器
redis相关操作配置:
配置redis的配置文件:
- Linux或者Mac:redis.conf
- Windows:redis.windows.conf
打开配置文件修改:
- 将bind 127.0.0.1进行注释
- 关闭保护模式:protected-mode yes改成no
结合配置文件开启redis服务器
- redis-server 配置文件(redis-server.exe redis.windows.conf)
启动客户端:
- redis-cli
执行工程:
- scrapy runspider xx.py
向调度器对列中放入一个起始的url:
调度器的对列在redis的客户端中
- lpush xxx www.xxx.com
- 爬取到的数据存储在了redis的proName:items这个数据结构中
redis数据库(flushall清除全部数据)
增量式爬虫:
- 概念:检测网站数据更新情况,只会爬取网站最新更新出来的数据
分析:
- 指定一个起始的url
- 基于CrawlSpider获取其他页面连接
- 基于Rule将其他页码连接进行请求
- 从每一个页码对应的源码中解析出每一个电影详情页的url
核心:检测详情页的url之前有没有请求过
将爬取过的电影详情页的url存储
- 存储到redis的set数据结构中
- 对详情页发起请求,然后解析出需要的内容
- 进行持久化存储
笔记附件:note.txt
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。