一.背景描述
在访问量上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文件一直在执行,又执行有误。