日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

Python爬蟲初體驗(2):多線程的應用及爬取中的實際問題

系統 2076 0

前情提要:Python爬蟲初體驗(1):利用requests和bs4提取網站漫畫

前幾天有些放松懈怠,并沒有做多少事情……這幾天要加油了!7月的計劃要抓緊時間完成!

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


好的廢話不多說,進入正題

總結了下上次的爬蟲體驗。雖然能保證穩定下載,但是下載 50 張XKCD漫畫花費的時間達到了將近 10 分鐘,效率比較低。

所以這次學習了多線程,以求達到較快下載完全部 2000 余張漫畫的目標。 (另外配合 V 姓網絡加速工具保證連接外網的質量)

額外加入了 threading 模塊來實現多線程。

另外,改進了一下代碼風格,變量名稱


threading 庫中,Thread()?是進行多線程操作的關鍵。

在這里簡單的應用:threading.Thread(target=xxxx, args=(), kwargs=None)

(target 指向函數本身,args 為向目標函數傳遞的常規參數,kwargs 為傳遞的關鍵字參數)

然后!

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

……

其實并不行。必須要把提取—解析—下載—存儲的全過程函數化,這樣才能實現多線程。

于是索性把所有過程都寫成了函數里……看起來雖然增加了代碼量,但是用起來就會很方便。

這里出現了一個問題:我開了?5 個線程,假如圖片一共有 2000 張還行,有 2003?張怎么辦?

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

但是,如果前 400 張圖比較小,第 401-800 張圖比較大,這樣的話第一個線程結束時間遠早于第二個,如何解決?

其實可以挨個下載:不預先分配每個線程下載哪些圖片,直接
……

吐槽:居然花了我 8?個小時來搞這段代碼!沒想到這部分是這么難弄,

搞的來和當年學 OI 時有一樣的心情了。 (心情簡單.jpg)

不得不說,學 OI 的時候調試代碼的過程,和現在很類似。只是這里涉及更多的是實際操作而非算法。

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

然后就可以繼續等待它慢慢扒圖……

……

……

然后就發現了一堆莫名其妙的錯誤!嗚嗚嗚……

Python爬蟲初體驗(2):多線程的應用及爬取中的實際問題_第1張圖片 Python爬蟲初體驗(2):多線程的應用及爬取中的實際問題_第2張圖片

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

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


再來。

后來發現,拋出異常時,僅僅針對?Readtime Error 的異常來解決問題。還有 Connection Error 等沒有處理。這個是最主要的問題。

某些地方寫入文件會有異常?嗯,這個暫時不了解原因。好像是圖片的鏈接讀取錯誤?

于是加入了兩種連網的錯誤處理。

為了防止連網出錯,特意把重試次數設為了 4 次,超時 Timeout 設為了 4 秒。

……

然而還是出錯了!

Python爬蟲初體驗(2):多線程的應用及爬取中的實際問題_第3張圖片

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

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

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

怪說不得線程又終止在這里了……

先把它們記下來,晚些時候再去解決吧。

發一個 不規范的 源碼存檔:

            
              #! 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 點準時睡覺 = =||

不說了,累死啦~趕快滾去睡


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

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

【本文對您有幫助就好】

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

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 青田县| 嘉峪关市| 乌拉特前旗| 龙州县| 册亨县| 辽阳市| 峨山| 河源市| 吉林省| 霍州市| 麻栗坡县| 平度市| 茂名市| 三穗县| 肃南| 辉县市| 三门峡市| 铜山县| 临猗县| 曲靖市| 太仆寺旗| 饶河县| 平利县| 柘荣县| 潞西市| 岢岚县| 建湖县| 石家庄市| 临夏市| 湛江市| 平原县| 霍邱县| 宁明县| 湛江市| 虞城县| 弋阳县| 永新县| 花莲县| 抚州市| 沙坪坝区| 富宁县|