黄色网页视频 I 影音先锋日日狠狠久久 I 秋霞午夜毛片 I 秋霞一二三区 I 国产成人片无码视频 I 国产 精品 自在自线 I av免费观看网站 I 日本精品久久久久中文字幕5 I 91看视频 I 看全色黄大色黄女片18 I 精品不卡一区 I 亚洲最新精品 I 欧美 激情 在线 I 人妻少妇精品久久 I 国产99视频精品免费专区 I 欧美影院 I 欧美精品在欧美一区二区少妇 I av大片网站 I 国产精品黄色片 I 888久久 I 狠狠干最新 I 看看黄色一级片 I 黄色精品久久 I 三级av在线 I 69色综合 I 国产日韩欧美91 I 亚洲精品偷拍 I 激情小说亚洲图片 I 久久国产视频精品 I 国产综合精品一区二区三区 I 色婷婷国产 I 最新成人av在线 I 国产私拍精品 I 日韩成人影音 I 日日夜夜天天综合

Python爬蟲(chóng)初體驗(yàn)(2):多線程的應(yīng)用及爬取中的實(shí)際問(wèn)題

系統(tǒng) 2313 0

前情提要:Python爬蟲(chóng)初體驗(yàn)(1):利用requests和bs4提取網(wǎng)站漫畫

前幾天有些放松懈怠,并沒(méi)有做多少事情……這幾天要加油了!7月的計(jì)劃要抓緊時(shí)間完成!

今天瘋狂肝這個(gè)程序,算是暑假睡得最晚的一天了……(不過(guò)程序仍然有問(wèn)題)


好的廢話不多說(shuō),進(jìn)入正題

總結(jié)了下上次的爬蟲(chóng)體驗(yàn)。雖然能保證穩(wěn)定下載,但是下載 50 張XKCD漫畫花費(fèi)的時(shí)間達(dá)到了將近 10 分鐘,效率比較低。

所以這次學(xué)習(xí)了多線程,以求達(dá)到較快下載完全部 2000 余張漫畫的目標(biāo)。 (另外配合 V 姓網(wǎng)絡(luò)加速工具保證連接外網(wǎng)的質(zhì)量)

額外加入了 threading 模塊來(lái)實(shí)現(xiàn)多線程。

另外,改進(jìn)了一下代碼風(fēng)格,變量名稱


threading 庫(kù)中,Thread()?是進(jìn)行多線程操作的關(guān)鍵。

在這里簡(jiǎn)單的應(yīng)用:threading.Thread(target=xxxx, args=(), kwargs=None)

(target 指向函數(shù)本身,args 為向目標(biāo)函數(shù)傳遞的常規(guī)參數(shù),kwargs 為傳遞的關(guān)鍵字參數(shù))

然后!

一樣的方法去弄就可以了……

……

其實(shí)并不行。必須要把提取—解析—下載—存儲(chǔ)的全過(guò)程函數(shù)化,這樣才能實(shí)現(xiàn)多線程。

于是索性把所有過(guò)程都寫成了函數(shù)里……看起來(lái)雖然增加了代碼量,但是用起來(lái)就會(huì)很方便。

這里出現(xiàn)了一個(gè)問(wèn)題:我開(kāi)了?5 個(gè)線程,假如圖片一共有 2000 張還行,有 2003?張?jiān)趺崔k?

emm,前 4 個(gè)線程下載 401 張圖,第 5 個(gè)下載 399 張圖就可以啦!

但是,如果前 400 張圖比較小,第 401-800 張圖比較大,這樣的話第一個(gè)線程結(jié)束時(shí)間遠(yuǎn)早于第二個(gè),如何解決?

其實(shí)可以挨個(gè)下載:不預(yù)先分配每個(gè)線程下載哪些圖片,直接
……

吐槽:居然花了我 8?個(gè)小時(shí)來(lái)搞這段代碼!沒(méi)想到這部分是這么難弄,

搞的來(lái)和當(dāng)年學(xué) OI 時(shí)有一樣的心情了。 (心情簡(jiǎn)單.jpg)

不得不說(shuō),學(xué) OI 的時(shí)候調(diào)試代碼的過(guò)程,和現(xiàn)在很類似。只是這里涉及更多的是實(shí)際操作而非算法。

實(shí)際操作就要考慮異常,異常處理,維護(hù),等等。所以花時(shí)間也是避免不了的啦……

然后就可以繼續(xù)等待它慢慢扒圖……

……

……

然后就發(fā)現(xiàn)了一堆莫名其妙的錯(cuò)誤!嗚嗚嗚……

Python爬蟲(chóng)初體驗(yàn)(2):多線程的應(yīng)用及爬取中的實(shí)際問(wèn)題_第1張圖片 Python爬蟲(chóng)初體驗(yàn)(2):多線程的應(yīng)用及爬取中的實(shí)際問(wèn)題_第2張圖片

(一共下載了 607 張圖片,最后 5 個(gè)線程全部斷掉了……為什么我設(shè)置了超時(shí)重試都還會(huì) Timeout Error……QAQ)

(最好笑的還是 retry 4 in 3 times……這個(gè)是如何做到的)


再來(lái)。

