Python爬蟲(chóng)(requests,Chrome的cookie文件,多線程)

爬蟲(chóng)的目標(biāo)

  • 我的目的是爬取某直播網(wǎng)站某一天所有頻道的網(wǎng)絡(luò)流量數(shù)據(jù),每一個(gè)頻道有一個(gè)自己的頁(yè)面,顯示可視化后的流量數(shù)據(jù)鹉动,而要獲取這個(gè)頻道頁(yè)面的url,需要先訪問(wèn)另一個(gè)列表頁(yè)面宏邮,每一個(gè)列表頁(yè)面包含了10個(gè)頻道的屬性信息(如channelid, starttime, endtime)泽示。因此缸血,爬蟲(chóng)過(guò)程整體分兩步:
    1. 訪問(wèn)列表頁(yè)面,通過(guò)指定日期和頁(yè)數(shù)設(shè)置http請(qǐng)求的params械筛。獲取返回的頁(yè)面內(nèi)容后捎泻,保存到本地文件。
    2. 讀取本地文件中保存的頻道屬性信息埋哟,作為訪問(wèn)頻道頁(yè)面請(qǐng)求的params笆豁,訪問(wèn)頻道頁(yè)面,獲取返回的內(nèi)容(即頻道的流量數(shù)據(jù))后定欧,保存到本地文件

第一步:爬取列表頁(yè)面

瀏覽器訪問(wèn)列表頁(yè)面時(shí)怎么做的渔呵?

  • 通過(guò)chrome瀏覽器的DevTools(快捷鍵F12)中對(duì)訪問(wèn)列表頁(yè)面的網(wǎng)絡(luò)監(jiān)控,可以知道在最終成功訪問(wèn)到目標(biāo)頁(yè)面之前砍鸠,實(shí)際上先跳轉(zhuǎn)到了4個(gè)別的頁(yè)面(xxpreLogin, xxcaLogin, xxLogin, sendxxToken)進(jìn)行登陸扩氢,最后才返回我要的訪問(wèn)列表的內(nèi)容。以上是我在打開(kāi)瀏覽器第一次訪問(wèn)該頁(yè)面的過(guò)程爷辱,但后來(lái)在頁(yè)面內(nèi)點(diǎn)擊轉(zhuǎn)到別的頁(yè)數(shù)時(shí)录豺,只有單純的一條訪問(wèn)請(qǐng)求了。為什么之后不用登陸認(rèn)證呢饭弓?因?yàn)楹髞?lái)的請(qǐng)求中帶有第一次認(rèn)證后得到的cookie双饥,服務(wù)器確認(rèn)過(guò)眼神驗(yàn)證了cookie后,就同意給出所請(qǐng)求的頁(yè)面數(shù)據(jù)弟断。

