在原版的基础上,添加了进程池,进程锁,以及数据处理分析小实验
首先分析一下在整个程序的哪个进程中,可以实现多进程提高运行效率,首先爬虫程序会先去拿网站的url,然后对url内的json数据进行处理,之后写入文件
所以在整个过程中,我们可以分别让多个进程去拿url拿response的数据,然后分批处理,写入文件中
首先找到我们遍历url的方法中,把方法体进一步封装到scraping_page_data()方法里
并创建线程池:
defdynamtic_scraping_data(page,headers,fileName):pool=multiprocessing.Pool()#进程池lock=multiprocessing.Manager().Lock()#加一个写锁,确保每页的内容在一起'''ifcode'lock=multiprocessing.Lock()'报错:RuntimeError:Lockobjectsshouldonlybesharedbetweenprocessesthroughinheritance'''foriinrange(page):pool.apply_async(scraping_page_data,(lock,i,headers,fileName))pool.close()pool.join()
这里有几个知识点,首先,对于进程池,会根据主机的系统资源,去分配到底有多少条进程
其次如果不调用join方法,子进程会在主进程结束后继续执行,因为后面有数据分析的功能所以我们条用了join方法,让主进程去等待其他的进程
在进程池中调用apply方法去实现每个子进程要执行的方法apply(method_name,(parameters....))
由于我们想要确保,每次拿到一条url,希望当前进程写入的数据的时候,不被其他进程打扰,我们给写入操作加上进程锁
在写的过程中,我发现如果按照创建一条进程的方法也创建进程锁,会报错,原因是用了进程池,而进程池中的进程并不是由当前同一个父进程创建的原因。
查了下资料,multiprocessing.Manager()返回的manager对象控制了一个server进程,可用于多进程之间的安全通信。
写入文件加锁:
defwrite_csv_rows(lock,path,headers,rows):#writeonerowlock.acquire()withopen(path,'a',encoding='gb18030',newline='')asf:f_csv=csv.DictWriter(f,headers)#如果写入数据为字典,则写入一行,否则写入多行iftype(rows)==type({}):f_csv.writerow(rows)else:f_csv.writerows(rows)lock.release()
这样我们会发现进程池会调用多个进程资源,并执行写入操作,互不干扰,但是这样就遗留下来了问题,总结会提到
接下来拿到数据后,我们读取cvs中的price,分析价格低于200¥的图书的价位分布,为了生成统计图,我们引入matplotlib包
首先处理脏数据:
这里提到一点在基础版忽视的问题,我们发现在读入的数据中,有一部分的图书名字会加上‘团购:’的无用信息,所以我们要在存入磁盘前删掉它
fordatainresult['Data']:bookinfo['bookName']=data['book_name'].replace('团购:','')bookinfo['price']=data['group_price']bookinfo['iconLink']=data['group_image']write_csv_rows(lock,fileName,headers,bookinfo)接下来就是处理数据的部分了:先拿到cvs文件中price的值,也就是栏位为1
然后遍历删掉title=>price后,我们发现在price的数据中,有的值为'39.9-159.9'格式,对于这种格式的数据,我们求平均值来计算
最后我们把价格低于200的数据存入数组,用于生成柱状图
#matplotlibviews,统计价格小于200的图书价格分布prices=[]price=read_csv_column(csv_filename,1)foriinrange(len(price)-1):if(price[i+1].find('-')!=-1):splitprice=price[i+1].split('-')average_price=(float(splitprice[0])+float(splitprice[1]))/2if(average_price<200):prices.append(average_price)elif(float(price[i+1])<200):prices.append(float(price[i+1]))plt.hist(prices,bins=10)print(prices)plt.show()调用show()方法后生成统计图
当然matplotlib为我们提供了很多方法去美化,以及生成点状图,线图等等,在这里仅仅是测试功能,自我学习,不多测试了
小实验全部代码:
总结:
在测试爬虫程序的过程中,发现了一个非常关键的问题,也就是网络稳定对爬虫程序的影响。如果读取页数相当庞大,在抓取过程中,
会有某个链接出现相应失败的情况,但是本程序默认的操作是跳过继续执行。所以这样就有可能导致数据丢失,以至于分析数据结果
不准确,google后,发现消息队列是一个很好的解决方法,把每次拿到的url存入队列中,每个进程作为消费者去pop队列中的url,如
果拿数据失败,则从新把url指令存入队列,等待重新执行。有机会试着实现一下