关于python爬取大量数据使用线程池提升效率的问题

需求是这样的,有一份包含上万条存放图片地址数据的xlsx文件,对该文件地址中的图片进行保存到本地。

 部分数据

先是通过pandas模块读取xlsx表格(表格需同python源文件同目录),指定工作簿,由于存在大量重复数据,所以通过 df.drop_duplicates(subset为指定列,keep为首个(first)或尾个(last)保留,inplace=False为删除所有重复项) 来指定删除某列的重复数据,将整理过的数据追加到空列表(imageUrlList)中,备用。

表格中,最下面这里是sheet_name。

!!! 难点 !!!

拿到所有的下载链接后,由于要使用线程池提高效率,所以将上文得到的列表(包含着图片地址链接)进行拆分,一分为多,把一个列表拆分为包含多个子列表的父列表(图片中的moreImageUrlList)

通过该方法拆分,方法来源于https://blog.csdn.net/weixin_45300868/article/details/102808109

 将拆分后的子列表存在父列表中,然后调用线程池,创建多个线程,通过for遍历父列表,得到每一个子列表(子列表包含着一批一批的图片地址链接),然后通过ThreadPoolExecutor(本文简称t)的submit()的方法,来多线程执行任务。submit()的第一个参数是被执行函数的名,第二个参数是给被执行函数携带的参数,还有第三个参数,表示以关键字参数的形式为被执行函数传参(本文未涉及)。

 被执行的函数

 静候大功告成,作者由于时间关系,并没有将程序执行到底。

下面上源代码:

import threading
import requests
import pandas as pd
import os
import time
import random
from concurrent.futures import ThreadPoolExecutor

def pathIsExistence(yourPath):  #判断保存路径是否存在
    flag = os.path.exists(yourPath)
    if not flag:
        os.mkdir(yourPath)

def getWorkbook(workbook,sheet_name): #读取xlsx文件,workbook是文件名(xxx.xlsx),sheet_name是工作表
    df = pd.read_excel(f"{workbook}",sheet_name=f"{sheet_name}")
    df = df.drop_duplicates(subset='ImageUrl', keep='first', inplace=False) #subset为指定列,keep为保留首个或尾个,inplace=False为删除所有重复项
    imageUrl = []
    for item in df['ImageUrl']:
        imageUrl.append(item)
    return imageUrl #将读取到的url保存在列表里,并返回

def getImageInNetwork(imageUrl):
    global pic_count    #把main里的pic_count当做全局变量使用
    header = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36'
    }
    rand = 0  # 程序随机休眠,防止被检测到访问过快
    for url in imageUrl:
        rand = round(random.random(),2) #得到随机休眠小数(保留两位)
        response = requests.get(url=url,headers=header).content #获取图片二进制信息
        with open(f"pic_dir/{pic_count}.png",mode='wb') as fp:
            fp.write(response)  #写入图片
            print("第",pic_count,"张图片保存完毕,","当前活跃线程数量:", threading.active_count(),"本次休眠时长",rand,"秒,请稍后...")
            fp.close()
            pic_count+=1
        time.sleep(rand)    #开始休眠

if __name__ == '__main__':
    pic_count = 0  # 用来记录多少张以及命名图片
    pathIsExistence(yourPath='pic_dir') #pic_dir是默认路径
    print("路径确认完毕!")
    print("即将获取url信息,请稍后...")
    imageUrlList = getWorkbook('data.xlsx', '测试集合')
    # print(len(imageUrlList))  #列表总个数
    moreImageUrlList = []
    n = 1000    #每1000个为一个列表数据
    for i in range(0, len(imageUrlList),n):
        moreImageUrlList.append(imageUrlList[i:i+n])    #将imageUrlList拆分称多个列表,便于使用多线程
    # print(len(moreImageUrlList)) 看大列表的长度,用总个数除以n,结果为大列表长度,验证程序是否存在问题
    print("url获取完毕,即将进行图片保存,请稍后...")
    with ThreadPoolExecutor(len(moreImageUrlList)) as t:   #大列表(moreImageUrlList)包含多少个子列表,就创建多少线程,也可以自定义
        for item in moreImageUrlList:  #遍历大列表中的每一个子列表
            t.submit(getImageInNetwork, item)   #多线程执行getImageInNetwork函数任务,imageUrlList是该函数的参数
    print("OVER")

# 该python文件是多线程模式
# 思路是先创建一个存放图片的路径,
# 访问xlsx文件得到所有的图片地址,将地址存放在列表中,地址是字符串类型
# 由于列表的数据太多,程序执行效率慢,所以将列表拆分为多个列表,
# 将被拆分的多个列表存放在moreImageUrlList列表中,moreImageUrlList包含多个被拆分的列表,每个被拆分的列表包含多个图片地址
# 通过线程池批量任务执行request发送请求,进行保存
# 图片以顺序数字命名

截图(方便查看行号):

看一下运行效果吧

 用线程池还是很快滴

说一下遇到的主要问题吧

1.调用线程池执行任务的时候,必须把包含所有图片地址链接的列表拆称多个子列表,才能用线程正常调用。否则只用一个包含所有地址链接的列表,用多线程调用它,就会产生多个线程执行同一任务的问题,多个线程请求同一张图片下载地址,造成资源浪费,保存效率低下,保存多张重复图片的问题。

2.线程池创建好后,需要用for循环为线程分配任务,否则会产生即使多个线程创建好,也没有被调用的问题。

3.用for循环为多线程分配任务时,for循环次数最好大于等于创建的线程个数。

 注:部分库文件需要预先下载才能使用

通过设置的方法

 

也可以通过终端pip的方法(由于作者并不熟练,所以就不演示了)。 

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值