爬蟲(二)——使用多線程的方式爬取新版CSDN博客的總訪問量

一.背景描述

    在訪問量上1萬+後就看不到具體的訪問數了,爲了獲得總訪問數可以將各篇博客的訪問數加起來,爲了實現這個步驟的自動化,使用了爬蟲的方法,怎麼爬csdn的博客訪問量可以參考以下博客,在此不贅述。

    https://blog.csdn.net/u011031257/article/details/80931165

    在爬取的時候發現了兩個問題:

1)如果只是單純的按照正則化的方法爬取帶‘閱讀數’的數據,每一頁都有一個閱讀數13的干擾項,不知道是啥。導致文章數和訪問量數不匹配,閱讀數會多一個。

2)訪問量≠閱讀數總和,訪問量會大一點,比如我現在閱讀數總和9922,但訪問量1w+了,有時發佈博客的時候最初一段時間會發生閱讀數往下掉的現象,應該是網址對計數機制有設定。

3)舊版本只有閱讀數,新版本增加了評論數,使用參考博客的正則匹配方式無法獲得閱讀數了,這裏用審查元素的方法重新對網頁信息進行了約束,修改了正則匹配模式,將閱讀數與評論數都爬出來,再根據一隔一的特性(visits[::2])進行後續處理。

二.多進程or多線程

    理論上是可以使用多線程,多進程,線程池,進程池的方法來實現功能,但特殊在我這裏是利用一個列表sum_list存儲各篇文章的閱讀數,如果使用進程的方法,那麼在往這個list進行append操作時,會發生資源衝突的現象,導致list在len邊長之後還會發生突然從0開始的問題,用線程的方法可以規避這個問題,有興趣可以看一下def的error_process的運行過程。

三、代碼

import requests
import re
from fake_useragent import UserAgent
import time
import concurrent
from concurrent import futures


from multiprocessing import Pool
starttime=time.time()
header={'User-Agent':UserAgent().ie}#csdn網頁不僞裝header也沒事
pages = 5
base_url = "https://blog.csdn.net/weixin_41819299/article/list/"#要挖掘網頁之間的聯繫
urls=[]
# global sum_num
sum_num = []

for x in range(pages):
    urls.append(base_url+str(x+1))
def usual():
    sum = 0
    for x in range(pages):
        r = requests.get(base_url+str(x+1),headers=header)
        titles = re.findall(r'<span class="article-type type-.*?">\n.*?</span>\n(.*?)</a>', r.text)
        i = r.content.find(bytes(titles[0].encode()))  # 以找到的文章位置爲基礎向後找閱讀數
        # print(titles)
        #舊版未加評論數前
        # visits = re.findall(r'<span class="read-num">閱讀數:(.*?)</span>', r.content.decode()[i:])
        visits = re.findall(r'<span class="num">(.*?)</span>', r.content.decode()[i:])
        print('visits :',visits)
        visits_wrong = re.findall(r'(<span class="read-num">閱讀數:.*?</span>)', r.text)#每一頁都有一個不知道是什麼的閱讀數:13,且都放在了開頭
        for x, y in zip(titles, visits[::2]): #文章與閱讀數組合
            print(x, ": ",y)
            sum += int(y)
    print(sum)

def get_info(url):
    r = requests.get(url,headers=header)
    titles = re.findall(r'<span class="article-type type-.*?">\n.*?</span>\n(.*?)</a>', r.text)
    i = r.content.find(bytes(titles[0].encode()))  # 以找到的文章位置爲基礎向後找閱讀數
    # visits = re.findall(r'<span class="read-num">閱讀數:(.*?)</span>', r.content.decode()[i:])#舊版未加評論數前
    visits = re.findall(r'<span class="num">(.*?)</span>', r.content.decode()[i:])
    # visits_wrong = re.findall(r'(<span class="read-num">閱讀數:.*?</span>)', r.text)#每一頁都有一個不知道是什麼的閱讀數:13,且都放在了開頭
    for x, y in zip(titles, visits[::2]): #文章與閱讀數組合
        print(x, ": ",y)
        sum_num.append(int(y))
        # print(len(sum_num))


def thread_process():
    with concurrent.futures.ThreadPoolExecutor(max_workers=pages) as executor:  # 創建線程池
        executor.map(get_info, urls)
    print(sum(sum_num))

def error_process():
    pool=Pool(2)
    pool.map(get_info,urls)
    pool.close()
    pool.join()
    print(len(sum_num))

if __name__ == '__main__':
    # usual()#一頁頁運行
    thread_process()#線程池
    # error_process()#錯誤的進程池
    print('total time :',time.time()-starttime)

 

四、分析

       這裏普通方法usual的運行時間在0.8s左右,線程池的方法在0.6s左右,有一定加速效果。但由於本身運行時間不長,以及還有線程的時間開銷,所以加速沒有那麼明顯。

 

五、思考

    在寫進程池的時候,必須要把它寫到  if __name__ == '__main__':或其他函數裏面,不能在最外面建進程池。不然非常奇怪,好像py文件一直在執行,又執行有誤。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章