我用什么方法能到達(dá)列表頁(yè)面咏花?

  • 第一個(gè)方法:模擬整個(gè)訪問(wèn)列表的步驟,包括登陸認(rèn)證等步驟阀趴,獲取最終訪問(wèn)列表頁(yè)面時(shí)所需的信息(如cookie, token)后再訪問(wèn)昏翰。這是最符合瀏覽器思路的一個(gè)方法,但登陸步驟繁多刘急;對(duì)于沒(méi)有詳細(xì)學(xué)習(xí)過(guò)HTML和JS的我來(lái)說(shuō)棚菊,照貓畫(huà)虎地發(fā)送請(qǐng)求容易,但理解對(duì)方發(fā)來(lái)的response就比較難了叔汁。另外统求,這4個(gè)別的頁(yè)面有hppts請(qǐng)求,涉及到數(shù)據(jù)的加密据块,因此要考慮的因素很多码邻。

  • 所以我換了一個(gè) 更粗暴直接的方法:直接查看訪問(wèn)列表頁(yè)面的最后一步帶了哪些cookie,如果我?guī)贤瑯拥腸ookie瑰钮,訪問(wèn)同樣的url冒滩,肯定也能訪問(wèn)成功。此處確定了我要從瀏覽器監(jiān)控頁(yè)面復(fù)制的信息:url(帶params)浪谴,cookie开睡。把復(fù)制的cookie保存到本地文件里,我的程序訪問(wèn)時(shí)從文件中讀取cookie并添加到請(qǐng)求的headers中苟耻,把url中的params分離出來(lái)重新構(gòu)造同格式但不同值的params篇恒,baseurl/params/headers準(zhǔn)備完畢后就可以發(fā)送請(qǐng)求了。

  • 寫(xiě)http請(qǐng)求時(shí)需要明確的三個(gè)基本內(nèi)容:

    1. 訪問(wèn)該頁(yè)面時(shí)實(shí)際請(qǐng)求的url是什么(瀏覽器地址欄的url通常不是請(qǐng)求數(shù)據(jù)所用的實(shí)際url)
    2. 訪問(wèn)該頁(yè)面需要的請(qǐng)求params是什么(比如請(qǐng)求第幾頁(yè))
    3. 訪問(wèn)該頁(yè)面的headers要有什么(比如最重要的cookie)
  • 這里我使用了基本的requests庫(kù)凶杖,代碼如下:

    from urllib import parse
    from urllib.parse import urlsplit, parse_qs
    import requests
    
    # 讀取cookie
    cookie_filename = 'cookies.txt'
    cookiefile = open(cookie_filename, 'r')
    cookies = {}
    for line in cookiefile.read().split(';'):
        name, value = line.strip().split('=',1)
        cookies[name] = value
    cookiefile.close()
    
    # 訪問(wèn)列表的start_page到end_page頁(yè)
    url = 'http://xxxx.xxxxxx.com/xxx/xxxx.do?' \
          'startTime=xxxx&endTime=xxxx&currentPage=xxxx&pt=xxxx'
    start_page, end_page = 1, 1000
    for page in range(start_page, end_page):
        baseurl = parse.splitquery(url)[0]
        params = parse_qs(urlsplit(url).query)
        params['currentPage'] = page
        params['startTime'] = '2018-01-01 00:00:00'
        params['endTime'] = '2018-01-01 23:59:59'
        response = requests.get(baseurl, params=params, cookies=cookies)
        content = response.content.decode()
        pagefile = 'page' + str(page) + '.txt'
        with open(pagefile, 'w', encoding='utf-8') as pfile:
            pfile.write(content)
    
  • 像上面說(shuō)的一樣胁艰,先復(fù)制下來(lái)url到代碼中,再?gòu)?fù)制cookies到文件中智蝠,這個(gè)代碼就能成功運(yùn)行了腾么。 BUT,好景不長(zhǎng)杈湾,這段代碼在成功運(yùn)行了大約不到十分鐘后解虱,突然取不到數(shù)據(jù)而是取回了像初次訪問(wèn)一樣的登陸認(rèn)證頁(yè)面。經(jīng)過(guò)一番分析漆撞,我發(fā)現(xiàn)目標(biāo)頁(yè)面的url中有一個(gè)param實(shí)際是跟時(shí)間有關(guān)的(指上面代碼中的pt)殴泰,cookies中也有類似服務(wù)器端的心跳時(shí)間cookie,多半是服務(wù)器看我?guī)У倪@個(gè)心跳cookie太老了不在它可以回答的時(shí)間區(qū)間內(nèi)浮驳,所以返回讓我重新認(rèn)證悍汛。另外,瀏覽器每次發(fā)出請(qǐng)求(哪怕是請(qǐng)求同樣的頁(yè)面)時(shí)有一個(gè)cookie值都會(huì)隨機(jī)改變至会,目前分析應(yīng)該是和證書(shū)有關(guān)离咐。所以,雖然用同樣的一對(duì)pt和cookie大約能持續(xù)幾分鐘的成功訪問(wèn)奉件,但如果要在崩了以后還能繼續(xù)訪問(wèn)宵蛀,只是更新pt到最新時(shí)間經(jīng)嘗試是不行的。好在我要訪問(wèn)的列表頁(yè)面總頁(yè)數(shù)不是很多瓶蚂,所以我將代碼稍加修改糖埋,記錄每次崩時(shí)訪問(wèn)到哪一頁(yè)了,然后用瀏覽器再訪問(wèn)一次窃这,復(fù)制新的url和cookies瞳别,從斷掉的地方接著跑。

  • 改動(dòng)后的代碼如下:

    from urllib import parse
    from urllib.parse import urlsplit, parse_qs
    import requests
    
    # 讀取cookie
    cookie_filename = 'cookies.txt'
    cookiefile = open(cookie_filename, 'r')
    cookies = {}
    for line in cookiefile.read().split(';'):
        name, value = line.strip().split('=',1)
        cookies[name] = value
    cookiefile.close()
    
    # 訪問(wèn)列表的start_page到end_page頁(yè)
    url = 'http://xxxx.xxxxxx.com/xxx/xxxx.do?' \
          'startTime=xxxx&endTime=xxxx&currentPage=xxxx&pt=xxxx'
    start_page, end_page = 1, 1000
    for page in range(start_page, end_page):
        baseurl = parse.splitquery(url)[0]
        params = parse_qs(urlsplit(url).query)
        params['currentPage'] = page
        params['startTime'] = '2018-01-01 00:00:00'
        params['endTime'] = '2018-01-01 23:59:59'
        response = requests.get(baseurl, params=params, cookies=cookies)
        # 如果返回的是登陸認(rèn)證頁(yè)面的地址杭攻,記錄訪問(wèn)到哪一頁(yè)了祟敛,退出循環(huán)
        resurl = parse.splitquery(response.url)[0]
        if resurl == 'https://xxxLogin.xxxx.xxx':
            print('Failed at page '+str(page)+', please restart.')
            break
        # 成功訪問(wèn)時(shí),保存數(shù)據(jù)
        content = response.content.decode()
        pagefile = 'page' + str(page) + '.txt'
        with open(pagefile, 'w', encoding='utf-8') as pfile:
            pfile.write(content)
    
  • 到此可以爬下來(lái)所有列表頁(yè)面的內(nèi)容了兆解,很明顯馆铁,每十分鐘人工刷新瀏覽器再?gòu)?fù)制粘貼一次是非常耗時(shí)的,這樣的代碼在后面爬取每個(gè)頻道的流量信息時(shí)是不可取的(加載一頁(yè)列表大約1-3秒锅睛,而加載一頁(yè)頻道流量信息可能要10+秒)埠巨,不過(guò)對(duì)于非時(shí)間敏感型的請(qǐng)求來(lái)說(shuō)历谍,上面的代碼是足夠用了。

  • 本著能少一次復(fù)制就少一次的原則辣垒,我希望每次只復(fù)制url望侈,而cookie的信息肯定能從瀏覽器(這里用的chrome)的本地緩存文件中讀,就不需要我花費(fèi)寶貴的時(shí)間復(fù)制到我準(zhǔn)備的文件里了勋桶。因此我查了Chrome瀏覽器在系統(tǒng)中保存cookie的位置脱衙,修改代碼,使 每次開(kāi)始爬取時(shí)直接讀取瀏覽器的cookie例驹。

  • 改動(dòng)后的代碼如下:

    from urllib import parse
    from urllib.parse import urlsplit, parse_qs
    import requests
    import sqlite3
    from win32crypt import CryptUnprotectData
    
    # 從Chrome的cookie文件讀取cookie
    cookie_path = r'C:\Users\ann\AppData\Local\Google\Chrome\User Data\Default\Cookies'
    host = '.xxx.com'
    sql = 'select host_key, name, encrypted_value from cookies '\
          'where host_key = \'' + host + '\''
    with sqlite3.connect(cookie_path) as conn:
        cu = conn.cursor()
        cookies = {name:CryptUnprotectData(encrypted_value)[1].decode() '\
                  'for host_key, name, encrypted_value in cu,execute(sql).fetchall()}
    
    # 訪問(wèn)列表的start_page到end_page頁(yè)
    url = 'http://xxxx.xxxxxx.com/xxx/xxxx.do?' \
          'startTime=xxxx&endTime=xxxx&currentPage=xxxx&pt=xxxx'
    start_page, end_page = 1, 1000
    for page in range(start_page, end_page):
        baseurl = parse.splitquery(url)[0]
        params = parse_qs(urlsplit(url).query)
        params['currentPage'] = page
        params['startTime'] = '2018-01-01 00:00:00'
        params['endTime'] = '2018-01-01 23:59:59'
        response = requests.get(baseurl, params=params, cookies=cookies)
        # 如果返回的是登陸認(rèn)證頁(yè)面的地址捐韩,記錄訪問(wèn)到哪一頁(yè)了,退出循環(huán)
        resurl = parse.splitquery(response.url)[0]
        if resurl == 'https://xxxLogin.xxxx.xxx':
            print('Failed at page '+str(page)+', please restart.')
            break
        # 成功訪問(wèn)時(shí)鹃锈,保存數(shù)據(jù)
        content = response.content.decode()
        pagefile = 'page' + str(page) + '.txt'
        with open(pagefile, 'w', encoding='utf-8') as pfile:
            pfile.write(content)
    
  • 至此荤胁,每當(dāng)訪問(wèn)崩了,我還是得手動(dòng)用瀏覽器訪問(wèn)一次仪召,復(fù)制url寨蹋,從斷掉的地方繼續(xù)跑。顯然扔茅,即使不用復(fù)制cookies了已旧,這個(gè)代碼還是不能滿足爬取頻道流量的大量數(shù)據(jù)的要求。

  • 因此召娜,爬取頻道流量信息時(shí)我用了另一種思路進(jìn)行訪問(wèn)运褪,不用cookie信息,解放了雙手玖瘸,實(shí)現(xiàn)了全自動(dòng)爬取秸讹。不過(guò),要訪問(wèn)頻道流量信息頁(yè)面雅倒,還得先知道頻道的一些屬性(比如channelId, startTime, endTime)璃诀,把屬性寫(xiě)進(jìn)params,才能獲得正確的頻道頁(yè)面蔑匣,因此劣欢,下一步我們先從爬下來(lái)的列表頁(yè)面中提取頻道屬性信息。

