爬虫(二)——使用多线程的方式爬取新版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文件一直在执行,又执行有误。

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