一、爬虫基础简介
什么是爬虫:
- 通过编写程序,模拟浏览器上网,然后让其去互联网上抓取数据的过程。
爬虫的价值:
- 实际应用
- 就业
爬虫究竟是合法还是违法的?
- 在法律中是不被禁止
- 具有违法风险
- 善意爬虫 恶意爬虫
爬虫带来的风险可以体现在如下2方面:
- 爬虫干扰了被访问网站的正常运营
- 爬虫抓取了收到法律保护的特定类型的数据或信息
如何在使用编写爬虫的过程中避免进入局子的厄运呢?
- 时常的优化自己的程序,避免干扰被访问网站的正常运行
- 在使用,传播爬取到的数据时,审查抓取到的内容,如果发现了涉及到用户隐私
商业机密等敏感内容需要及时停止爬取或传播
爬虫在使用场景中的分类
- 通用爬虫:
抓取系统重要组成部分。抓取的是一整张页面数据。
- 聚焦爬虫:
是建立在通用爬虫的基础之上。抓取的是页面中特定的局部内容。
- 增量式爬虫:
检测网站中数据更新的情况。只会抓取网站中最新更新出来的数据。
爬虫的矛与盾
反爬机制
门户网站,可以通过制定相应的策略或者技术手段,防止爬虫程序进行网站数据的爬取。
反反爬策略
爬虫程序可以通过制定相关的策略或者技术手段,破解门户网站中具备的反爬机制,从而可以获取门户网站中相关的数据。
robots.txt协议:
君子协议。规定了网站中哪些数据可以被爬虫爬取哪些数据不可以被爬取。
http协议
- 概念:就是服务器和客户端进行数据交互的一种形式。
常用请求头信息
- User-Agent:请求载体的身份标识
- Connection:请求完毕后,是断开连接还是保持连接
常用响应头信息
- Content-Type:服务器响应回客户端的数据类型
https协议:
- 安全的超文本传输协议
加密方式
- 对称秘钥加密
- 非对称秘钥加密
- 证书秘钥加密
二、Requests模块基础
- urllib模块
- requests模块
requests模块:python中原生的一款基于网络请求的模块,功能非常强大,简单便捷,效率极高。
作用:模拟浏览器发请求。
如何使用:(requests模块的编码流程)
- 指定url
- UA伪装
- 请求参数的处理
- 发起请求
- 获取响应数据
- 持久化存储
环境安装:
pip install requests
实战编码:
- 需求:爬取搜狗首页的页面数据
实战巩固
- 需求:爬取搜狗指定词条对应的搜索结果页面(简易网页采集器)
- UA检测
- UA伪装
- 需求:破解百度翻译
- post请求(携带了参数)
- 响应数据是一组json数据
- 需求:爬取豆瓣电影分类排行榜 https://movie.douban.com/中的电影详情数据
- 作业:爬取肯德基餐厅查询http://www.kfc.com.cn/kfccda/index.aspx中指定地点的餐厅数据
- 需求:爬取国家药品监督管理总局中基于中华人民共和国化妆品生产许可证相关数据
http://125.35.6.84:81/xk/
- 动态加载数据
- 首页中对应的企业信息数据是通过ajax动态请求到的。
http://125.35.6.84:81/xk/itownet/portal/dzpz.jsp?id=e6c1aa332b274282b04659a6ea30430a
http://125.35.6.84:81/xk/itownet/portal/dzpz.jsp?id=f63f61fe04684c46a016a45eac8754fe
- 通过对详情页url的观察发现:
- url的域名都是一样的,只有携带的参数(id)不一样
- id值可以从首页对应的ajax请求到的json串中获取
- 域名和id值拼接处一个完整的企业对应的详情页的url
- 详情页的企业详情数据也是动态加载出来的
- http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsById
- http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsById
- 观察后发现:
- 所有的post请求的url都是一样的,只有参数id值是不同。
- 如果我们可以批量获取多家企业的id后,就可以将id和url形成一个完整的详情页对应详情数据的ajax请求的url
数据解析:
聚焦爬虫
正则
bs4
xpath
三、数据解析
聚焦爬虫:爬取页面中指定的页面内容。
- 编码流程:
- 指定url
- 发起请求
- 获取响应数据
- 数据解析
- 持久化存储
数据解析分类:
- 正则
- bs4
- xpath(***)
数据解析原理概述:
- 解析的局部的文本内容都会在标签之间或者标签对应的属性中进行存储
- 1.进行指定标签的定位
- 2.标签或者标签对应的属性中存储的数据值进行提取(解析)
正则解析:
<div class="thumb">
<a href="/article/121721100" target="_blank">
<img src="//pic.qiushibaike.com/system/pictures/12172/121721100/medium/DNXDX9TZ8SDU6OK2.jpg" alt="指引我有前进的方向">
</a>
</div>
ex = '<div class="thumb">.*?<img src="(.*?)" alt.*?</div>'
bs4进行数据解析
- 数据解析的原理:
- 1.标签定位
- 2.提取标签、标签属性中存储的数据值
- bs4数据解析的原理:
- 1.实例化一个BeautifulSoup对象,并且将页面源码数据加载到该对象中
- 2.通过调用BeautifulSoup对象中相关的属性或者方法进行标签定位和数据提取
- 环境安装:
- pip install bs4
- pip install lxml
- 如何实例化BeautifulSoup对象:
- from bs4 import BeautifulSoup
- 对象的实例化:
- 1.将本地的html文档中的数据加载到该对象中
fp = open('./test.html','r',encoding='utf-8')
soup = BeautifulSoup(fp,'lxml')
- 2.将互联网上获取的页面源码加载到该对象中
page_text = response.text
soup = BeatifulSoup(page_text,'lxml')
- 提供的用于数据解析的方法和属性:
- soup.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'):>表示的是一个层级
- oup.select('.tang > ul a'):空格表示的多个层级
- 获取标签之间的文本数据:
- soup.a.text/string/get_text()
- text/get_text():可以获取某一个标签中所有的文本内容
- string:只可以获取该标签下面直系的文本内容
- 获取标签中属性值:
- soup.a['href']
xpath解析:最常用且最便捷高效的一种解析方式。通用性。
- xpath解析原理:
- 1.实例化一个etree的对象,且需要将被解析的页面源码数据加载到该对象中。
- 2.调用etree对象中的xpath方法结合着xpath表达式实现标签的定位和内容的捕获。
- 环境的安装:
- pip install lxml
- 如何实例化一个etree对象:from lxml import etree
- 1.将本地的html文档中的源码数据加载到etree对象中:
etree.parse(filePath)
- 2.可以将从互联网上获取的源码数据加载到该对象中
etree.HTML('page_text')
- xpath('xpath表达式')
- xpath表达式:
- /:表示的是从根节点开始定位。表示的是一个层级。
- //:表示的是多个层级。可以表示从任意位置开始定位。
- 属性定位://div[@class='song'] tag[@attrName="attrValue"]
- 索引定位://div[@class="song"]/p[3] 索引是从1开始的。
- 取文本:
- /text() 获取的是标签中直系的文本内容
- //text() 标签中非直系的文本内容(所有的文本内容)
- 取属性:
/@attrName ==>img/src
四、验证码
验证码识别
反爬机制:验证码.识别验证码图片中的数据,用于模拟登陆操作。
识别验证码的操作:
- 人工肉眼识别。(不推荐)
- 第三方自动识别(推荐)
- 云打码:http://www.yundama.com/demo.html
云打码的使用流程:
- 注册:普通和开发者用户
- 登录:
- 普通用户的登录:查询该用户是否还有剩余的题分
- 开发者用户的登录:
- 创建一个软件:我的软件-》添加新软件-》录入软件名称-》提交(软件id和秘钥)
- 下载示例代码:开发文档-》点此下载:云打码接口DLL-》PythonHTTP示例下载
实战:识别古诗文网登录页面中的验证码。
使用打码平台识别验证码的编码流程:
- 将验证码图片进行本地下载
- 调用平台提供的示例代码进行图片数据识别
五、requests高级模块
模拟登录:
- 爬取基于某些用户的用户信息。
需求:对人人网进行模拟登录。
- 点击登录按钮之后会发起一个post请求
- post请求中会携带登录之前录入的相关的登录信息(用户名,密码,验证码......)
- 验证码:每次请求都会变化
需求:爬取当前用户的相关的用户信息(个人主页中显示的用户信息)
http/https协议特性:无状态。
没有请求到对应页面数据的原因:
发起的第二次基于个人主页页面请求的时候,服务器端并不知道该此请求是基于登录状态下的请求。
cookie:用来让服务器端记录客户端的相关状态。
- 手动处理:通过抓包工具获取cookie值,将该值封装到headers中。(不建议)
- 自动处理:
- cookie值的来源是哪里?
- 模拟登录post请求后,由服务器端创建。
session会话对象:
- 作用:
1.可以进行请求的发送。
2.如果请求过程中产生了cookie,则该cookie会被自动存储/携带在该session对象中。
- 创建一个session对象:session = requests.Session()
- 使用session对象进行模拟登录post请求的发送(cookie就会被存储在session中)
- session对象对个人主页对应的get请求进行发送(携带了cookie)
代理:破解封IP这种反爬机制。
什么是代理:
- 代理服务器。
代理的作用:
- 突破自身IP访问的限制。
- 隐藏自身真实IP
代理相关的网站:
- 快代理
- 西祠代理
- www.goubanjia.com
代理ip的类型:
- http:应用到http协议对应的url中
- https:应用到https协议对应的url中
代理ip的匿名度:
- 透明:服务器知道该次请求使用了代理,也知道请求对应的真实ip
- 匿名:知道使用了代理,不知道真实ip
- 高匿:不知道使用了代理,更不知道真实的ip
六、高性能异步爬虫
高性能异步爬虫
目的:在爬虫中使用异步实现高性能的数据爬取操作。
异步爬虫的方式:
- 1.多线程,多进程(不建议):
好处:可以为相关阻塞的操作单独开启线程或者进程,阻塞操作就可以异步执行。
弊端:无法无限制的开启多线程或者多进程。
- 2.线程池、进程池(适当的使用):
好处:我们可以降低系统对进程或者线程创建和销毁的一个频率,从而很好的降低系统的开销。
弊端:池中线程或进程的数量是有上限。
- 3.单线程+异步协程(推荐):
event_loop:事件循环,相当于一个无限循环,我们可以把一些函数注册到这个事件循环上,
当满足某些条件的时候,函数就会被循环执行。
coroutine:协程对象,我们可以将协程对象注册到事件循环中,它会被事件循环调用。
我们可以使用 async 关键字来定义一个方法,这个方法在调用时不会立即被执行,而是返回
一个协程对象。
task:任务,它是对协程对象的进一步封装,包含了任务的各个状态。
future:代表将来执行或还没有执行的任务,实际上和 task 没有本质区别。
async 定义一个协程.
await 用来挂起阻塞方法的执行。
七、动态加载数据
selenium模块的基本使用
问题:selenium模块和爬虫之间具有怎样的关联?
- 便捷的获取网站中动态加载的数据
- 便捷实现模拟登录
什么是selenium模块?
- 基于浏览器自动化的一个模块。
selenium使用流程:
- 环境安装:pip install selenium
- 下载一个浏览器的驱动程序(谷歌浏览器)
- 下载路径:http://chromedriver.storage.googleapis.com/index.html
- 驱动程序和浏览器的映射关系:http://blog.csdn.net/huilan_same/article/details/51896672
- 实例化一个浏览器对象
- 编写基于浏览器自动化的操作代码
- 发起请求: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()释放动作链对象
12306模拟登录
- 超级鹰:http://www.chaojiying.com/about.html
- 注册:普通用户
- 登录:普通用户
- 题分查询:充值
- 创建一个软件(id)
- 下载示例代码
- 12306模拟登录编码流程:
- 使用selenium打开登录页面
- 对当前selenium打开的这张页面进行截图
- 对当前图片局部区域(验证码图片)进行裁剪
- 好处:将验证码图片和模拟登录进行一一对应。
- 使用超级鹰识别验证码图片(坐标)
- 使用动作链根据坐标实现点击操作
- 录入用户名密码,点击登录按钮实现登录
八、scrapy框架
scrapy框架
- 什么是框架?
- 就是一个集成了很多功能并且具有很强通用性的一个项目模板。
- 如何学习框架?
- 专门学习框架封装的各种功能的详细用法。
- 什么是scrapy?
- 爬虫中封装好的一个明星框架。功能:高性能的持久化存储,异步的数据下载,高性能的数据解析,分布式
- scrapy框架的基本使用
- 环境的安装:
- mac or linux:pip install scrapy
- windows:
- pip install wheel
- 下载twisted,下载地址为http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
- 安装twisted:pip install Twisted‑17.1.0‑cp36‑cp36m‑win_amd64.whl
- pip install pywin32
- pip install scrapy
测试:在终端里录入scrapy指令,没有报错即表示安装成功!
- 创建一个工程:scrapy startproject xxxPro
- cd xxxPro
- 在spiders子目录中创建一个爬虫文件
- scrapy genspider spiderName www.xxx.com
- 执行工程:
- scrapy crawl spiderName
- scrapy数据解析
- scrapy持久化存储
- 基于终端指令:
- 要求:只可以将parse方法的返回值存储到本地的文本文件中
- 注意:持久化存储对应的文本文件的类型只可以为:'json', 'jsonlines', 'jl', 'csv', 'xml', 'marshal', 'pickle
- 指令:scrapy crawl xxx -o filePath
- 好处:简介高效便捷
- 缺点:局限性比较强(数据只可以存储到指定后缀的文本文件中)
- 基于管道:
- 编码流程:
- 数据解析
- 在item类中定义相关的属性
- 将解析的数据封装存储到item类型的对象
- 将item类型的对象提交给管道进行持久化存储的操作
- 在管道类的process_item中要将其接受到的item对象中存储的数据进行持久化存储操作
- 在配置文件中开启管道
- 好处:
- 通用性强。
- 面试题:将爬取到的数据一份存储到本地一份存储到数据库,如何实现?
- 管道文件中一个管道类对应的是将数据存储到一种平台
- 爬虫文件提交的item只会给管道文件中第一个被执行的管道类接受
- process_item中的return item表示将item传递给下一个即将被执行的管道类
- 基于Spider的全站数据爬取
- 就是将网站中某板块下的全部页码对应的页面数据进行爬取
- 需求:爬取校花网中的照片的名称
- 实现方式:
- 将所有页面的url添加到start_urls列表(不推荐)
- 自行手动进行请求发送(推荐)
- 手动请求发送:
- yield scrapy.Request(url,callback):callback专门用做于数据解析
- 五大核心组件
引擎(Scrapy)
用来处理整个系统的数据流处理, 触发事务(框架核心)
调度器(Scheduler)
用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
下载器(Downloader)
用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)
爬虫(Spiders)
爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面
项目管道(Pipeline)
负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
- 请求传参
- 使用场景:如果爬取解析的数据不在同一张页面中。(深度爬取)
- 需求:爬取boss的岗位名称,岗位描述
- 图片数据爬取之ImagesPipeline
- 基于scrapy爬取字符串类型的数据和爬取图片类型的数据区别?
- 字符串:只需要基于xpath进行解析且提交管道进行持久化存储
- 图片:xpath解析出图片src的属性值。单独的对图片地址发起请求获取图片二进制类型的数据
- ImagesPipeline:
- 只需要将img的src的属性值进行解析,提交到管道,管道就会对图片的src进行请求发送获取图片的二进制类型的数据,且还会帮我们进行持久化存储。
- 需求:爬取站长素材中的高清图片
- 使用流程:
- 数据解析(图片的地址)
- 将存储图片地址的item提交到制定的管道类
- 在管道文件中自定制一个基于ImagesPipeLine的一个管道类
- get_media_request
- file_path
- item_completed
- 在配置文件中:
- 指定图片存储的目录:IMAGES_STORE = './imgs_bobo'
- 指定开启的管道:自定制的管道类
- 中间件
- 下载中间件
- 位置:引擎和下载器之间
- 作用:批量拦截到整个工程中所有的请求和响应
- 拦截请求:
- UA伪装:process_request
- 代理IP:process_exception:return request
- 拦截响应:
- 篡改响应数据,响应对象
- 需求:爬取网易新闻中的新闻数据(标题和内容)
- 1.通过网易新闻的首页解析出五大板块对应的详情页的url(没有动态加载)
- 2.每一个板块对应的新闻标题都是动态加载出来的(动态加载)
- 3.通过解析出每一条新闻详情页的url获取详情页的页面源码,解析出新闻内容
- CrawlSpider:类,Spider的一个子类
- 全站数据爬取的方式
- 基于Spider:手动请求
- 基于CrawlSpider
- CrawlSpider的使用:
- 创建一个工程
- cd XXX
- 创建爬虫文件(CrawlSpider):
- scrapy genspider -t crawl xxx www.xxxx.com
- 链接提取器:
- 作用:根据指定的规则(allow)进行指定链接的提取
- 规则解析器:
- 作用:将链接提取器提取到的链接进行指定规则(callback)的解析
#需求:爬取sun网站中的编号,新闻标题,新闻内容,标号
- 分析:爬取的数据没有在同一张页面中。
- 1.可以使用链接提取器提取所有的页码链接
- 2.让链接提取器提取所有的新闻详情页的链接
- 分布式爬虫
- 概念:我们需要搭建一个分布式的机群,让其对一组资源进行分布联合爬取。
- 作用:提升爬取数据的效率
- 如何实现分布式?
- 安装一个scrapy-redis的组件
- 原生的scarapy是不可以实现分布式爬虫,必须要让scrapy结合着scrapy-redis组件一起实现分布式爬虫。
- 为什么原生的scrapy不可以实现分布式?
- 调度器不可以被分布式机群共享
- 管道不可以被分布式机群共享
- scrapy-redis组件作用:
- 可以给原生的scrapy框架提供可以被共享的管道和调度器
- 实现流程
- 创建一个工程
- 创建一个基于CrawlSpider的爬虫文件
- 修改当前的爬虫文件:
- 导包:from scrapy_redis.spiders import RedisCrawlSpider
- 将start_urls和allowed_domains进行注释
- 添加一个新属性:redis_key = 'sun' 可以被共享的调度器队列的名称
- 编写数据解析相关的操作
- 将当前爬虫类的父类修改成RedisCrawlSpider
- 修改配置文件settings
- 指定使用可以被共享的管道:
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 400
}
- 指定调度器:
# 增加了一个去重容器类的配置, 作用使用Redis的set集合来存储请求的指纹数据, 从而实现请求去重的持久化
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 使用scrapy-redis组件自己的调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 配置调度器是否要持久化, 也就是当爬虫结束了, 要不要清空Redis中请求队列和去重指纹的set。如果是True, 就表示要持久化存储, 就不清空数据, 否则清空数据
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-cli
- 执行工程:
- scrapy runspider xxx.py
- 向调度器的队列中放入一个起始的url:
- 调度器的队列在redis的客户端中
- lpush xxx www.xxx.com
- 爬取到的数据存储在了redis 的proName:items这个数据结构中
九、增量式爬虫
增量式爬虫
- 概念:监测网站数据更新的情况,只会爬取网站最新更新出来的数据。
- 分析:
- 指定一个起始url
- 基于CrawlSpider获取其他页码链接
- 基于Rule将其他页码链接进行请求
- 从每一个页码对应的页面源码中解析出每一个电影详情页的URL
- 核心:检测电影详情页的url之前有没有请求过
- 将爬取过的电影详情页的url存储
- 存储到redis的set数据结构
- 对详情页的url发起请求,然后解析出电影的名称和简介
- 进行持久化存储