第二步:從列表頁(yè)面文件獲取頻道屬性信息

  • 從瀏覽器訪問(wèn)的列表頁(yè)面來(lái)看裁良,一頁(yè)列表有10條頻道屬性信息凿将,因此我到保存的列表頁(yè)面文件中觀察(文件里保存的直接是每個(gè)頻道的數(shù)據(jù),不是這一頁(yè)的html代碼)价脾,每條頻道信息開(kāi)頭都有'"channelhead"'字樣(沒(méi)錯(cuò)帶雙引號(hào)牧抵,這里channelhead不是真實(shí)信息只是我舉個(gè)例子),每條頻道的屬性包括type, channelId, startTime, endTime等侨把。這里我只需要type=1的頻道的信息犀变,因此讀文件時(shí)需要加判斷條件妹孙。
  • 這里我結(jié)合了python的find函數(shù)和簡(jiǎn)單的正則表達(dá)式進(jìn)行提取,代碼如下:
    import re
    
    start_page, end_page = 1, 1000
    query_str = ['channelId startTime endTime']
    
    for page in range(start_page, end_page):
        # 讀出第page頁(yè)的列表頁(yè)面文件
        pagefile = 'page' + str(page) + '.txt'
        pfile = open(pagefile, 'r', encoding='utf-8')
        data = pfile.read()
        pfile.close()
        # 從列表頁(yè)面數(shù)據(jù)有選擇地提取頻道屬性
        query_count = data.count('"allowVideo"')
        index1 = data.find('"allowVideo"')
        for i in range(query_count-1):
            # 找到一條頻道信息的區(qū)間[index1, index2]
            index2 = data.find('"allowVideo"', index1+1)
            query_data = data[index1:index2]
            if query_data.find('"type":"1"') > 0:
                # channelId:先用find找到channelid位置弛作,再用正則匹配提取區(qū)間內(nèi)的數(shù)字
                index_chid = query_data.find('channelId')
                chid = re.sub(r'\D', '', query_data[index_chid, index_chid+30])
                # startTime
                index_st = query_data.find('startTime')
                st = query_data[index_st+12, index_st+19]
                # endTime
                index_et = query_data.find('endTime')
                et = query_data[index_et+10, index_et+19]
                str = chid + ' ' + st + ' ' + et
                query_str.append(str)
            index1 = index2
    
    # 把所有的query存入另一個(gè)文件
    # (實(shí)際上我得到的query的屬性遠(yuǎn)不止這些涕蜂,因此是分很多文件存的华匾,
    # 由于文件讀寫(xiě)操作不是此次爬蟲(chóng)的重點(diǎn)映琳,所以這里不細(xì)講)
    query_filename = 'query.txt'
    with open(query_filename, 'w', encoding='utf-8') as qfile:
        for item in query_str:
            qfile.write(item + '\n')
    
  • 到此我提取了所有需要的頻道屬性的信息,只待拿著這些信息去請(qǐng)求頻道網(wǎng)絡(luò)流量頁(yè)面了

