Skip to content

Commit 5188ca4

Browse files
committed
Merge branch 'master' of github.com:wkunzhi/Python3-Spider
2 parents c053ae5 + abf08d6 commit 5188ca4

File tree

3 files changed

+305
-0
lines changed

3 files changed

+305
-0
lines changed

Diff for: 【bilibili】自动登录/README.md

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
## B站自动登录
2+
3+
本案例根据 `selenium` 实现。
4+
5+
## 效果图
6+
7+
![image](https://csrftoken.oss-cn-beijing.aliyuncs.com/github/blibili-login-report.png)
8+
9+
## Q&A
10+
11+
> ChromeDriver - WebDriver for Chrome
12+
13+
```
14+
因为是模拟点击,所以需要下载插件。
15+
16+
点击下方链接即可跳转至下载界面。
17+
```
18+
19+
> 为什么要模拟滑动多次?
20+
21+
```
22+
因为获取滑块的偏移量,在模拟操作的时候,机器在控制滑动速度的时候比较均匀,可能会被判定为机器。
23+
24+
当然了,我们会在今后给予更好的滑动支持~
25+
```
26+
27+
[下载ChromeDriver](https://chromedriver.chromium.org/downloads)
28+
29+
## Support
30+
31+
```
32+
案例于 2020-04-23 前均可用,如有疑问请联系作者。
33+
```
34+
35+
## Donate
36+
37+
Thanks ~

Diff for: 【bilibili】自动登录/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#! /usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
# Date: 2020/4/23

Diff for: 【bilibili】自动登录/login.py

+265
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
#! /usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
# Date: 2020/4/23
4+
5+
import time
6+
import base64
7+
import random
8+
9+
from io import BytesIO
10+
from PIL import Image
11+
12+
from selenium import webdriver
13+
from selenium.webdriver import ActionChains
14+
from selenium.webdriver.common.by import By
15+
from selenium.webdriver.support.ui import WebDriverWait
16+
from selenium.webdriver.support import expected_conditions as ec
17+
18+
19+
BROWSER_EXECUTABLE_PATH = "/Users/liuzhichao/PycharmProjects/bilibili/chromedriver"
20+
21+
22+
class LoginBli:
23+
24+
# 登录URI
25+
login_url = "https://passport.bilibili.com/login"
26+
27+
def __init__(self, username, password):
28+
"""初始化
29+
30+
Parameters
31+
----------
32+
username: string
33+
B站账号
34+
35+
password: string
36+
B站密码
37+
38+
"""
39+
self.username = username
40+
self.password = password
41+
# 定义浏览器
42+
self.browser = webdriver.Chrome(executable_path=BROWSER_EXECUTABLE_PATH)
43+
# 定义显示等待
44+
self.wait = WebDriverWait(self.browser, 20)
45+
46+
def open(self):
47+
"""模拟点击登陆
48+
49+
自动打开浏览器, 进入登陆界面
50+
输入用户名, 密码
51+
"""
52+
# 打开浏览器, 进入登陆界面
53+
self.browser.get(self.login_url)
54+
55+
# 用户名输入框
56+
self.wait.until(
57+
ec.presence_of_element_located((By.ID, 'login-username'))
58+
).send_keys(self.username)
59+
# 清空用户名输入框
60+
61+
# 密码输入框
62+
self.wait.until(
63+
ec.presence_of_element_located((By.ID, 'login-passwd'))
64+
).send_keys(self.password)
65+
# 清空密码输入框
66+
67+
# 登录按钮
68+
login_button = self.wait.until(
69+
ec.presence_of_element_located((By.XPATH, '//*[@id="geetest-wrap"]/div/div[5]/a[1]'))
70+
)
71+
# 点击登录
72+
login_button.click()
73+
# 防止因网络波动,图片加载过慢,等待加载出来
74+
time.sleep(2)
75+
76+
def get_geetest_image(self):
77+
"""
78+
获取极验验证码图片
79+
80+
"""
81+
image_name = ["geetest_canvas_fullbg", "geetest_canvas_bg"]
82+
image = []
83+
for index in range(0, 2):
84+
# 执行js 拿到canvas画布里面的图片数据
85+
js = f'return document.getElementsByClassName("{image_name[index]}")[0].toDataURL("image/png");'
86+
# 图片数据
87+
complete_img_data = self.browser.execute_script(js)
88+
# base64 编码的图片信息
89+
complete_img_base64 = complete_img_data.split(',')[1]
90+
# 转成bytes类型
91+
complete_img = base64.b64decode(complete_img_base64)
92+
# 加载图片 return 回去对比
93+
image_c = Image.open(BytesIO(complete_img))
94+
image_c.save(f'image{index + 1}.png')
95+
image.append(image_c)
96+
97+
return image
98+
99+
def is_pixel_similar(self, image1, image2, x, y):
100+
"""比较两张图片的像素点
101+
102+
注意:像素点比较是有偏差的,需要允许一定范围的误差,我们可以设置一个阈值
103+
104+
"""
105+
# 获取两张图片执行位置的像素点
106+
c_pixel = image1.load()[x, y]
107+
ic_pixel = image2.load()[x, y]
108+
# 阈值 允许误差
109+
threshold = 10
110+
# 对比
111+
if abs(c_pixel[0] - ic_pixel[0]) < threshold and \
112+
abs(c_pixel[1] - ic_pixel[1]) < threshold and \
113+
abs(c_pixel[2] - ic_pixel[2]) < threshold:
114+
return True
115+
return False
116+
117+
def get_slice_gap(self, image1, image2):
118+
"""获取缺口的偏移量
119+
120+
通过比较两张图片的所有像素点, 获取两张图片是从哪里开始不同
121+
从而得到 移动块 要在 x 方向移动的距离
122+
123+
返回 缺口的偏移量
124+
125+
Parameters
126+
----------
127+
image1: Image instance
128+
完整的图片
129+
130+
image2: Image instance
131+
有缺失的图片
132+
133+
Returns
134+
---------
135+
int
136+
"""
137+
# image2.size:['width', 'height']
138+
for x in range(image1.size[0]):
139+
for y in range(image1.size[1]):
140+
if not self.is_pixel_similar(image1, image2, x, y):
141+
# 移动块只在水平方向移动 只需返回 x
142+
return x
143+
144+
def get_track(self, distance):
145+
"""根据偏移量获取移动轨迹
146+
147+
返回 移动轨迹
148+
149+
Parameters
150+
----------
151+
distance: int
152+
偏移量
153+
154+
Returns
155+
---------
156+
int
157+
"""
158+
# 移动轨迹
159+
track = []
160+
# 当前位移
161+
current = 0
162+
# 减速阈值
163+
164+
mid = distance * 4 / 5
165+
# 计算间隔
166+
t = 0.2
167+
# 初速度
168+
v = 0
169+
170+
while current < distance:
171+
if current < mid:
172+
# 加速度为正2
173+
a = 20
174+
else:
175+
# 加速度为负3
176+
a = -30
177+
# 初速度v0
178+
v0 = v
179+
# 当前速度v = v0 + at
180+
v = v0 + a * t
181+
# 移动距离x = v0t + 1/2 * a * t^2
182+
move = v0 * t + 1 / 2 * a * t * t
183+
# 当前位移
184+
current += move
185+
# 加入轨迹
186+
track.append(round(move))
187+
return track
188+
189+
def move_to_gap(self, slider, tracks):
190+
"""模拟人工将滑块到缺口处
191+
192+
Parameters
193+
----------
194+
slider: Any
195+
滑块
196+
197+
tracks: Any
198+
轨迹
199+
200+
"""
201+
ActionChains(self.browser).click_and_hold(slider).perform()
202+
for x in tracks:
203+
ActionChains(self.browser).move_by_offset(xoffset=x, yoffset=0).perform()
204+
time.sleep(random.random())
205+
ActionChains(self.browser).release().perform()
206+
207+
def get_geetest_button(self):
208+
"""获取初始验证按钮
209+
"""
210+
return self.wait.until(ec.element_to_be_clickable((By.CLASS_NAME, 'geetest_slider_button')))
211+
212+
def login_success(self):
213+
"""判定是否登录成功
214+
215+
响应一个布尔值
216+
217+
Returns
218+
----------
219+
bool
220+
"""
221+
try:
222+
# 登录成功后 界面上会有一个消息按钮
223+
return bool(
224+
WebDriverWait(self.browser, 5).until(ec.presence_of_element_located(
225+
(By.XPATH, '//a[@href="//message.bilibili.com/new"]'))
226+
)
227+
)
228+
except Exception as exc:
229+
print(exc)
230+
return False
231+
232+
def login(self):
233+
"""开始
234+
235+
"""
236+
237+
# 打开浏览器, 输入账号 密码, 点击登陆
238+
self.open()
239+
# 获取验证图 image2(有缺失的验证图) image1(完整的验证图)
240+
image1, image2 = self.get_geetest_image()
241+
242+
# 获取缺口的偏移量
243+
gap = self.get_slice_gap(image1, image2)
244+
245+
print(f'缺口的偏移量为:{gap}')
246+
# 拖动滑块 有误差-8
247+
track = self.get_track(gap - 12)
248+
slider = self.get_geetest_button()
249+
self.move_to_gap(slider, track)
250+
time.sleep(3)
251+
252+
if self.login_success():
253+
print('登陆成功,获取 cookie 成功 ~ ')
254+
cookies = {cookie["name"]: cookie["value"] for cookie in self.browser.get_cookies()}
255+
print(cookies)
256+
else:
257+
self.login()
258+
259+
260+
if __name__ == '__main__':
261+
account = input("请输入B站账号 >>>")
262+
pwd = input("请输入B站密码 >>>")
263+
264+
instance = LoginBli(account, pwd)
265+
instance.login()

0 commit comments

Comments
 (0)