爬虫-python -(8) 多线程与多进程操作以及线程池 异步操作

本文介绍了如何通过异步操作来提高Python爬虫的效率,包括多线程、多进程的实现方式,以及线程池和进程池的概念和应用。还提供了一个线程池实例,用于爬取并保存新发地菜价数据。通过这些技术,可以有效提升爬虫的并发能力,减少爬取时间。
摘要由CSDN通过智能技术生成

1.通过异步操作提高爬虫效率

一般爬虫过程为,请求网页-响应请求-从响应中提取数据-保存有用数据,每次都是这样,如果有大量的网站,重复这样操作肯定很慢。
现在可以通过异步操作,提高爬虫的效率。
这里异步操作可以是多线程,多进程以及协程。
这里有存在两个容易混淆的定义,线程和进程
进程是资源单位(某几块地方) 每个进程必须包含至少一个线程
线程是执行单位 (这块地方工作的人)

2.多线程

创建多线程有两种方法,第一种是直接调用函数thread函数,将需要创建的新进程函数和传参放入,然后start,就可以开始执行。

import threading
 def fun (n,m):
     for i in range(n):
         print('子线程',i+m)

 if __name__=='__main__':
     n=100
     m=200
     T=threading.Thread(target=fun,args=(n,m))  #创建一个新的线程并安排任务  通过args传递参数 只剩下一个参数时候后面的逗号必须要
     T.start()                                 #可以开始工作,后面要看cpu是否给他分配
     for i in range(n):
         print('主线程',i)

也可以通过类的继承方法,创建一个继承thread的类,直接调用新类,即可。

class MyThread(threading.Thread):
    def __init__(self,funs,args):
        threading.Thread.__init__(self)  #不要忘记调用Thread的初始化方法
        self.funs=funs
        self.args=args
    def run(self):   #这里需要写成固定的run  因为线程执行时候被执行的就是run
        self.funs(*self.args)

def fun(m):        
    for i in range(100):
        print('子线程1',i+m)
def fun1(m):        
    for i in range(m):
        print('子线程2',i)

if __name__ == '__main__':
    T=MyThread(fun,(100,))  #继承thread类的方法
    T2=MyThread(fun1,(200,))  #继承thread类的方法
    T.start()
    T2.start()
    T.join()  #等待t这个线程执行完毕 再执行下面的线程

    for i in range(100):
        print('主线程',i)

3.多进程

多进程和多线程使用过程很相似,但是里面实现过程,完全不同,因为多进程用的比较少,所以就简单了解下

from multiprocessing import Process

def fun(m):
    for i in range(m):
        print('子进程',i)

if __name__=='__main__':
    P=Process(target=fun,args=(100,))
    P1=Process(target=fun,args=(100,))    
    P.start()
    P1.start()
    for i in range(100):
        print('主进程',i)

4.线程池与进程池

由于用的时候可能要创建很多线程,但是要不浪费线程,要重复使用这些线程,就出现了线程池和进程池的概念。

from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor

def fun(name):
    for i in range(100):
        print(name,i)

if __name__=='__main__':
    with ThreadPoolExecutor(5) as T:      #线程池创建5个线程
        for i in range(20):               #给线程池20个任务
            T.submit(fun,name=f'线程{i}')  #提交任务
    #必须等以上结束下面才会执行  with 里面相当于有start 和join的功能
    print('over!!')

5.线程池实例-新发地菜价保存

新发地菜价

import requests
from concurrent.futures import ThreadPoolExecutor
import csv
import time
from collections import deque

url = 'http://www.xinfadi.com.cn/getPriceData.html'
data_queue = deque([])

def getPriceData(pagenumber):
    data={
        'limit': 20,
        'current': pagenumber}
    reap = requests.post(url= url,data=data)
    reapcontent= reap.json()['list']
    reap.close()
    data_queue.append(['PG-%d'%pagenumber])
    for i in reapcontent:
        data_queue.append([i['prodCat'],i['prodName'],i['avgPrice'],i['highPrice'],i['lowPrice'],i['place'],i['pubDate']])
    #print(pagenumber,'over!!') 

def dataStorage():  #保存数据
    with open('新发地菜价.csv',mode='w+',encoding='utf-8',newline='') as f:  #保存文件
        csvWrite = csv.writer(f)
        while True:
            try:
                dataer = data_queue.popleft()
                csvWrite.writerow(dataer)  #将结果保存到csv
            except:
                print('数据已经全部保存!')
                break


if __name__=='__main__':
    time_start = time.time() #开始计时
    with ThreadPoolExecutor(20) as t:   #进程池里面进程数量为10
        for i in range(200):            #给进程池100个任务 打开200个
            t.submit(getPriceData,pagenumber=i+1)
    dataStorage()
    print('over!!')
    time_end = time.time()    #结束计时
    time_c= time_end - time_start   #运行所花时间
    print('time cost', time_c, 's')

在这里插入图片描述

6.总结

今天的多线程其实用途蛮大,对其他语言也有启发性,挺好的,今天就看这么多把。明天继续。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值