Python_小林的爬取QQ空間相冊圖片鏈接程序


前言

昨天看見某人的空間有上傳了XXXX個頭像,然后我就想着下載回來【所以本質上這是一個頭像下載程序】,但是一個個另存為太浪費時間了,上網搜索有沒有現成的工具,居然要注冊碼,還賣45一套。你們的良心也太壞了!。而且居然!!!還有一個和我同名的下載器?這一點實在讓我十分的嫉恨。於是我下決心搞清楚這個東西,然而大部分資料都是Python源碼,並且很多都是過期的且無法使用的了[反正我是沒找到一個能用]。

好在有很多人在網上發過一些教程,雖然Python是第一次接觸[差不多一天的時間],但它是實在讓我驚訝。這個語言太簡單,太簡潔了!你很容易就能明白。就這樣從這些斷斷續續的代碼和教程中,一邊了解語法,一邊找代碼,調試了一天。寫出了這個程序。

1.准備需要的東西

  python3.0,下面這些引用的模塊

 

  import sys
  import re
  import os  
  import requests
  import execjs
  import time
  from goto import with_goto #添加於19.8.6  https://github.com/snoack/python-goto

  以及:

  一個能夠登錄並開通了QQ空間的QQ賬號和密碼。

  要爬取空間相冊對象的QQ賬號。

  一個能成功登錄QQ空間,並且帶有pskey參數的Cookies。

1.2 抓取Cookies

  一開始我也是看着教程去找的,最后發現,Chrome[版本 76.0.3809.87(正式版本) (64 位)]根本就抓不到。接下來我還是得用一個經典的工具。

步驟:打開fiddler ,打開瀏覽器,訪問登錄你的賬號,在攔截的列表中找到右側欄中Cookies里帶pskey參數,右鍵菜單選擇查看標頭將它復制,替換py文件中的Cookies參數。

  

(圖為fiddler的攔截信息)

 

 (圖為查看標頭對話框)

2.0 替換代碼 [在新的腳本已放棄。]

 

    設置准備登錄的QQ號碼【與Cookies對應】

       設置將准備的爬取對象QQ號碼

    設置好抓取的cookies

    設置好time_sleep的時間【安全性考慮[過快會導致遠程主機中斷連接,然后出些奇怪的異常。]】

    在新的腳本里,你將通過Input輸入需要的數據來得到參數。

2.1 執行代碼

  在安裝完需求模塊與准備好一切之后,將它保存,在PowerShell or cmd命令行中啟動。 程序在爬取完相冊鏈接后會執行Input,

填入指定目錄,將輸出鏈接.txt文件到該目錄。【這時的文本文件里是未轉義的JS代碼[這正是我要的。],你可以寫一個轉義工具將  \/ 手動轉義,例如下面給出的代碼:】

'\Code for vb6
'\e-mail: 1919988942@qq.com
Dim url() As String

