文章目录

  • 1.通过异步操作提高爬虫效率
  • 2.多线程
  • 3.多进程
  • 4.线程池与进程池
  • 5.线程池实例-新发地菜价保存
  • 6.总结


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')

python线程并发执行 python多线程并发请求_python

6.总结

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