一晚上功夫爬取了海量資源連結,卻不知如何自動儲存到網盤,一袋煙後我頓悟了

羅小黑的黑發表於2020-09-23

覺得有幫助的別忘了關注一下知識圖譜與大資料公眾號,完整程式碼移步從今天開始種樹

開始

在上一文中,我們儲存了百度雲盤的地址和提取碼,但是這種分享連結很容易被遮蔽,最好的做法就是儲存資源到自己的網盤,不過採集的連結有上萬個,人肉儲存並不現實,所以本文嘗試了批量儲存資源,如您還沒看過上文,這裡可以跳轉。
爬蟲學習3:搭建自己的電影資源網保姆式教學

觀察請求

以下面資源連結為例:

https://pan.baidu.com/s/1tHSxZQueF-Wsa2T0NlT3vQ

在瀏覽器中輸入以上鍊接,會自動跳轉到https://pan.baidu.com/share/init?surl=tHSxZQueF-Wsa2T0NlT3vQ
在這裡插入圖片描述
輸入正確提取碼後發現直接跳轉到資源儲存頁面了,F12 NETWORK裡也看不到此請求的返回值,這時候只能使用Fiddler才能抓到包了。

Fiddler抓包

開啟Fiddler,為了不讓其它各類請求影響到我們,首先進行簡單設定,以此來顯示我們想要的請求,點選Filters 進行如下設定,最後點選Actions裡的Run Filterset now,就只會顯示pan.baidu.com域名的請求:
在這裡插入圖片描述

測試post資料

為了得到點選提取檔案按鈕時傳送的請求和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如下:

BIDUPSID=EC39F255CF7B146E8ADD4FA37DB16739;BAIDUID=BBBC2B2C1269AF3AA7D113D07FAC5E80:FG=1;  PSTM=1587390447; PANWEB=1; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; BDCLND=C4jsJ4aHacfrqq02TYbUOKGFFDefnJAMNmU%2BI3v5FNM%3D;

水平有限,沒辦法將這段js改寫為python,不過好在python提供了一個執行js程式碼的庫pyexecjs,該庫執行於Nodejs環境,首先要保證你的機器安裝了Nodejs:

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 StringForm Data為:
在這裡插入圖片描述
Query String大部分引數都提到過,可以參考上面,這裡出現了新的兩個引數shareidfrom,經過測試除了這兩個引數和上文中logid,其它引數均為固定。

Form Data引數 是否變化
fsidlist 變化
path 你自己選擇儲存的路徑,約等於固定
type 固定

到這裡又稍微被shareidfromfsidlist三個引數卡住了,在頁面中搜尋這三個引數也沒有結果,在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裡要攜帶BDUSSSTOKEN,這兩個引數在資源頁面的cookies裡,且都為固定的,沒有登入的話只有STOKENBDUSS的值要在登入狀態下才能看到,下圖時登入後的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轉存百度雲盤資源。
更多內容請移步從今天開始種樹,關注知識圖譜與大資料公眾號,獲取更多內容,當然不關注也無所謂。
在這裡插入圖片描述
在這裡插入圖片描述

相關文章