以爲有幫助的別忘了關注一下知識圖譜與大數據公衆號javascript
在上一文中,咱們保存了百度雲盤的地址和提取碼,可是這種分享連接很容易被屏蔽,最好的作法就是保存資源到本身的網盤,不過採集的連接有上萬個,人肉保存並不現實,因此本文嘗試了批量保存資源,如您還沒看過上文,這裏能夠跳轉。
爬蟲學習3:搭建本身的電影資源網保姆式教學html
如下面資源連接爲例:java
https://pan.baidu.com/s/1tHSxZQueF-Wsa2T0NlT3vQ
在瀏覽器中輸入以上連接,會自動跳轉到https://pan.baidu.com/share/init?surl=tHSxZQueF-Wsa2T0NlT3vQ
,
輸入正確提取碼後發現直接跳轉到資源保存頁面了,F12 NETWORK裏也看不到此請求的返回值,這時候只能使用Fiddler才能抓到包了。python
打開Fiddler,爲了避免讓其它各種請求影響到咱們,首先進行簡單設置,以此來顯示咱們想要的請求,點擊Filters 進行以下設置,最後點擊Actions裏的Run Filterset now,就只會顯示pan.baidu.com
域名的請求:
web
爲了獲得點擊提取文件按鈕時發送的請求和post的數據,先嚐試輸入一個錯誤的提取碼123,查看請求:
正則表達式
https://pan.baidu.com/share/verify?surl=tHSxZQueF-Wsa2T0NlT3vQ&t=1593142082616&channel=chunlei&web=1&app_id=250528&bdstoken=855345cbf66bbfba3d30d5e201ea346f&logid=MTU5MzE0MjA4MjYyMDAuNTM5MjA2Nzg2ODEyNTcxNA==&clienttype=0
看來上面這個url就提取數據的請求,接下來具體看一下都提交了哪些數據:
Form Data中的pwd就是剛纔輸入的錯誤提取碼,而其它兩個參數vcode和vcode_str在沒有驗證碼的狀況下也不用管,再看看Query String中的參數,爲了測試這些參數有哪些是變化的,我挑選了一些資源連接去嘗試,具體過程就省略了,總結以下:算法
Query String參數 | 是否變化 |
---|---|
surl | 即資源連接中的最後部分,可直接獲取 |
t | 時間戳,可直接獲取 |
channel | 固定 |
web | 固定 |
app_id | 固定 |
bdstoken | 固定 |
logid | 變化 |
clienttype | 固定 |
Form Data參數 | 是否變化 |
---|---|
pwd | 即提取碼 |
vcode | 無 |
vcode_str | 無 |
從上面表格來看,須要手動獲取的就是logid,稍微有點經驗的話應該都能想到這會不會是js動態生成的,抱着這個心態來到sources標籤下,搜索logid關鍵字,果不其然,有個JS文件裏有這個參數,直接定位到相應的行數:
直接在68行打一個斷點,查看運行狀態:
細心的同窗一個很快就能發現上圖紅框裏的值就是第一次請求https://pan.baidu.com/share/init?surl=tHSxZQueF-Wsa2T0NlT3vQ
cookies裏的BAIDUID
的值,logid
就是經過上面一些js代碼中的一些算法獲得的,cookies以下:django
BIDUPSID=EC39F255CF7B146E8ADD4FA37DB16739;BAIDUID=BBBC2B2C1269AF3AA7D113D07FAC5E80:FG=1; PSTM=1587390447; PANWEB=1; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; BDCLND=C4jsJ4aHacfrqq02TYbUOKGFFDefnJAMNmU%2BI3v5FNM%3D;
水平有限,沒辦法將這段js改寫爲python,不過好在python提供了一個執行js代碼的庫pyexecjs
,該庫運行於Nodejs環境,首先要保證你的機器安裝了Nodejs:json
pip install pyexecjs
下圖即表明安裝正確了,execjs能夠正常使用
將js代碼稍微修改一下,保存爲yunpan.js
:瀏覽器
var u = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/~!@#¥%……&" , l = String.fromCharCode , d = function (e) { if (e.length < 2) { var n = e.charCodeAt(0); return 128 > n ? e : 2048 > n ? l(192 | n >>> 6) + l(128 | 63 & n) : l(224 | n >>> 12 & 15) + l(128 | n >>> 6 & 63) + l(128 | 63 & n) } var n = 65536 + 1024 * (e.charCodeAt(0) - 55296) + (e.charCodeAt(1) - 56320); return l(240 | n >>> 18 & 7) + l(128 | n >>> 12 & 63) + l(128 | n >>> 6 & 63) + l(128 | 63 & n) } , f = /[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g , g = function (e) { return (e + "" + Math.random()).replace(f, d) } , h = function (e) { var n = [0, 2, 1][e.length % 3] , t = e.charCodeAt(0) << 16 | (e.length > 1 ? e.charCodeAt(1) : 0) << 8 | (e.length > 2 ? e.charCodeAt(2) : 0) , o = [u.charAt(t >>> 18), u.charAt(t >>> 12 & 63), n >= 2 ? "=" : u.charAt(t >>> 6 & 63), n >= 1 ? "=" : u.charAt(63 & t)]; return o.join("") } , m = function (e) { return e.replace(/[\s\S]{1,3}/g, h) } , p = function () { return m(g((new Date).getTime())) } , w = function (e, n) { return n ? p(String(e)).replace(/[+\/]/g, function (e) { return "+" == e ? "-" : "_" }).replace(/=/g, "") : p(String(e)) }; #獲取logid函數,本身添加的 function getlogid(e) { var logid = w(e) return logid }
使用execjs執行一下
def get_logid(self, bid): with open('..//js//yunpan.js', encoding='utf-8') as f: yunpan = f.read() js = execjs.compile(yunpan) logid = js.call('getlogid', bid) return logid
獲得全部的變化參數後就能夠提交數據了:
requests.packages.urllib3.disable_warnings() class YunPan(SpiderBase): def __init__(self): super(YunPan, self).__init__() self.index_url = "https://pan.baidu.com/s/1wy0LC4O6iY7l9M6RD25k6w" #提交提取碼的連接 self.submmit_url = "https://pan.baidu.com/share/verify?surl={}&t={}&channel=chunlei&web=1&app_id=250528&bdstoken=7a8e1e34b454fd27de65b7662f67c2fa&logid={}==&clienttype=0" #保存連接 self.save_url = "https://pan.baidu.com/share/transfer?shareid={}&from={}&ondup=newcopy&async=1&channel=chunlei&web=1&app_id=250528&bdstoken=7a8e1e34b454fd27de65b7662f67c2fa&logid={}&clienttype=0" self.pan_code = "Love" def get_logid(self, bid): with open('..//js//yunpan.js', encoding='utf-8') as f: yunpan = f.read() js = execjs.compile(yunpan) logid = js.call('getlogid', bid) return logid def init(self): ua = random.choice(self.ua) header = { "User-Agent": ua } resp = self.download_page(self.index_url, headers=header, verify=False) resp.encoding = resp.apparent_encoding bid = resp.cookies.get("BAIDUID", "") logid = self.get_logid(bid) key = self.index_url.split("surl=")[-1] key = self.index_url.split("/")[-1][1:] return bid, logid, key def post_pan_code(self): ua = random.choice(self.ua) # 跳轉 data = { "pwd": "Love", "vscode": "", "vscode_str": "", } bid, logid, key = self.init() url = self.submmit_url.format(key, timestep, logid, ) resp = self.download_page(url, method="post", headers=self.headers(), data=data, verify=False) res = json.loads(resp.text) print(res) return res
經過Fiddler抓包發現返回的res爲下,errno爲0即表明提交成功:
{"errno":0,"err_msg":"","request_id":8738382064533520558,"randsk":"g2VwUSYs1KSuOMh9%2FQDVUUwc7ICFq4CZNmU%2BI3v5FNM%3D"}
留意上面的randsk
。
若是你是用瀏覽器抓包就會發現輸對提取碼點擊按鈕後請求會所有刷新一次,就不能確認究竟是哪一個請求跳轉到資源頁面的,只能使用Fiddler才能抓到POST
成功後的請求,通過測試,發現POST數據後又再次請求了https://pan.baidu.com/s/1wy0LC4O6iY7l9M6RD25k6w
這個連接,沒錯,就是上面一開始請求的連接,區別就是此次請求cookie中攜帶了剛纔返回的randsk
的值。因此再次請求時添加上randsk
:
res = self.post_pan_code() randsk = res.get("randsk", "") c = requests.cookies.RequestsCookieJar() c.set("BDCLND", randsk) self.session.cookies.update(c) #之因此加 verify=False是由於https請求有時候會報OPEN SSL的異常錯誤 #最好在導庫時加上requests.packages.urllib3.disable_warnings() resp = self.download_page(self.index_url, headers=headers, verify=False) #保證沒有亂碼 resp.encoding = resp.apparent_encoding #這時候resp裏已經時資源頁面的內容了
抓包保存資源連接發現爲:
#保存連接 https://pan.baidu.com/share/transfer?shareid=4180912663&from=2693937402&ondup=newcopy&async=1&channel=chunlei&web=1&app_id=250528&bdstoken=7a8e1e34b454fd27de65b7662f67c2fa&logid=MTU5MzE4MTYzNDY0NDAuOTE1MzE0NDI5MzI1NTY4OA==&clienttype=0"
Query String
和Form Data
爲:
Query String大部分參數都提到過,能夠參考上面,這裏出現了新的兩個參數shareid
和from
,通過測試除了這兩個參數和上文中logid
,其它參數均爲固定。
Form Data參數 | 是否變化 |
---|---|
fsidlist | 變化 |
path | 你本身選擇保存的路徑,約等於固定 |
type | 固定 |
到這裏又稍微被shareid
和from
、fsidlist
三個參數卡住了,在頁面中搜索這三個參數也沒有結果,在source中搜索了一下,卻是又一個shareid,可是貌似也關係不大,折騰了一會就想到既然參數名搜不到,那我搜一下參數值試試?,果真在資源頁面中搜到了三個參數的值(即https://pan.baidu.com/s/1wy0LC4O6iY7l9M6RD25k6w
這頁面),以下圖:
正則表達式便可提取出三個值。
def extract_data(self, html): #提取三個值 import re share_id_pattern = 'yunData.SHARE_ID = "(.*?)";' from_pattern = 'yunData.SHARE_UK = "(.*?)";' fsid_pattern = 'yunData.FS_ID = "(.*?)";' try: share_id = re.findall(share_id_pattern, html, re.S)[0] _from = re.findall(from_pattern, html, re.S)[0] fsid = re.findall(fsid_pattern, html, re.S)[0] except IndexError: print("提取shareid、from、fsid失敗") return share_id, _from, fsid
轉存時候注意cookie裏要攜帶BDUSS
和STOKEN
,這兩個參數在資源頁面的cookies裏,且都爲固定的,沒有登陸的話只有STOKEN
,BDUSS
的值要在登陸狀態下才能看到,下圖時登陸後的cookie:
代碼以下:
def transfer_resource(self,share_id, from_id, fsid,logid,randmsk): url = self.save_url.format(share_id,from_id,logid) data = { "fsidlist":"["+fsid+"]", "path":"/個人資源", "type":'1' } BDUSS="你的cookies裏的BDUSS" self.session.cookies.set("STOKEN","STOKEN",domain=".baidu.com") self.session.cookies.set("BDUSS",BDUSS,domain=".baidu.com") resp = self.download_page(url,method="post",headers=self.headers(),data=data,verify=False) #這裏返回resp errno爲0即表明成功
到這裏就結束了,本文講解了如何使用python轉存百度雲盤資源。
更多內容請關注知識圖譜與大數據公衆號,獲取更多內容,固然不關注也無所謂。