爬虫基础
一、君子协议
服务器渲染:在服务器那边直接把数据和HTML整合在一起,统一返回给浏览器
客户端渲染:第一次请求只拿到和HTML的骨架,第二次请求拿到数据,进行结合,在页面源代码中,看不到数据,拿到数据需要进行抓包
二、数据解析
- re解析 正则表达式 快
- bs4解析 简单 效率低
- xpath解析 流行的解析方式
正则表达式
语法:使用元字符进行排列
eg:爬取电影天堂
1 | # -*- coding:utf-8 -*- |
bs4
思路:以爬取优美图库为例
1 | # -*- coding:utf-8 -*- |
Xpath
在XML文档中搜索内容的一门语言
注意:不能匹配tbody标签 可以//越过tbody
html是xml的一个子集
eg:爬取猪八戒网
1 | # -*- coding:utf-8 -*- |
三、多线程
线程:执行单位
进程:资源单位,每一个进程至少有一个线程
启动一个程序,默认有一个主线程
多线程创建
1 | # 第一种:简单,直白 |
多进程创建
1 | from multiprocessing import Process |
线程池:一次性开辟一些线程,用户直接给线程池提交任务,线程任务的调度交给线程池来完成
1 | from concurrent.futures import ThreadPoolExecutor(线程池类),ProcessPoolExecutor(进程池类) |
四、常用数据解析方法
- re解析 正则表达式
思路:使用元字符进行匹配
1 | . 匹配除换行符之外的任意字符 |
Eg: 爬取盗版电影天堂
1 | # -*- coding:utf-8 -*- |
- bs4
Eg: 爬取优美图库
1 | # -*- coding:utf-8 -*- |
- xpath
Eg: 爬取猪八戒网
1 | # -*- coding:utf-8 -*- |
```python
-- coding:utf-8 --
@Time :2022/8/9 18:57
@SOFTWARE :爬虫学习
“””
响应内容的分类: 1.结构化: json数据(高频出现): json模块 re模块 jsonpath模块 xml数据(低频出现): re模块 lxml模块 bs4模块 2.非结构化: html数据:re模块 lxml模块 xml: 可拓展标记语言 更专注于对数据的传输和存储
“””
jsonpath 多层嵌套的复杂字典直接提取数据
import jsonpath
$ 根节点 最外层的大括号
. 子节点
.. 内部任意位置 子孙结点
jsonpath 结果为列表 获取数据需要索引
lxml模块 使用
import lxml
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
-
## 五、scrapy爬虫框架
- ### CrawlSpider:类 Spider的一个子类
- 全站数据爬取的方式:
- 基于Spider:手动请求
- 基于CrawlSpider
- CrawlSpider的使用:
- 创建 一个工程
- cd xxx
- 创建爬虫文件(CrawlSpider):
- scrapy genspider -t crawl xxx www.xxxx.com
- LinkExtractor 链接提取器
- 作用:根据指定规则((allow=r'正则'))进行指定链接的提取
- 规则解析器
- 作用:将链接提取器提取到的链接进行指定规则(callback) 的解析操作
- 需求:爬取网站的编号 标题 内容 标号
- 分析:爬取的数据没有在同一张页面中
- 1. 可以使用链接提取器提取所有的页码链接
2. 让链接提取器提取所有的详情页的链接
- ### 分布式爬虫
- 概念:我们需要搭建一个分布式机群 让其对一组资源进行分布式联合爬取
- 作用:提升爬虫效率
- 如何实现分布式?
- 安装scrapy-redis的组件
- 原生的sctapy是不可以试下分布式爬虫 必须要让scrapy结合着scrapy-redis组件一起实现分布式爬虫
- 为什么原生的scrapy不能实现分布式?
- 调度器不可以被分布式机群共享
- 管道不可以被分布式机群共享
- scrapy-redis组件的作用:
- 可以给原生的scrapy框架提供可以被共享的管道和调度器
- 实现流程
- 创建一个工程
- 创建一个基于CrawlSpider的爬虫文件
- 修改当前的爬虫文件:
- 导包 from scrapy_redis.spider import RedisCrawlSpider
- 将start_url 和 allowed_domains 进行注释
- 修改配置文件settings
- 指定使用可以被共享的管道
- ITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline' : 400}
- 指定调度器
- 增加一个去重容器类的配置 作用使用Redis的set集合来存储请求的指纹数据
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.REFDupeFilter"
- 使用scrapy_redis组件自己的调度器
SCHEDULER = "scrapy_reds.scheduler.Scheduler"
- 配置调度器是否要持久化 也就是当爬虫结束了 要不要清空Redis中请求队列和去重指纹的set 实现增量式爬虫 只爬取没有爬过的网站
SCHEDULER_PERSIST = True
- redis相关操作配置:
- 配置redis的配置文件:
- ### 增量式爬虫
- 概念:检测网站数据更新的情况 只会爬取网站最新更新出来的数据
- 分析
- 指定一个起始rul
- 基于CrawlSpider获取其他页码链接
- 基于rule将其他页码链接进行请求
- 从每一个页码对应的源码中解析出每一个电影详情页的url
- 核心: 检测电影详情页的url之前有没有请求过
- 将爬取过的电影详情页的url存储
- 存储到Redis的set数据结构(自动去重)中
- 对详情页url发起请求 然后解析出电影的名称和简介
- 进行持久化存储
## 六、js算法解析处理
- ### 常见的加密算法
- 线性散列MD5算法
- 对称加密DES/AES算法
- 非对称加密算法RSA
- base64伪加密
- https证书秘钥加密
### MD5加密
MD5是一种被广泛使用的线性散列算法 可以产出一个128位 (16字节) 的散列值(hash value) 用于确保信息传输的一致性 且MD5加密之后产生的是一个固定长度(32位或者16位)的数据
- #### 解密
- 理论上不存在解密 但可以进行反向暴力破解
- #### 增加破解的成本
- 使用一段无意义且随机生成的私钥进行MD5加密会生成一个加密串1
- 将要加密的数据跟串1进行拼接 再进行一次MD5加密 生成串2
- 将串2再进行MD5加密 生成的串3就是最终加密后的数据
- 注册账号时的密码 一般使用MD5加密
### DES/AES算法加密
- DES:Data Encraption Standard 即数据加密标准 是一种使用秘钥加密的算法 该加密算法是一种**对称加密方式** 其加密运算 解密运算需要使用的是同样的秘钥(一组字符串)
- 注意
- 现在用这个AES标准代替DES
- AES和DES的区别
- DES加密后密文长度是8的整数倍
- AES加密后密文长度是16的整数倍
- 应用场景的不同
- 企业级开发使用DES足够安全
- 如果要求高使用AES
- DES/AES切换只需要修改CraptoJS.AES <=> CraptoJS.DES
- 使用AES/DES进行数据交互时要求双方都拥有相同的私钥
- 破解方法
- 暴力破解
- DES如果使用56位的秘钥 则可能的秘钥数量是2的56次方个
- DES算法的四个入口参数:
- Key 、Data 、Mode、 padding
### RSA加密
RSA加密算法是一种**非对称加密算法** 在公开秘钥加密和电子商业中RSA被广泛使用
- 非对称加密算法
- 非对称加密算法需要有两个秘钥:
- 公开秘钥 简称公钥 publickey
- 私有秘钥 简称私钥 privatekey
- 公钥与私钥是一对 如果公钥对数据加密 只有对应的私钥才能解密 。因为加密和解密使用的是两个不同的秘钥 所以叫做非对称加密
- 注意
- 使用时都是使用公钥加密 私钥解密 公钥可以公开 私钥自己保留
- 算法强度复杂、安全性依赖于算法与秘钥但是由于其算法复杂 而使得加密解密速度没有对称加密解密的快
- 使用流程和场景介绍
- 通过公钥加密 使用私钥解密 **私钥是通过公钥计算生成的** 假设ABC三方之间相互进行加密通信。大家相互之间使用公钥进行信息加密 信息读取时使用各自对应的私钥进行信息解密
- 用户输入的支付密码会通过RSA加密
### Base64伪加密
- Base64是一种用64个字符来表示任意二进制数据的方法。base64是一种编码方式而不是加密算法。只是看上去像是加密而已。
- Base64使用A-Z a-z 0-9 + / 这64个字符实现对数据进行加密
HTTPS加密
https是基于http和SSL/TLS实现的一个协议 他可以保证在网络上传输的数据都是加密的 从而保证数据安全
- http协议是不安全的
- http协议在数据传输的过程中都是铭文 所以存在在数据泄露和篡改
- 使用对称秘钥进行数据加密
- 为了防止数据泄露和篡改 我们队数据进行加密 可以生成一个对称密码 将对称秘钥分别交给浏览器和服务器端 他们之间传输的数据都使用对称秘钥进行加密和解密
- 请求和响应流程:
- 客户端使用对称秘钥对请求进行加密并发送给服务端
- 服务端接收到密文后 使用对称秘钥进行解密 然后处理请求 最后使用对称秘钥把返回数据进行加密 返回给客户端
- 客户端接收到密文后 使用对称秘钥进行解密 得到响应内容
## 三、selenium
自动化测试工具
环境搭建:
1. pip install selenium
2. 安装浏览器驱动webdriver
- ```python
# -*- coding:utf-8 -*-
# @Time :2022/8/10 10:34
# @SOFTWARE :爬虫学习
from selenium import webdriver
url = "http://www.baidu.com"
driver = webdriver.ChromiumEdge
driver.get(url)
print(driver.page_source)
print(driver.current_url)
print(driver.title)
抓取某拉钩视频
- ```python
-- coding:utf-8 --
@Time :2022/5/2 10:32
@SOFTWARE :爬虫学习
from selenium import webdriver
import time
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.edge.options import Options # 无头浏览器web = webdriver.Edge()web.get(“https://www.lagou.com/")
el = web.find_element(By.XPATH,’//*[@id=”changeCityBox”]/p[1]/a’)el = web.find_element_by_xpath(‘//*[@id=”changeCityBox”]/p[1]/a’)
el.click()time.sleep(2)
web.find_element_by_xpath(‘//*[@id=”search_input”]’).send_keys(‘python’, Keys.ENTER)
web.find_element(By.XPATH,’//[@id=”search_input”]’).send_keys(‘python’,Keys.ENTER)
li_list = web.find_elements(By.XPATH,’//[@id=”jobList”]/div[1]/div’)
for li in li_list:company_name = li.find_element(By.XPATH,'./div[1]/div[2]/div[1]/a').text # //*[@id="jobList"]/div[1]/div[1]/div[1]/div[2]/div[1]/a price = li.find_element(By.XPATH,'./div[1]/div[1]/div[2]/span').text location = li.find_element(By.XPATH,'./div[1]/div[1]/div[1]/a').text # job = li.find_element(By.XPATH,'') print(price,company_name,location)
如何进行切换窗口
当我们利用selenium打开新窗口时,默认还在原来的窗口
web.switch_to.window(web.window_handles[-1])
web.close()
处理iframe,需要先切拿到iframe,切换到iframe,再进行拿数据
web.switch_to.frame(xxx) 切换到iframe 页面
web.switch_to.default_content() 切换回原页面
无头浏览器 -> 让浏览器后台运行,界面不出现
from selenium.webdriver.edge.options import Options # 无头浏览器
opt = Options()
opt.add_argument(“–headless”)
opt.add_argument(“–disbale–gpu”)
web = webdriver.Edge(options=opt)
class car():def __init__(self): pass def __str__(self): return 1 def __del__(self): pass
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
-
## 四、相关库
- session
- ```python
# -*- coding:utf-8 -*-
# @Time :2022/8/9 11:56
# @SOFTWARE :爬虫学习
import requests
import re
"""
requests.session 进行状态保持
作用:
自动处理cookie
场景:
连续的多次请求
"""
def login():
# session对象
session = requests.session()
# headers
session.headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.81 Safari/537.36 Edg/104.0.1293.47"
}
# url1 获取token
url1 = "https://github.com/login"
# 发送请求获取相应
res1 = session.get(url1).content.decode()
# 正则提取
token = re.findall('name="authenticity_token" value="(.*?)" />',res1)[0]
# print(token)
# url2 登录
url2 = "https://github.com/session"
# 构建表单数据
data = {
"commit": "Sign in",
"authenticity_token": token,
"login": "1946497315@qq.com",
"password": "Yzh963987",
"webauthn-support": "supported",
"webauthn-iuvpaa-support": "supported",
}
# 发送请求登录
res2 = session.post(url2,data = data)
# url3 验证
url3 = "https://github.com/Bajibajj"
response = session.get(url3)
with open('github.html','wb') as f:
f.write(response.content)
if __name__ == '__main__':
login()
- ```python
post提交表单
- ```python
-- coding:utf-8 --
@Time :2022/8/9 11:21
@SOFTWARE :爬虫学习
import requests
import jsonurl
headers
data
1.固定值 抓包获取的不变值
2.输入值 抓包比较
3.预设值 静态文件 需要提前从惊天html中获取
4.预设值 发送请求 需要对指定地址发送请求
5.在客户端生成 分析js模拟生成数据
发送请求相应
数据解析 json
url
url = “https://fanyi.baidu.com/v2transapi?from=zh&to=en"headers
headers = {
“User-Agent”: “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.81 Safari/537.36 Edg/104.0.1293.47”
}dat = {
}res = requests.post(url,headers = headers,data=dat)print(res.content.decode())"from": "zh", "to": "en", "query": "字典", "transtype": "realtime", "simple_means_flag": "3", "sign": "763934.1002287", "token": "198e3bfc271aca63eb0ea4a09e56f78b", "domain": "common"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- verify
- ```python
# -*- coding:utf-8 -*-
# @Time :2022/8/9 11:16
# @SOFTWARE :爬虫学习
import requests
url = "http://www.baidu.com"
response = requests.get(url,verify = False)
print(response.text)
- ```python