crawlProject/note.txt
2023-07-19 11:01:44 +08:00

451 lines
21 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

爬虫在使用场景中的分类:
- 通用爬虫
抓取系统重要组成部分。抓取的是一整张页面的数据。
- 聚焦爬虫
是建立在通用爬虫的基础之上。抓取的是页面中特定的局部内容。
- 增量式爬虫
检测网站中数据更新的情况。只会抓取网站中最新更新出来的数据。
爬虫的矛与盾:
- 反爬机制
门户网站,可以通过制定相应的策略或者技术手段,防止爬虫程序进行网站数据的爬取。
- 反反爬策略
爬虫程序可以通过制定相关的策略或者技术手段,破解门户网站中具备的反爬机制,从而获取门户网站中的相关数据。
http协议
- 概念:服务器与客户端进行数据交互的一种形式。
- 常用请求头信息:
1User-Agent请求载体的身份标识。
2Connection请求完毕后是断开连接还是保持连接。
- 常见响应头信息:
1Content-Type服务器响应回客户端的数据类型
https协议
- 概念:安全的超文本传输协议
- 加密方式
1对称密钥加密
2非对称密钥加密
3证书密钥加密
requests模块python中原生的一款基于网络请求的模块功能强大简单便捷效率极高。
- 作用:模拟浏览器发请求
- 如何使用
1指定url
2发起请求Get or Post
3获取响应数据
4持久化存储
动态加载数据:
网页信息可能是动态加载的ajax动态请求可以在XHR中查看真正url
数据解析:
- 正则
- bs4
- xpath***
数据解析原理:
大部分文本内容都存在标签中或者标签对应的属性中
进行指定标签定位
标签或标签对应的属性中存储的数据值进行提取(解析)
聚焦爬虫编码流程:
- 指定url
- 发起请求
- 获取响应数据
- 数据解析
- 持久化存储
正则表达式:(import re)
- 单字符:
. : 除换行以外的所有字符
[] : [aoe] [a-w] 匹配集合任意一个字符
\d : 数字 [0-9]
\D : 非数字
\w : 数字,字谜,下划线,中文
\W : 非\w
\s : 所有的空白字符,包括空格,制表符,换页符等等。等价于 [\f\n\r\t\v]
\S : 非空白
- 数量修饰:
* : 任意多次 >=0
+ : 至少依次 >=1
? : 可有可无0次或1次
{m} : 固定m次 hello{3, }
{m,} : 至少m次
{m,n} : m-n次
- 边界:
$ : 以某某结尾
^ : 以某某开头
- 分组:
(ab)
- 贪婪模式: .*
- 非贪婪模式: .*?
- re.I: 忽略大小写
- re.M: 多行匹配
- re.S: 单行匹配
- re.sub(正则表达式,替换内容, 字符串)
<div class="pic">
<a href="https://www.douban.com/photos/album/1727324287/">
<img src="https://img1.doubanio.com/view/photo/albumcover/public/p2578730628.webp"
data-origin="https://img1.doubanio.com/view/photo/albumcover/public/p2578730628.webp" alt="">
</a>
</div>
ex = '<div class="pic">.*?<img src=.*? data-origin="(.*?)" alt=.*?</div>'
bs4进行数据解析
- 数据解析原理:
标签定位
提取标签、标签属性的数据值
- bs4数据解析原理
1.实例化一个BeautifulSoup对象并将页面源码数据加载到该对象中
2.通过调用BeautifulSoup对象中相关属性或者方法进行标签定位和数据提取
- 进行环境的安装:
pip install bs4
pip install lxml
- 如何实例化BeautifulSoup对象
1.from bs4 import BeautifulSoup
2.对象的实例化
- 将本地的html文档中的数据加载到该对象中
fp = open('./test.html', 'r', encoding='utf-8')
soup = BeautifulSoup(fp, 'lxml')
- 将互联网上获取的页面源码加载到该对象中
page_text = response.text
soup = BeautifulSoup(page_text, 'lxml')
- 提供的用于数据解析的方法和属性:
soup.tagName: 返回的是文档中出现的第一个tagName标签
soup.find()
- soup.find('tagName'): 相当于soup.tagName
- soup.find('tagName', class_='song'): 属性定位
- soup.find_all('tagName'): 返回符合要求的所有标签,是一个列表
soup.select():
- select('某种选择器'): 返回的是一个列表
- 层级选择器:
soup.select('.tang > ul > li > a') > 表示一个层级
soup.select('.tang > ul a') ' '空格表示多个层级
- 获取标签中的文本数据
soup.a.text/.strong/.get_text():
text/get_text(): 可以获取某一个标签中的所有文本内容
string: 只可以获取该标签下面直系的文本内容
- 获取标签中属性值
soup.a['属性名']
xpath解析 最常用且最便捷高效的一种解析方式,通用性强。
- 原理1.实例化一个etree对象且需要将被解析的页面源码数据加载到该对象中。
2.调用etree对象中的xpath方法结合着xpath表达式实现标签的定位和内容的捕获。
- 环境的安装:
pip install lxml
- 如何实例化etree对象: from lxml import etree
将本地的html文档中的数据加载到该对象中
etree.parse(filePath)
将互联网上获取的页面源码加载到该对象中
etree.HTML(page_text)
- xpath("xpath表达式")
/: 表示从根节点开始定位,一个/表示一个层级.
//: 表示多个层级(可以表示从任意位置定位)
属性定位: //div[@class="song"] tag[@attrNAme="attrValue"]
索引定位: tree.xpath('//div[@class="song"]/p[3]') 索引是从1开始的
取文本:/text() 只能取到标签的直系文本 //text() 可以取到标签中非直系的文本内容(所有文本内容)
取属性:/@attrName ==>img/@src
局部解析: title = li.xpath('./a/div[2]//h3/text()')[0] 一定要加"."
验证码识别:
- 识别验证码操作:
- 人工肉眼识别。(不推荐)
- 第三方自动识别。(推荐)
- ddddocr库
import ddddocr
ocr = ddddocr.DdddOcr()
with open('1.png', 'rb') as f:
img_bytes = f.read()
res = ocr.classification(img_bytes)
print(res)
模拟登录:
- 爬取基于某些用户的用户信息。
- 点击登录按钮之后可能会发起一个post请求
- post请求中会携带相关的用户信息用户名密码验证码....
- 页面会更新_VIEWSTATE 页面隐藏域和__VIEWSTATEGENERATOR 页面隐藏域时,我们需要对这个数据也进行爬取
viewstate = tree.xpath("//input[@id='__VIEWSTATE']/@value")[0]
viewstategenerator = tree.xpath("//input[@id='__VIEWSTATEGENERATOR']/@value")[0]
EVENTVALIDATION = tree.xpath("//input[@id='__EVENTVALIDATION']/@value")
- 我们一次只能用requests发一次请求之后再需要发请求时用Session(),将请求包装成一个对象,这样就不会导致访问失败
session = requests.Session()
code_data = session.get(url=code_img_src, headers=headers).content
http/https协议特性 无状态
- 发起的第二次基于个人页面请求的时候,服务器端并不知道此请求是基于登录状态下的请求
- cookie 用来让服务器端记录客户端的相关状态
- 自动处理: cookie值的来源 登陆时post请求中携带有cookie值
session会话对象
- 作用:
- 可以进行请求的发送
- 如果请求过程中产生了cookie则该cookie会被自动存储携带在该session对象中
代理: 破解封IP这种反爬机制
什么是代理:
- 代理服务器
代理的作用:
- 突破自身ip访问的限制
- 可以隐藏自身真实的ip
代理IP的匿名度
- 透明服务器知道使用了代理知道真实ip
- 匿名服务器知道使用了代理不知道真实IP
- 高匿:服务器不知道使用了代理
高性能异步爬虫:
- 异步爬虫的方式:
- 多线程,多进程(不建议): 无法无限制的开启多线程和多进程。
- 进程池、线程池:池的容量是有上线的。
- 单线程 + 异步协程(推荐):
- event_loop: 事件循环,相当于一个无限循环,我们可以把一些函数注册到这个事件循环上,当满足某些条件时,函数就会被循环执行。
- coroutine: 协程对象我们可以将协程对象注册到事件循环中它会被事件循环调用。我们可以使用async关键字来定义一个方法这个方法在调用的时候不会立即执行而是返回一个协程对象。
- task: 任务,它是对协程对象的进一步封装,包含了任务的各个状态。
- future: 代表将要执行或还没有执行的任务实际上和task没有本质区别。
- async: 定义一个协程。
- await: 用来挂起阻塞方法的执行。
selenium模块的基本使用
- 下载selenium pip install selenium
- 下载一个浏览器驱动程序(谷歌)
- 下载路径http://chromedriver.storage.googleapis.com/index.html
- 实例化一个浏览器对象
- 编写基于浏览器自动化操作代码
- 发起请求: get(url)
- 标签定位: find系列方法
- 标签交互: send_keys('xxx')
- 执行js程序: execute_script('jsCode')
- 前进,后退: back(),forward()
- 关闭浏览器: quit()
- selenium处理iframe
- 如果定位的标签存在iframe标签之中则必须使用switch_to.iframe(id)
- 动作链: from selenium.webdriver import ActionChains
- 实例化一个动作链对象: action = ActionChains(bro)
- 执行操作: action.click_and_hold(div) action.move_by_offset(17, 0).perform()
- 释放对象: action.release()
scrapy框架
- 什么是框架?
- 集成了很多功能并且具有很强通用性的一个项目模板。
- 如何学习框架?
- 专门学习框架封装的各种功能的详细用法。
- 什么是scrapy?
- 爬虫中封装好的一个明星框架。
- 功能: 高性能的持久化存储,异步的数据下载,高性能的数据解析操作,分布式
- 基本使用
- 环境安装: mac/linux: pip install scrapy
: windows: pip install wheel
下载twisted下载地址为: http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
下载twisted: pip install Twisted-20.3.0-cp39-cp39-win_amd64.whl
pip install pywin32
pip install scrapy
- 创建一个工程: scrapy startproject xxxPro
- cd xxxPro
- 在spiders子目录中创建一个爬虫文件
- scrapy genspider spiderName www.xxx.com
- 执行: scrapy crawl spiderName(scrapy crawl test --nolog 采用无日志信息输出,但是这样不好,我们使用接下来的方法)
- 在配置文件中添加: LOG_LEVEL = 'ERROR' 表示只输出错误信息
- 修改user-agent: USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
- scrapy数据解析
- 在parse(self, response)函数中进行编写详细操作方法可见qiubai案例
- scrapy持久化存储
- 基于终端命令的持久化存储:
- 要求: 只可以将parse方法的返回值存储到本地的文本文件中
- 注意持久化存储的文件类型只可以为 'json', 'jsonlines', 'jl', 'csv', 'xml', 'marshal', 'pickle'
- scrapy crawl xxx -o filePath
- 优点: 简介高效便捷
- 缺点: 局限性比较强(数据只可以存储到指定文件后缀的文本文件中)
- 基于管道持久化存储:
- 数据解析
- 在item类中定义相关的属性
- 将解析到的数据封装到item类型的对象
- 将item类型的对象提交给管道进行持久化存储
- 在管道类的process_item中要将其接受到的item对象中存储的数据进行持久化存储操作
- 在配置文件中开启管道
- 好处:通用性强
- 爬虫文件提交的item类型对象最终会提交给哪一个管道类
- 先执行的管道类
面试题: 将爬取到的数据一份存储到本地一份存储到数据库,如何实现?
- 管道文件中一个管道类对应的是将数据存储到一种平台
- 爬虫文件提交的item只会给管道文件种第一个被执行的管道类接受
- process_item中的return item表示item将会被传递给下一个即将被执行的管道类
基于spider的全站数据爬取
- 就是将网站下的某板块下的全部页码对应的页面数据进行爬取
- 爬取笑话网的段子数据
- 将所有页面的url添加到start_urls列表(不推荐)
- 自行手动进行请求发送
scrapy五大核心部件
- 管道
- 与引擎交互,之后持久化存储
- spider
- 爬虫文件中的爬虫类
- 调度器
- 过滤器: 过滤重复的请求对象
- 队列: 存放请求对象
- 下载器
- 与互联网通信,数据下载
- 引擎
- 接收队列以及发送response
- 所有类都要经过引擎
- 核心作用: 用作数据流处理以及触发事件
请求传参
- 使用场景: 爬取解析的数据不在同一张页面中。(深度爬取)
- 需求: 爬取boss直聘的岗位名称岗位描述
图片数据爬取之ImagePipeline
- 基于scrapy爬取字符串类型数据和图片类型数据的区域
- 字符串: 只需要基于xpath解析且提交管道进行持久化存储
- 图片: 我们只能解析到图片地址,之后我们要单独对图片地址发起请求获取图片二进制类型数据
- 基于ImagePipeline:
- 只需要将img的src属性值提交给管道管道就会对图片的src进行请求发送获取图片的二进制类型的数据且还会进行持久化存储
- 需求: 爬取站长素材中的高清图片
中间件
- 下载中间件
- 位置: 引擎和下载器之间
- 作用: 批量拦截到整个工程中所有的请求和响应
- 拦截请求:
- 请求头信息(UA伪装, 代理ip)
- 拦截响应:
- 篡改响应数据,响应对象
CrawlSpider: 类Spider的一个子类
- 全站数据爬取的方式
- 基于spider实现: 手动请求发送
- 基于CrawlSpider实现
- CrawlSpider的使用:
- 创建一个工程
- 创建爬虫文件: scrapy genspider -t crawl xxx www.xxx.com
- 链接提取器
- 作用: 根据指定规则(allow=r'正则表达式')进行指定链接提取
- 规则解析器
- 作用: 将链接提取器提取到的链接进行指定规则(callback)的解析操作
分布式爬虫
- 概念: 我们需要搭建一个分布式机群,让其对一组资源进行分布联合爬取
- 作用: 提示爬取数据的效率
- 如何实现分布式?
- 安装一个scrapy-redis的组件
- 原生的scrapy是不可以实现分布式爬虫必须要让scrapy结合这scrapy-redis组件一起实现分布式爬虫
- 为什么原生的scrapy不可以实现分布式爬虫
- 不同电脑的scrapy调度器不能共享
- 管道不可以被分布式机群共享
- scrapy-redis组件的作用:
- 可以给原生的scrapy框架提供可以被共享的管道和调度器
- 实现流程
- 创建一个工程
- 创建一个基于crawlspider的爬虫文件
- 修改当前的爬虫文件:
- from scrapy_redis.spiders import RedisCrawlSpider
- 将start_url allowed_domain进行注释
- redis_key = 'name' 可以被共享的调度器队列的名称
- 编写相关的数据解析操作
- 将当前爬虫类的父类修改成RedisCrawlSpider
- 修改配置文件
- 指定可以被共享的管道:
- ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeLine': 400,
}
- 指定调度器
- DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDuperFilter'
- SCHEDULER = 'scrapy_redis.scheduler.Scheduler'
- SCHEDULER_PERSIST = True
- 指定redis服务器
- REDIS_HOST = '127.0.0.1' # 服务器ip
- REDIS_PORT = 6379
- redis相关操作配置:
- 配置redis的配置文件:
- linux或者mac: redis.conf
- windows: redis.windows.conf
- 打开配置文件修改:
- 注释绑定: # bind 127.0.0.1
- 关闭保护模式: protected-mode no
- 结合配置文件启动redis数据库
- redis-server 配置文件
- ./redis-cli
- 执行工程
- scrapy runspider xxx.py
- 向调度器的队列中放入起始url
- 调度器的队列在redis客户端中
- lpush xxx www.xxx.com
增量式爬虫
- 概念: 检测网站数据更新情况,只会爬取网站最新更新出来的数据
- 分析:
- 起始url
- 基于CrawlSpider获取其他页面链接
- 基于Rule将其他页码链接进行请求
- 从每一个页面对应的页面源码中解析出每一部电影详情页的url
- 核心: 检测详情页的url之前有没有请求过
- 将爬取过的电影详情页url存储
- 存储到redis的set数据库
conn = Redis(host='127.0.0.1', port=6379)
ex = self.conn.sadd('urls', detail_url)
if ex == 1:
print('该url没有被爬取过可以进行数据爬取'
yield scrapy.Request(detail_url, callback=self.parse_detail)
else:
print('数据还没有更新,暂无新数据!')
class PipeLine(object):
conn = None
def open_spider(self, spider):
self.conn = spider.conn
def process_item(self, item, spider):
dic = {
'name': item['name'],
'desc': item['desc'],
}
print(dic)
self.conn.lpush('movieData', dic)
return item
- 对详情页的url发起请求然后解析出电影的名称和时间
- 进行持久化存储
scrapy_splash
- 使用scrapy_splash最终拿到的response相当于在浏览器全部渲染完成之后的网页页面
- 作用: 模拟浏览器加载js并返回js运行后的数据
- 安装环境:
- 安装docker
- sudo docker pull scrapinghub/splash
- 尝试运行镜像:
- 在前台运行: sudo docker run -p 8050:8050 scrapinghub/splash
- 在后台运行: sudo docker run -d -p 8050:8050 scrapinghub/splash
JS逆向
- 数据加密
- 看到的是一堆密文
- 请求头加密
- 表单加密
- 模拟生成规则,在被加密前是什么内容
- 参数加密
- cookie加密
- 通常是在浏览器有正确地响应但是爬虫返回的是一堆js代码或者非正常的响应
Web逆向技巧
- 爬虫的接口定位
- 字体加密Unicode编码数据加密
- 无混淆的js
- 关键字搜索
- 解密搜decrypt
- 加密搜encrypt
- ajax渲染搜JSON.parse JSON.parse函数或者方法密文a = 函数或者方法(密文)|JSON.parse(a)
- 搜接口自带的关键字(特点:方法或者函数包裹密文数据)
- xhr断点
- 路径搜索
- 跟栈
- hook
- 反debug: 内存写入变量
- 注入: 控制台注入 本地替换
1. html -- lxml -- re
2. json -- 如何提取键值以及组装成自己想要的样子