第三步:獲取每個(gè)頻道網(wǎng)絡(luò)流量頁(yè)面

  • 第一步的方法蜘拉,雖然能繞過(guò)登陸認(rèn)證的步驟萨西,也能自動(dòng)從Chrome的本地cookie文件中提取我要的cookie,但每隔幾分鐘我還是得手動(dòng)刷新瀏覽器并復(fù)制其url粘貼到代碼中旭旭,整個(gè)操作非常笨拙耗時(shí)效率低下谎脯。于是我又換了一個(gè)思路:有什么方法能 直接調(diào)用Chrome瀏覽器來(lái)訪問(wèn) 嗎?我不想知道它訪問(wèn)的細(xì)節(jié)持寄,只要給我返回的最終結(jié)果就好源梭。于是我發(fā)現(xiàn)了 selenium庫(kù)的webdriver 方法。

  • 調(diào)用webdriver.Chrome()會(huì)生成一個(gè)瀏覽器對(duì)象稍味,瀏覽器對(duì)象的.get(url)方法就能使瀏覽器自己帶上合適的cookie并返回相應(yīng)頁(yè)面废麻,瀏覽器的.page_source屬性就是頁(yè)面的內(nèi)容啦。

  • 使用selenium庫(kù)的webdriver調(diào)用Chrome瀏覽器訪問(wèn)頻道頁(yè)面并存入文件的代碼如下:

    from selenium import webdriver
    import time
    form urllib import parse
    import os
    
    baseurl = 'http://xxx.xxxx.xxx/xxx.do'
    # query文件的前綴模庐,每個(gè)query文件都有多條channel的屬性信息
    query_fileadd = r'D:\xxx\query'
    # 每個(gè)query文件對(duì)應(yīng)一個(gè)裝channelpage的文件夾
    channelpage_fileadd = r'D:\xxx\ChannelPages\Query'
    
    start, end = 1, 10
    
    # 初始化一個(gè)Chrome的driver
    browser = webdriver.Chrome()
    for i in range(start, end+1):
        # 讀取第i個(gè)query文件的所有queries
        query_filename = query_fileadd + str(i) + '.txt'
        qfile = open(query_filename, 'r')
        query_list = []
        for line in qfile:
            query_list.append(line)
        qfile.close()
        # 確保創(chuàng)建了channelpage的路徑
        cpdir = os.path.join(channelpage_fileadd, str(i))
        if not os.path.exists(cpdir):
            os.mkdir(cpdir)
        # 根據(jù)query_list發(fā)送http請(qǐng)求獲取channel page
        for i in range(len(query_list))
            # url的拼接
            q = query_list[i].split(' ')
            chid, st, et = q[0], q[1], q[2]
            pt = str(int(time.time()))
            params = dict(startTime=st, endTime=et, channelId=chid, pt=pt)
            url = baseurl + '?' + parse.urlencode(query=params)
            # 獲取channel page的數(shù)據(jù)
            browser.get(url)
            channelpagedata = browser.page_source
            # 寫(xiě)入channel文件
            cpfname = os.path.join(cpdir, 'channel'+str(i)+'.txt')
            with open(cpfname, 'w', encoding='utf-8') as cf:
                cf.write(channelpagedata)
    browser.quit()
    
    • 上面的代碼實(shí)現(xiàn)了一鍵訪問(wèn)下載頻道頁(yè)面的目標(biāo)烛愧,解放了雙手,非常開(kāi)心掂碱!然而過(guò)了幾分鐘之后怜姿,我眉頭一皺,發(fā)現(xiàn) 事情并沒(méi)有這么簡(jiǎn)單疼燥。
    • 之前提到過(guò)沧卢,瀏覽器訪問(wèn)一個(gè)頻道頁(yè)面大約要10+秒,導(dǎo)致上面調(diào)用瀏覽器的代碼下載非常龜速醉者,那么如何能讓下載速度提高呢但狭? 多線程。這里我需要的數(shù)據(jù)沒(méi)有下載先后的要求湃交,存數(shù)據(jù)的文件也是每個(gè)頻道有獨(dú)立的文件熟空,所以甚至連鎖也用不上。
    • 每個(gè)線程負(fù)責(zé)的任務(wù):這里我有多個(gè)query的文件搞莺,每個(gè)文件有多條channel的屬性息罗,每條channel屬性對(duì)應(yīng)訪問(wèn)一次channel頁(yè)面。假設(shè)query文件一共50個(gè)才沧,每個(gè)文件有1000條屬性迈喉,簡(jiǎn)單來(lái)分配的話绍刮,每條線程負(fù)責(zé)10個(gè)query文件共5條線程就行(如果內(nèi)存容量比較大,那可以更多條挨摸,速度會(huì)相對(duì)更快)孩革。
    • 這里多線程我使用的 threading 庫(kù),多線程訪問(wèn)并存儲(chǔ)的代碼如下:
    from selenium import webdriver
    from urllib import parse
    import os
    import threading
    
    def getChannelPages(startpage, endpage, query_fileadd, channelpage_fileadd, baseurl, params):
        browser = webdriver.Chrome()
        for i in range(startpage, endpage, 100):
            # 讀取文件的query存入query_list
            qfname = query_fileadd + str(i) + '.txt'
            qfile = open(qfname)
            query_list = []
            for line in qfile:
                query_list.append(line)
            qfile.close()
            # 確保創(chuàng)建了存channel page的路徑
            cpdir = os.path.join(channelpage_fileadd, str(i))
            if not os.path.exists(cpdir):
                os.mkdir(cpdir)
            # 根據(jù)query_list發(fā)送http請(qǐng)求獲取channel page
            for index in range(len(query_list)):
                # url的拼接
                query = query_list[index].split(' ')
                chid, st, et = query[0], query[1], query[2]
                pt = str(int(time.time()))
                params['startTime'] = st
                params['endTime'] = et
                params['channelId'] = chid
                url = baseurl + '?' + parse.urlencode(query=params)
                # 獲取channel page數(shù)據(jù)
                browser.get(url)
                channelpagedata = browser.page_source
                # 寫(xiě)入channel文件
                cpfadd = os.path.join(cpdir, 'channel' + str(index) + '.txt')
                with open(cpfadd, 'w', encoding='utf-8') as cf:
                    cf.write(channelpagedata)
        browser.quit()
    
    if __name__ == '__main__':
        query_fileadd = r'D:\xxx\query'
        channelpage_fileadd = r'D\xxx\ChannelPages'
        # channel頁(yè)面參數(shù)設(shè)置: baseurl, params
        baseurl = 'http://xxxx.xxxx.xxx/xxx.do'
        params = dict(startTime='',endTime='',channelId='')
        threads = []
        # 初始化各線程
        startpage, endpage = 1, 1000
        for i in range(startpage, endpage, 200):
            t = threading.Thread(target=getChannelPages, args=(i, i+199, query_fileadd, baseurl, params))
            threads.append(t)
        # 開(kāi)始表演
        for t in threads:
            t.start()
        for t in threads:
            t.join()
    
    • 目前代碼實(shí)現(xiàn)了多線程爬蟲(chóng)的功能得运,大幅提高了爬蟲(chóng)的速度膝蜈,不過(guò)還有很多可以優(yōu)化的部分(比如換一種線程負(fù)責(zé)的功能,或者改成多進(jìn)程熔掺,還有異步饱搏,除了線程以外,對(duì)頻道屬性的提取可以用更高效的正則表達(dá)式而不是find函數(shù))置逻。

    完整代碼

    • 下面是爬取列表頁(yè)推沸,提取query,爬取頻道頁(yè)的完整代碼券坞,其中爬取列表頁(yè)也改成多線程方式了鬓催。
    from selenium import webdriver
    from urllib import parse
    import time
    import re
    import os
    import threading
    
    def getHistoryPages(startpage, endpage, listpage_fileadd, baseurl, params):
        # 訪問(wèn)第startpage頁(yè)到第endpage頁(yè)的列表頁(yè)面,每100頁(yè)存入一個(gè)文件
        browser = webdriver.Chrome()
        for bigpage in range(startpage, endpage, 100):
            if bigpage+99 > endpage:
                end = endpage
            else:
                end = bigpage + 99
            page_data = []
            for page in range(bigpage, end+1):
                params['currentPage'] = str(page)
                params['pt'] = str(int(time.time))
                url = baseurl + '?' + parse.urlencode(query=params)
                browser.get(url)
                page_data.append(browser.page_source)
            listpage_filename = listpage_fileadd + str(bigpage) + '-' + str(bigpage+99) + '.txt'
            lfile = open(listpage_filename, 'w', encoding='utf-8')
            for item in page_data:
                lfile.write(item+'\n')
            lfile.close()
            del page_data
        browser.quit()
    
    def extractQueries(listpage_filename, query_filename):
        # 把給定文件里的page data轉(zhuǎn)換成query
        query_str = ['channelId startTime endTime']
        file = open(listpage_filename, 'r', encoding='utf-8')
        data = file.read()
        file.close()
        query_count = data.count('"allowVideo"')
        index1 = data.find('"allowVideo"')
        for i in range(query_count - 1):
            index2 = data.find('"allowVideo"', index1 + 1)
            query_data = data[index1:index2]
            if query_data.find('"type":"1"'):
            index_chid = query_data.find('channelId')
            chid = re.sub(r'\D', '', query_data[index_chid, index_chid+30])
            index_st = query_data.find('startTime')
            st = query_data[index_st+12, index_st+19]
            index_et = query_data.find('endTime')
            et = query_data[index_et+10, index_et+19]
            str = chid + ' ' + st + ' ' + et
            query_str.append(str)
        index1 = index2
        # 把query_str寫(xiě)入文件
        qfile = open(query_filename, 'w', encoding='utf-8')
        for item in query_str:
            qfile.write(item+'\n')
    
    def getChannelPages(startpage, endpage, query_fileadd, channelpage_fileadd, baseurl, params):
        browser = webdriver.Chrome()
        for i in range(startpage, endpage, 100):
            # 讀取文件的query存入query_list
            qfname = query_fileadd + str(i) + '-' + str(i+99) '.txt'
            qfile = open(qfname)
            query_list = []
            for line in qfile:
                query_list.append(line)
            qfile.close()
            # 確保創(chuàng)建了存channel page的路徑
            cpdir = os.path.join(channelpage_fileadd, str(i) + '-' + str(i+99))
            if not os.path.exists(cpdir):
                os.mkdir(cpdir)
            # 根據(jù)query_list發(fā)送http請(qǐng)求獲取channel page
            for index in range(len(query_list)):
                # url的拼接
                query = query_list[index].split(' ')
                chid, st, et = query[0], query[1], query[2]
                pt = str(int(time.time()))
                params['startTime'] = st
                params['endTime'] = et
                params['channelId'] = chid
                url = baseurl + '?' + parse.urlencode(query=params)
                # 獲取channel page數(shù)據(jù)
                browser.get(url)
                channelpagedata = browser.page_source
                # 寫(xiě)入channel文件
                cpfadd = os.path.join(cpdir, 'channel' + str(index) + '.txt')
                with open(cpfadd, 'w', encoding='utf-8') as cf:
                    cf.write(channelpagedata)
        browser.quit()
    
    if __name__ == '__main__':
    
        listpage_fileadd = r'D:\xxx\xxx\page'
        query_fileadd = r'D:\xxx\query'
        channelpage_fileadd = r'D\xxx\ChannelPages'
    
        # 列表頁(yè)面參數(shù)設(shè)置: baseurl, params
        baseurl = 'http://xxxx.xxxx.xxx/xxx.do'
        params = dict(startTime='2018-01-01 00:00:00', endTime='2018-01-01 00:00:00')
        startpage, endpage = 1, 1000
        threads = []
        for page in range(startpage, endpage, 200):
            t = threading.Thread(target=getHistoryPages, args=(page, page+199, listpage_fileadd, baseurl, params))
            threads.append(t)
        for t in threads:
            t.start()
        for t in threads:
            t.join()
    
        # 從channel list文件中提取channel的屬性
        startpage, endpage = 1, 1000
        for listpage in range(startpage, endpage, 100):
            listpage_filename = listpage_fileadd + str(listpage) + '-' + str(listpage+99) + '.txt'
            query_filename = query_fileadd + str(listpage) + '-' + str(listpage+99) + '.txt'
            extractQueries(listpage_filename, query_filename)
    
        # channel頁(yè)面參數(shù)設(shè)置: baseurl, params
        baseurl = 'http://xxxx.xxxx.xxx/xxx.do'
        params = dict(startTime='',endTime='',channelId='')
        threads = []
        startpage, endpage = 1, 1000
        for i in range(startpage, endpage, 200):
            t = threading.Thread(target=getChannelPages, args=(i, i+199, query_fileadd, baseurl, params))
            threads.append(t)
        for t in threads:
            t.start()
        for t in threads:
            t.join()
    
    

    后續(xù)

    • 爬下來(lái)這么多數(shù)據(jù)恨锚,肯定不能存在同一個(gè)電腦里宇驾,因此之后還經(jīng)歷了設(shè)置ftp server等步驟對(duì)爬下來(lái)的數(shù)據(jù)進(jìn)行轉(zhuǎn)移,這里不細(xì)講了眠冈,網(wǎng)上有很多教程飞苇。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蜗顽,隨后出現(xiàn)的幾起案子布卡,更是在濱河造成了極大的恐慌须鼎,老刑警劉巖混狠,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異咽瓷,居然都是意外死亡崔挖,警方通過(guò)查閱死者的電腦和手機(jī)贸街,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)狸相,“玉大人薛匪,你說(shuō)我怎么就攤上這事∨Ь椋” “怎么了逸尖?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我娇跟,道長(zhǎng)岩齿,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任苞俘,我火速辦了婚禮盹沈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘吃谣。我一直安慰自己乞封,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布基协。 她就那樣靜靜地躺著歌亲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪澜驮。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 50,050評(píng)論 1 291
  • 那天惋鸥,我揣著相機(jī)與錄音杂穷,去河邊找鬼。 笑死卦绣,一個(gè)胖子當(dāng)著我的面吹牛耐量,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播滤港,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼廊蜒,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了溅漾?” 一聲冷哼從身側(cè)響起山叮,我...
    開(kāi)封第一講書(shū)人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎添履,沒(méi)想到半個(gè)月后屁倔,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡暮胧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年锐借,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片往衷。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡钞翔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出席舍,到底是詐尸還是另有隱情布轿,我是刑警寧澤,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站驮捍,受9級(jí)特大地震影響疟呐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜东且,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一启具、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧珊泳,春花似錦鲁冯、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至秧了,卻和暖如春跨扮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背验毡。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工衡创, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人晶通。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓璃氢,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親狮辽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子一也,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)喉脖,斷路器椰苟,智...
    卡卡羅2017閱讀 134,638評(píng)論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法动看,內(nèi)部類的語(yǔ)法尊剔,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法菱皆,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,602評(píng)論 18 399
  • Address:https://www.zybuluo.com/XiangZhou/note/208532 Exp...
    天蠍蒗漫閱讀 11,289評(píng)論 2 55
  • 大家好须误,上次我們實(shí)驗(yàn)了爬取了糗事百科的段子,那么這次我們來(lái)嘗試一下爬取百度貼吧的帖子仇轻。與上一篇不同的是京痢,這次我們需...
    追不到的那縷風(fēng)閱讀 560評(píng)論 0 0
  • “快速時(shí)尚”源自于上世紀(jì)中葉的歐洲,本是對(duì)服裝秀場(chǎng)設(shè)計(jì)的快速回饋和模仿篷店。隨著服裝設(shè)計(jì)和終端銷售的日益密切祭椰,時(shí)至上世...
    Starwarming閱讀 372評(píng)論 0 0