后來(lái)發(fā)現(xiàn),拋出異常時(shí),僅僅針對(duì)?Readtime Error 的異常來(lái)解決問(wèn)題。還有 Connection Error 等沒(méi)有處理。這個(gè)是最主要的問(wèn)題。

某些地方寫入文件會(huì)有異常?嗯,這個(gè)暫時(shí)不了解原因。好像是圖片的鏈接讀取錯(cuò)誤?

于是加入了兩種連網(wǎng)的錯(cuò)誤處理。

為了防止連網(wǎng)出錯(cuò),特意把重試次數(shù)設(shè)為了 4 次,超時(shí) Timeout 設(shè)為了 4 秒。

……

然而還是出錯(cuò)了!

Python爬蟲(chóng)初體驗(yàn)(2):多線程的應(yīng)用及爬取中的實(shí)際問(wèn)題_第3張圖片

這次總算是查到了失敗的原因。

XKCD漫畫還真的有些不同尋常,例如:第 404 張漫畫竟然……就是一個(gè) 404 not found 的網(wǎng)站???

第 1350,2067 張漫畫居然有用戶交互的方式?

怪說(shuō)不得線程又終止在這里了……

先把它們記下來(lái),晚些時(shí)候再去解決吧。

發(fā)一個(gè) 不規(guī)范的 源碼存檔:

            
              #! python3
# Upgraded version. Use Threading to speed-up the download process.

import os,requests,bs4,threading,math,time
url = "http://xkcd.com/"
errList = []
mutex = threading.Lock()

def createDir():
    os.chdir("G:\\work\\gjmtest")
    os.makedirs(".\\comicsplus2", exist_ok=True)
    os.chdir(".\\comicsplus2")

def getResource(link,num=None,notify=False,tle=4):                       # default timeout: 3 seconds
    count = 1
    while count <= 4:
        try:
            if num is None:
                res = requests.get(link, timeout=tle)
            else:
                res = requests.get(link+str(num), timeout=tle)
            res.raise_for_status()
            return res
        except:
            count += 1
            if notify is True and count <= 4:
                print("Timeout. Retry %d in 3 times..." % count)
    if num is None:
        raise TimeoutError("Can't connect to "+url+".Please check your Internet configuration.")
    else:
        raise TimeoutError("Can't connect to "+url+str(num)+".Please check your Internet configuration.")

def getSoup(res):
    soup = bs4.BeautifulSoup(res.text, features="html.parser")
    return soup

def getImageNum(soup):
    b = soup.select("#middleContainer")              # find image num
    picNumString = b[0].text
    picNumPosStart = picNumString.find("xkcd.com")
    picNumPosEnd = picNumString.find("Image URL")
    picNum = picNumString[(picNumPosStart + 9):(picNumPosEnd - 2)]
    return picNum

def getImageUrl(soup):
    k = soup.select("#comic img")
    picUrl = k[0].get("src")  # //img.xxxxxx
    return picUrl

def writeFile(num, pic_res, pic_link, path="G:\\work\\gjmtest\\comicsplus2\\"):
    f = open(path + num + '_' + os.path.basename(pic_link), "wb")
    for chunk in pic_res.iter_content(100000):
        f.write(chunk)
    f.close()

def errorRetry():
    for i in errList:                               # for each picture number (error occurred)
        download(i)

def download(i):                                    # download particular picture
    print("Downloading picture %d..." % i)
    try:
        res = getResource(url, num=i, notify=True)
    except:
        mutex.acquire()
        errList.append(i)
        print("Error occurred: picture %d !!!" % i)
        mutex.release()
        return
    print("Parsing webpage of picture %d..." % i)
    soup = getSoup(res)
    num = getImageNum(soup)
    picurl = getImageUrl(soup)  # picture resources
    picres = requests.get("http:" + picurl)
    print("Writing picture %d..." % i)
    writeFile(num, picres, picurl)
    print("Succeeded in picture %d." % i)

def downloadSeq(start, end):                           # [start, end)
    for i in range(start, end):
        download(i)

def createThread(total_num):
    count = 1
    th = 0
    thread_list = []
    remain = total_num
    while remain > groupNum:
        thread_list.append(threading.Thread(target=downloadSeq, args=(count, count + groupNum)))
        thread_list[th].start()
        remain -= groupNum
        count += groupNum
        th += 1
    thread_list.append(threading.Thread(target=downloadSeq, args=(count, total_num)))
    thread_list[th].start()
    for t in thread_list:
        t.join()

def getTotalImage():  # one-time use
    try:
        res = getResource(url)
    except:
        print("Your network is too bad!")
        exit()
    soup = getSoup(res)
    num = int(getImageNum(soup))
    print("Total image number is %d." % num)
    print("Downloading process started.")
    return num

if __name__ == "__main__":
    startTime = time.time()
    createDir()
    totalNum = getTotalImage()
    threads = 5
    groupNum = math.ceil(totalNum / threads)

    createThread(totalNum)
    errorRetry()
    endTime = time.time()
    timeCost = round(endTime - startTime, 1)
    print("Done. Total time: %s sec." ,str(timeCost))

            
          

我保證,以后一定要 11 點(diǎn)準(zhǔn)時(shí)睡覺(jué) = =||

不說(shuō)了,累死啦~趕快滾去睡


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論