-
Notifications
You must be signed in to change notification settings - Fork 257
/
note.txt
451 lines (412 loc) · 20.7 KB
/
note.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
爬虫在使用场景中的分类:
- 通用爬虫
抓取系统重要组成部分。抓取的是一整张页面的数据。
- 聚焦爬虫
是建立在通用爬虫的基础之上。抓取的是页面中特定的局部内容。
- 增量式爬虫
检测网站中数据更新的情况。只会抓取网站中最新更新出来的数据。
爬虫的矛与盾:
- 反爬机制
门户网站,可以通过制定相应的策略或者技术手段,防止爬虫程序进行网站数据的爬取。
- 反反爬策略
爬虫程序可以通过制定相关的策略或者技术手段,破解门户网站中具备的反爬机制,从而获取门户网站中的相关数据。
http协议:
- 概念:服务器与客户端进行数据交互的一种形式。
- 常用请求头信息:
(1)User-Agent:请求载体的身份标识。
(2)Connection:请求完毕后,是断开连接还是保持连接。
- 常见响应头信息:
(1)Content-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 -- 如何提取键值以及组装成自己想要的样子