考慮到B站移動(dòng)滑塊登陸比較難而且成功率一般
故而采用了簡(jiǎn)單的二維碼登陸
至少常用的PC+手機(jī)的網(wǎng)站都支持二維碼登錄
一劳翰、登陸流程
先用瀏覽器F12找到和登錄相關(guān)的url
1敦锌、判斷用戶是否已經(jīng)登錄了
Get https://account.bilibili.com/home/userInfo
已登錄則響應(yīng) code=0以及個(gè)人信息
未登錄則響應(yīng) code=-101
2、獲取二維碼地址
Get https://passport.bilibili.com/qrcode/getLoginUrl
這個(gè)地址有兩個(gè)作用
一個(gè)是返回cookie值sid
另一個(gè)是返回的url就是二維碼的內(nèi)容
需要利用Python的qrcode庫(kù)制作成二維碼
3佳簸、根據(jù)地址生成二維碼并顯示
需要安裝庫(kù)qrcode和matplotlib
pip install qrcode
pip install matplotlib
4乙墙、 循環(huán)判斷是否掃碼
Post https://passport.bilibili.com/qrcode/getLoginInfo!
data={
'oauthKey':oauthKey,
'gourl': 'https://passport.bilibili.com/account/security'
}
共有三種響應(yīng)
未掃碼時(shí)返回
{'status': False, 'data': -4, 'message': "Can't scan~"}
已掃碼時(shí)返回
{'status': True,'ts': 1538296048,'data': {'url': ...''}}
以及登錄cookies
二維碼過(guò)期時(shí)返回
{'status': False, 'data': -2, 'message': "Can't Match oauthKey~"}
5生均、獲取個(gè)人信息
主要是獲取uid
小電視抽獎(jiǎng)和連接直播彈幕時(shí)會(huì)用到
Get https://api.live.bilibili.com/User/getUserInfo
代碼實(shí)現(xiàn)
二听想、API層 api.py
- API應(yīng)滿足單例模式
只能實(shí)例化一次,返回的都是同一個(gè)對(duì)象 - ajax 負(fù)責(zé)管理所有請(qǐng)求
可以用于添加日志 - 添加Login類負(fù)責(zé)登錄
確定請(qǐng)求方法马胧、請(qǐng)求地址汉买、請(qǐng)求參數(shù) - 添加統(tǒng)一接口類BilibiliAPI
作為唯一的暴露給服務(wù)層的接口
from utils import singleton
def ajax(s,url,method='GET',data=None):
if method=='GET':return http.get(s,url,data)
else:return http.post(s,url,data)
class Login():
def isLogin(self,s):
url="https://account.bilibili.com/home/userInfo"
return ajax(s,url)
def get_vdcode(self,s):
url="https://passport.bilibili.com/qrcode/getLoginUrl"
return ajax(s,url)
def loop_vdcode(self,s,oauthKey):
url="https://passport.bilibili.com/qrcode/getLoginInfo"
data={
'oauthKey':oauthKey,
'gourl': 'https://passport.bilibili.com/account/security'
}
return ajax(s,url,'POST',data)
@singleton
class BilibiliAPI():pass
BilibiliAPI.Login=Login()
三、服務(wù)層 server.py
設(shè)置響應(yīng)類型
負(fù)責(zé)調(diào)用api層
決定api層響應(yīng)結(jié)果的邏輯
可以給主程序提供完整的登錄服務(wù)
from api import BilibiliAPI as API
class Login():
def __init__(self,s):
self.s=s
self.oauthKey=''
self.s._type='json'
def isLogin(self):
r=API.Login.isLogin(self.s)
if r['code']==-101:return False
else:return self.get_info()
def get_info(self):
r=API.User.get_info(s)
self.info=r['data']
return True
def get_vdcode(self):
r=API.Login.get_vdcode(self.s)
code_url=r['data']['url']
img=self.make_vdcode(code_url)
self.show_img(img)
self.oauthKey=r['data']['oauthKey']
def show_img(self,img):
import matplotlib.pyplot as plt
plt.imshow(img)
plt.show()
def make_vdcode(self,code_url):
import qrcode
return qrcode.make(code_url)
def loop_vdcode(self):
import time
r=API.Login.loop_vdcode(self.s,self.oauthKey)
while not r['status']:
time.sleep(1)
r=API.Login.loop_vdcode(self.s,self.oauthKey)
if r['data']==-2:
print('二維碼已過(guò)期')
break
if r['status']:self.info=r['data']
return r['status']
四佩脊、測(cè)試代碼
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0',
'Accept': 'application/json, text/plain, */*',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Accept-Encoding': 'gzip, deflate, br',
'Referer': 'https://live.bilibili.com/',
'Origin': 'https://live.bilibili.com',
'Connection': 'keep-alive'
}
s=session(headers,'cookie.txt')
login=Login(s)
while not login.isLogin():
login.get_vdcode()
login.loop_vdcode()
s.save()
保存后的cookie.txt
#LWP-Cookies-2.0
Set-Cookie3: DedeUserID=269938304; path="/"; domain=".bilibili.com"; path_spec; domain_dot; expires="2018-10-29 12:11:55Z"; version=0
Set-Cookie3: DedeUserID__ckMd5=e3b81ec041161182; path="/"; domain=".bilibili.com"; path_spec; domain_dot; expires="2018-10-29 12:11:55Z"; version=0
Set-Cookie3: SESSDATA="136e3f28%2C1540815115%2C4df7c156"; path="/"; domain=".bilibili.com"; path_spec; domain_dot; expires="2018-10-29 12:11:55Z"; HttpOnly=None; version=0
Set-Cookie3: bili_jct=957f743c8c8ad984fc6d260f43963242; path="/"; domain=".bilibili.com"; path_spec; domain_dot; expires="2018-10-29 12:11:55Z"; version=0
Set-Cookie3: sid=c5g6gj6d; path="/"; domain=".bilibili.com"; path_spec; domain_dot; expires="2019-09-29 12:12:11Z"; version=0