Public Function url_format(stra As String) As String
'由於可能需要轉義的url代碼行超出Integr(32...)級別,啟用long更穩妥
Dim strb() As String
Dim i As Long
url_format = ""
strb = Split(stra, "\")
For i = 0 To UBound(strb)
If strb(i) <> "/" Then
url_format = url_format + strb(i)
End If
Next
End Function

Public Sub main()
Dim i As Long
Open App.Path & "\ling_url.txt" For Input As #1
Do Until EOF(1)
ReDim Preserve url(i)
Line Input #1, url(i)
url(i) = url_format(url(i))
i = i + 1
Loop
Close #1
Open App.Path & "\ling_Val_url.txt" For Output As #2
For i = 0 To UBound(url())
Print #2, url(i)
Next
Close #2
End Sub

   執行代碼結果(示例):

 

 

(2019/8/6 10:40更新。)

3.代碼分析:

 (2019/8/7.2:00 先睡了。)

 (2019/8/6 14:30。)

 

 

#Python : 小林的QQ空間相冊下載鏈接爬取腳本
# -*- coding: utf-8 -*-
import sys
import re
import os
import requests
import execjs
import time
from goto import with_goto #添加於19.8.6

#cookie 需要有p_skey參數
global cookie  #pgv_pvi=7315500032; pgv_pvid=288873475; ptui_loginuin=1919988942; RK=YoxQKU3NQ7; ptcz=5d6856f2fff94194ae900a4ee204a2528654c81e39c9c812977ab125cfcaecdf; qz_screen=1920x1080; QZ_FE_WEBP_SUPPORT=1; __Q_w_s__QZN_TodoMsgCnt=1; _tucao_session=ZEVTWlB3S1JWR0VkQjgzVTM5YUQzMzFwYUJRNm5xVk5GQUpWUStPNDNRWW9uQ0JFNlp2bkdjRWFqZ2pVVUhIU05VUGJ5QUpRNCtIR1FUSEhtSCt2M3luanFabTlua05LWmUxR2RJMWlMQlE9--2TItPIzlBB2dEb4ZW7UPXQ%3D%3D; pgv_si=s1334556672; _qpsvr_localtk=0.4684345944820856; ptisp=ctc; pgv_info=ssid=s6272865000; 1919988942_todaycount=0; 1919988942_totalcount=3081; cpu_performance_v8=5; uin=o1919988942; skey=@EgVMVB3lC; p_uin=o1919988942; pt4_token=RsZjbes01h79ehRaXRxZVG3VFtky4*P2udV4tx51XiY_; p_skey=1H*iJGgv*7-Gg-O5reZ7ZtixgWkziuu0a596lUmeU1I_; x-stgw-ssl-info=2d4aa1681cad595b93c934eab2ed891a|0.166|1565046557.661|5|r|I|TLSv1.2|ECDHE-RSA-AES128-GCM-SHA256|57000|N|0"
global uin   #1919988942"
global fuin   #2757145758"
global time_sleep   #2 #安全性考慮
fail='對不起,您尚未登錄或者登錄超時。'
#返回合法的文件夾名稱(如果你使用的是除中文外定義的名稱,請手動修改正則表達式。)
def mkName_Legalization(Strname):
    strvalue = Strname
    strvalue = re.sub(r'[^a-zA-Z0-9(\u4e00-\u9fa5)]+',' ',strvalue)#僅保留數字A-Za-z中文字符
    return strvalue.strip()#去掉頭尾空格
#返回Http消息
def getHttp(url,cookie):
    Goyes= True
    while Goyes :
        try:
            headers = {'Referer': 'https://qzs.qq.com/qzone/photo/v7/page/photo.html?init=photo.v7/module/albumList/index&navBar=1',
                    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0 Iceweasel/38.3.0',
                    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                    'cookie':cookie
            }
            s = requests.session()
            Goyes = False
            return s.get(url, headers=headers).content
        except requests.exceptions.Timeout:
            print('訪問超時,將自動重連。')
        except requests.exceptions.ConnectionError:
            print('連接錯誤,將自動重連。')
#返回列表所有字符串
def getlist(urllist):
    strA = ""
    for i in urllist:
        strA = strA + str(i) + "\r\n"
    return strA

@with_goto
def main():
    print("歡迎使用小林的QQ相冊爬取腳本,小林的第一個Python作品!\r\n")
    uin = input("請填寫登錄QQ賬號. \r\n")
    fuin = input("請填寫爬取對象的QQ賬號. \r\n")
    cookie = input("請填寫帶pskey的Cookies. \r\n")
    fileName = input("請填寫導出目錄地址...\r\n")
    time_sleep = float(input("請填寫請求間隔時間,單位為秒,可輸入小數,例如0.1,0.5. \r\n"))
    print("開始模擬登錄。")
    t = execjs.compile("function time(){return String(Math.random().toFixed(16)).slice(-9).replace(/^0/, '9')}").call(
        'time')
    gtk_t = 'function a(skey){var hash = 5381;for (var i = 0, len = skey.length;i < len;++i) {hash += (hash << 5) + skey.charCodeAt(i);}return hash & 2147483647;}'
    qq = cookie.split("p_skey=")[1].split(";")[0]
    gtk = execjs.compile(gtk_t).call('a', qq)  # .replace("p_skey=(.*?);"))
    label .getrepeat
    json_qq = getHttp(
        "https://h5.qzone.qq.com/proxy/domain/photo.qzone.qq.com/fcgi-bin/fcg_list_album_v3?g_tk={0}&callback=shine0_Callback&t={1}&hostUin={2}&uin={3}&appid=4&inCharset=utf-8&outCharset=utf-8&source=qzone&plat=qzone&format=jsonp&notice=0&filter=1&handset=4&pageNumModeSort=40&pageNumModeClass=15&needUserInfo=1&idcNum=4&callbackFun=shine0&_=1516544656243".format(
            gtk, t, fuin, uin),cookie)
    if (json_qq == None):
        goto .getrepeat
    json_qq = str(json_qq,encoding='utf-8')
    if (json_qq != ""):
        state = re.findall(r'"message":"(.*?)",', json_qq)
        #登陸失敗
        if (state[0] != fail):
            print("登錄成功,開始爬取。")
            json_text = json_qq.replace("shine0_Callback(", "").replace(");", "")
            url_id = re.findall(r'"id" : "(.*?)",', json_text) #獲取相冊列表
            url_str = ""
            url_name = re.findall(r'"name" : "(.*?)",', json_text) #獲取相冊名稱
            total = re.findall(r'"total" : (.*?),', json_text) #獲取相冊照片數量
            url_Index = 0   #當前頁數
            url_Count = 0
            url_text = [] #JSON數據
            #為了安全性我選擇了導出鏈接和下載鏈接兩步分開 [也就是這部分不包括下載]
            print("開始申請相冊信息。[固定延遲:"+str(time_sleep)+"]")
            url_Count = 0#
            url_Start =[] #相冊列表中的起點
            url_Over = [] #相冊列表中的終點
            for x in url_id: 
                # 由於限制每次最多只能申請500張
                #range(0,toatl,501) =》 for(i= 0; i<=total;i+=501)
                print("開始第"+ str(url_Index + 1) + "個相冊的爬取,")
                print('name:' + url_name[url_Index])
                url_Start.append(url_Count)#記錄開始的列表索引
                url_forcount = int(int(total[url_Index]) / 500) + 1 #計算要多少次才能遍歷這個相冊
                for i in range(0, url_forcount, 1): 
                    text = "" #防止接收失敗無法驗證
                    label .begin
                    url_q = "https://h5.qzone.qq.com/proxy/domain/photo.qzone.qq.com/fcgi-bin/cgi_list_photo?g_tk={0}&callback=shine0_Callback&t={1}&mode=0&idcNum=4&hostUin={2}&topicId={4}&noTopic=0&uin={3}&pageStart={6}&pageNum={5}&skipCmtCount=0&singleurl=1&batchId=&notice=0&appid=4&inCharset=utf-8&outCharset=utf-8&source=qzone&plat=qzone&outstyle=json&format=jsonp&json_esc=1&question=&answer=&callbackFun=shine0&_=1516549331973".format(
                        gtk, t, fuin, uin, x,500,i*500+1)  # g_tk,t,fuin,uin,url_id,pagesnum,pagesstart
                    text = getHttp(url_q,cookie)
                    if (text == None):
                        goto .begin
                    state = str(text,encoding='utf-8') #驗證狀態
                    if(state == ''):
                        print("返回異常。將自動重復該序號的循環。")
                        goto .begin
                    time.sleep(time_sleep)
                    url_text.append(text) #獲取返回到列表
                    url_Count = url_Count + 1
                    print("當前進度:" + str(i+1) + "/" + str(url_forcount))
                print("\r\n" + '完成了第'+ str(url_Index + 1) + '個相冊.') 
                url_Index = url_Index + 1 # 自增 循壞繼續
                url_Over.append(url_Count)#記錄結束的列表索引
            print("QQzone所有在線記錄操作結束。")
            print("開始導出鏈接...")
            #x = url_Getpaget[0] to url_GetPage[Ubound(me)] x=>第x + 1個相冊
            url_Index = 0
            for x in range(0,len(total),1):
                #mkNameL... ->返回合法的文件夾名稱(如果你使用的是除中文外定義的名稱,請手動修改正則表達式。)
                url_str = "" #刷新
                path = fileName + '\\qzone' + '\\' + mkName_Legalization(url_name[x]) + '\\'#輸出路徑
                mkdir(path)#創建目標
                f = open( path + mkName_Legalization(url_name[x]) + '_download.txt', 'wb+')#打開/創建
                url_str = url_str + "<小林的分隔符——————" + mkName_Legalization(url_name[x]) + "——————Start>"+"\r\n" #添加相冊記載開始信息
                for i in range(url_Start[x],url_Over[x],1):
                    url_Download = re.findall(r'"url" : "(.*?)",', url_text[i].decode('utf-8'))#返回真實下載地址
                    url_str = url_str + getlist(url_Download) #添加該相冊所有真實鏈接地址
                print("\r\n"+ '完成了第'+str(x + 1)+'個相冊的下載信息。')
                url_str = url_str + "<小林的分隔符——————" + mkName_Legalization(url_name[x]) + "——————End>"+"\r\n" #添加相冊記載結束信息
                f.write(bytes(url_str,'UTF-8'))
                f.close
            print("下載開始?")
            Download_Bool = input("Y/N :")
            #你可以在這之后添加下載的代碼,但我的主要目的並不是這個。
        print(fail)
    print("程序結束")
    #程序走到這一步就Goodbay了。
# mkdir函數引用來源:https://www.cnblogs.com/monsteryang/p/6574550.html
def mkdir(path):
    # 去除首位空格
    path=path.strip()
    # 去除尾部 \ 符號
    path=path.rstrip("\\")
    # 判斷路徑是否存在
    # 存在     True
    # 不存在   False
    isExists=os.path.exists(path)
    # 判斷結果
    if not isExists:
        os.makedirs(path) 
        print(path+' 創建成功')
        return True
    else:
        print(path+' 目錄已存在')
        return False
#      入口
if __name__ == "__main__":
    main()

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM