爬虫日常练习-艾图网多页面图片爬取

news2024/11/25 11:38:47

文章目录

  • 前言
  • 页面分析
  • 设计代码

前言

欢迎回来兄弟们,想必各位都已经过了一遍上篇文章了吧,没过的兄弟们可以移步上篇文章指点一二,链接: 爬虫日常练习-艾图网单页面图片爬取
感谢xdm
在这里插入图片描述

昨天的内容想必已经挑起了大家的胃口,我们趁热打铁。一个页面上就那么一点图片,光下载这么点多没有意思。我们直接把所有页面图片全部下载下来

在这里插入图片描述

页面分析

书接上文,我们对页面再次进行分析,艾图网链接:艾图网

在这里插入图片描述

我们可以看到在页面的最底部有一个页面条,一共有24页内容,每一页图片链接的获取方式都和第一页相同。我们不经设想,如果在这里获取到每一个页面的url,并循环请求获取图片链接,那么就可以将所有图片下载下来

在这里插入图片描述在这里插入图片描述
我们观察二三两个页面,发现在顶部的url只有一处发生了变化,index_后面的参数会跟着页面变化

在这里插入图片描述
观察第一页发现url也可以更改为这种格式

在这里插入图片描述

并且检查页面发现并没有发现页面的url,这个时候直接大胆下判断,通过改变index_后的参数来切换页面

设计代码

我们只需要在前篇文章的代码上小小的修改这么一两处就可以实现该功能

if __name__ == '__main__':
    for i in range(1, 25):
        url = f"https://www.iituku.com/lvyou/index_{i}.html?sort=0"
        pic_url_list = get_pic_url(url)
        download(pic_url_list)

就只需要加上那么一个循环,为了规范,我们在目录下创建一个新的文件夹,并更改下载路径。

def download(list):
    for src in list:
        resp = requests.get(src)
        pic_name = src.split('/')[-1]
        with open('./aitu_pic/'+pic_name, mode='wb') as f:
            f.write(resp.content)
        print('下载完成')

为了查看下载速度,我们再添加time模块

if __name__ == '__main__':
    start_time = time.time()
    for i in range(1, 25):
        url = f"https://www.iituku.com/lvyou/index_{i}.html?sort=0"
        pic_url_list = get_pic_url(url)
        download(pic_url_list)
    end_time = time.time()
    print(end_time-start_time)

运行结果展示如下
在这里插入图片描述在这里插入图片描述在这里插入图片描述

我们可以看到在一分钟以内已经全部下载好了旅游模块的所有图片。这个速度相对于手工一张一张的下载可谓是天差地别。可老哥们觉得这就是它的极限了吗?
在这里插入图片描述
不不不,太小看我们啦xd。接下来就不得不提一嘴线程与协程了,但是我现在不说,哎嘿,就是玩。驴还得休息呢,人不得睡觉。明天在更,拜拜兄弟们

在这里插入图片描述
哈哈哈哈开个玩笑xdm,咋可是实实在在的老实人
言归正传,如何去提升这类爬虫的运行速度呢?这个时候就要引入线程与进程的概念了
在这里插入图片描述我们可以打开任务浏览器,可以看到我们的电脑正在运行多个进程。如果在这里详细介绍的话占用的篇幅过多,后期可以新开一个模块​介绍,这里不再多说。只简单的阐述他们的关系。

进程是操作系统资源分配的基本单位。而线程是处理器任务调度的执行的基本单位。一个进程可以包含多个进程。细心的同学肯定能想到,通过多个进程调用系统资源肯定不如通过多个线程调用进程的资源来的迅速。我们可以通过本次实验测试一下

首先第一步就是先调用需要的进程池与线程池,以及可以在进程间传递参数的Queue队列

from multiprocessing import Process,Queue
from concurrent.futures import ThreadPoolExecutor

我们首先确立一个基本思路。设立两个进程,一个用来获取页面所有的图片链接,一个用来下载图片
。根据这个思路来设计代码

if __name__ == '__main__':
    start_time = time.time()
    q = Queue()
    p1 = Process(target=get_pic_url, args=(q,)) #进程一用来获取图片链接
    p2 = Process(target=download, args=(q,))  # 进程二用来下载图片

然后对get_pic_url函数做些更改

def get_pic_url(q):
    for i in range(1, 25):
        url = f"https://www.iituku.com/lvyou/index_{i}.html?sort=0"
        resp = requests.get(url)
        resp.encoding = 'utf-8'
        tree = etree.HTML(resp.text)
        pic_url_string = tree.xpath('//html/body/script[2]/text()')[0]
        obj = re.compile(r'var imagesarr=\"(.*?)\";')
        data = obj.findall(pic_url_string)[0]
        data = str(data).replace('"', '')
        data_list = data.split('}')
        # pic_url_list = []
        for li in data_list:
            http = re.findall(r'picture:(.*?)/nu', li)
            if not http:
                continue
            else:
                http_str = http[0]
            http = http_str.replace('\\', '')
            # pic_url_list.append(http)
            q.put(http)
            print(f'{http}已加载队列')

我们可以看到将页面网址的重要参数改为循环可变参数,借此完成不同页面数据的爬取。并且采用了multiprocessing的queue队列来完成不同进程之间的数据传递,这个时最重要的一步,我们会通过它来向专门下载图片的进程传递图片链接。

接着编写一个线程池来分散流量

def download_pic(q):
    with ThreadPoolExecutor(10) as t:
        while True:
            if q.empty(): # 判断队列中是否有数值,没有就跳出循环
                break
            else:
                src = q.get()
                t.submit(download,src)

以及对download函数做一些修改

def download(src):
        resp = requests.get(src)
        pic_name = src.split('/')[-1]
        with open('./aitu_pic/'+pic_name, mode='wb') as f: # 在文件夹下新建aitu_pic文件夹存储图片文件
            f.write(resp.content)
        print(f'{pic_name}下载完成')

至此大部分代码全部编写完成。但心急的小伙伴如果这个时候立马运行的话。估计会受到不小的打击。
在这里插入图片描述
包括我本人也在这里耗费了数个小时才一点一点实验出来,虽然不难,但很恶心人。在这里无私分享给大家,希望大家不用在额外花费这么多时间

在这里插入图片描述
大家可以注意看我的代码,在p1启动后,p2随之启动。理想中的状态应该是下面的网址加载到队列后,p2会立马提取到队列中的链接并通过线程池进行下载操作。然而我们在运行栏翻了半天也只有加载队列信息而没有下载提示。这就很伤脑筋了,忙活半天只有链接没有下载。
在这里插入图片描述
那么为什么我们编写的程序没有按照想要的效果运行呢,查阅半天也找不到为什么,一开始怀疑是不是queue.get的阻塞导致进程死锁,排查了一下也没找出问题。我就自我揣摩了一下,怀疑原因可能很简单,因为进程是异步运行的。大家可以看下面的一行代码

            if q.empty():
                break

这是一个简单的判断,用来判断队列里面是否有数值。我们的本意是要用它来做进程的结束判断,但是在这里反而成了一个巨大的bug。由于两个进程是异步运行的。而由于p1的代码要实现的步骤较多,导致在p1还没有进行到向队列传输数据时,p2已经开始向q队列请求。这就导致了p2直接跳过了线程池向download函数发送请求循环。最后p1只能不断的向q队列传输参数。而p2早就停止了对q的请求,根本就不会发送下载请求。因此我们需要错开p1与p2。在p1启动时,强制休眠一段时间在启动p2.

    p1.start()
    time.sleep(4)
    p2.start()
    p1.join()
    p2.join()

我们再来重新看下运行效果
在这里插入图片描述可以看到,我们只用了11秒的时间就系在了一个网站某模块的全部图片​
在这里插入图片描述先把​完整代码献给xdm

import time
import requests
from lxml import etree
import re
from multiprocessing import Process, Queue
from concurrent.futures import ThreadPoolExecutor


def get_pic_url(q):
    for i in range(1, 25):
        url = f"https://www.iituku.com/lvyou/index_{i}.html?sort=0"
        resp = requests.get(url)
        resp.encoding = 'utf-8'
        tree = etree.HTML(resp.text)
        pic_url_string = tree.xpath('//html/body/script[2]/text()')[0]
        obj = re.compile(r'var imagesarr=\"(.*?)\";')
        data = obj.findall(pic_url_string)[0]
        data = str(data).replace('"', '')
        data_list = data.split('}')
        # pic_url_list = []
        for li in data_list:
            http = re.findall(r'picture:(.*?)/nu', li)
            if not http:
                continue
            else:
                http_str = http[0]
            http = http_str.replace('\\', '')
            # pic_url_list.append(http)
            q.put(http)
            print(f'{http}已加载队列')


def download(src):
        resp = requests.get(src)
        pic_name = src.split('/')[-1]
        with open('./aitu_pic/'+pic_name, mode='wb') as f:
            f.write(resp.content)
        print(f'{pic_name}下载完成')


def download_pic(q):
    with ThreadPoolExecutor(10) as t:
        while True:
            if not q.empty():
                src = q.get()
                t.submit(download, src)
            else:
                break


if __name__ == '__main__':
    q = Queue()
    p1 = Process(target=get_pic_url, args=(q,)) # 进程一用来获取图片链接
    p2 = Process(target=download_pic, args=(q,))  # 进程二用来下载图片
    start_time = time.time()
    p1.start()
    time.sleep(4)
    p2.start()
    p1.join()
    p2.join()
    end_time = time.time()
    print(end_time-start_time)

相比较与上面下载图片的一分钟,多进程与线程无疑带来了巨大的速度提升,这11秒期间甚至包括了我们设置的4秒强制休眠。时间多的朋友也可以试着把p1省略掉,尝试单进程多线程会不会提高速度。下次内容暂定为利用协程下载图片,争取吃透这一块内容。就说这么多,拜拜兄弟们​
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/429604.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自动化测试(一):自动化测试理论

什么是自动化测试: 自动化测试是把以人为驱动的测试行为转化为机器执行的一种过程。通常,在设计了测试用例并通过评审之后,由测试人员根据测试用例中描述的规程一步步执行测试,得到实际结果与期望结果的比较。在此过程中&#xf…

【前端之旅】快速上手ElementUI

一名软件工程专业学生的前端之旅,记录自己对三件套(HTML、CSS、JavaScript)、Jquery、Ajax、Axios、Bootstrap、Node.js、Vue、小程序开发(Uniapp)以及各种UI组件库、前端框架的学习。 【前端之旅】Web基础与开发工具 【前端之旅】手把手教你安装VS Code并附上超实用插件…

指针在C++中的应用及顺序表结构体中的应用

目录 一.指针的概要 指针 一级指针 二级指针 二、一个生动形象的例子 三、总结 四、顺序表结构体中的应用 一.指针的概要 指针 是一种数据类型,它保存着一个变量的内存地址。指针可以通过间接引用操作符 * 来访问所指向的变量。在 C 中,指针通常…

【AI绘画】探索艺术与技术的融合:AI绘画的新前景

AI绘画的新前景1.AI绘画是把双刃剑2.AI绘画的应用场景3.AI绘画的技术4.结语随着人工智能(AI)技术的快速发展,AI绘画成为了艺术创作领域中备受关注的话题。传统的艺术创作方式受到了AI技术的冲击,为艺术家和观众带来了全新的体验和…

socket 通信(一)

一 基本示例 #include <stdio.h> #include <sys/socket.h> // socket() #include <arpa/inet.h> // inet_addr() #include <netinet/in.h> // sockaddr_in{} INADDR_ANY #include <unistd.h> // close() #include <errno.h> …

php7类型约束,严格模式

在PHP7之前&#xff0c;函数和类方法不需要声明变量类型 &#xff0c;任何数据都可以被传递和返回&#xff0c;导致几乎大部分的调用操作都要判断返回的数据类型是否合格。 为了解决这个问题&#xff0c;PHP7引入了类型声明。 目前有两类变量可以声明类型&#xff1a; 形参&a…

2023年贵州建筑八大员考试时间报名时间是什么时间呢?甘建二

2023年贵州建筑八大员考试时间报名时间是什么时候开始呢&#xff1f;贵州建筑八大员考试时间是什么时候呢&#xff1f;什么时候考试呢&#xff1f; 建筑八大员主要是用于企业资质、招投标、检查使用&#xff0c;目前很多企业急需这个证书&#xff0c;关于建筑八大员报名时间和考…

网络拥塞控制,对越远的流量越宽容

考虑下面的网络传输场景&#xff1a; ​ S1&#xff0c;S2&#xff0c;S3&#xff0c;S4 向 D 方向发送&#xff0c;R4 发生拥塞。R4 必须丢弃一些数据进行疏导反馈拥塞信号&#xff0c;否则谁也过不去。 优先丢掉离得最近的 S4 的数据&#xff0c;其次依次丢 S3&#xff0c;S2…

051:cesium加载mapbox的多种Style形式地图

第051个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中加载mapbox的多种Style形式地图,包括dark-v11,streets-v12,navigation-night-v1,outdoors-v12,satellite-v9,satellite-streets-v12,light-v10。 直接复制下面的 vue+cesium源代码,操作2分钟即可运行实…

如何让心情保持平静?100多条禅修心法

静的层次和阶段 静首先是不要去争&#xff0c;没有任何争的心&#xff0c;没有任何杂念心。静有几个层次阶段&#xff1a; ⒈. 自己的心情相对于自己平静&#xff0c;是平静的第一个阶段。 ⒉. 第二个平静的阶段是&#xff1a;别人觉得你很静&#xff0c;自己也很静&#xf…

ArduPilot开源飞控系统之简单介绍

ArduPilot开源飞控系统之简单介绍1. 源由2. 了解&阅读2.1 ArduPilot历史2.2 关于GPLv32.3 ArduPilot系统组成2.4 ArduPilot代码结构3. 后续4. 参考资料ArduPilot是一个可信赖的自动驾驶系统&#xff0c;为人们带来便利。为此&#xff0c;提供了一套全面的工具&#xff0c;几…

人人看得懂的AI教程

人人看得懂的AI教程&#xff0c;从0开始入门AI教程&#xff0c;一步一步AI&#xff0c;人工智能学习笔记 现在写书真的方便&#xff0c;闲来无事写了本从0开始学AI的书籍&#xff0c;哈哈 一、基础知识 1.1 人工智能概览 1.2 机器学习 1.3 深度学习 1.4 数据科学 二、编程知…

Stable Diffusion - API和微服务开发

Stable Diffusion 是一种尖端的开源工具&#xff0c;用于从文本生成图像。 Stable Diffusion Web UI 通过 API 和交互式 UI 打开了许多这些功能。 我们将首先介绍如何使用此 API&#xff0c;然后设置一个示例&#xff0c;将其用作隐私保护微服务以从图像中删除人物。 推荐&…

CoreDNS 性能优化

CoreDNS 作为 Kubernetes 集群的域名解析组件&#xff0c;如果性能不够可能会影响业务&#xff0c;本文介绍几种 CoreDNS 的性能优化手段。合理控制 CoreDNS 副本数考虑以下几种方式:根据集群规模预估 coredns 需要的副本数&#xff0c;直接调整 coredns deployment 的副本数:k…

总结下Spring boot异步执行逻辑的几种方式

文章目录概念实现方式Thread说明Async注解说明线程池CompletableFuture&#xff08;Future及FutureTask&#xff09;创建CompletableFuture异步执行消息队列概念 异步执行模式&#xff1a;是指语句在异步执行模式下&#xff0c;各语句执行结束的顺序与语句执行开始的顺序并不一…

【高项】项目人力资源管理,沟通管理与干系人管理(十大管理)

【高项】项目人力资源管理&#xff0c;沟通管理与干系人管理&#xff08;十大管理&#xff09; 文章目录1、人力资源管理1.1 什么是人力资源管理&#xff1f;1.2 如何进行人力资源管理&#xff1f;&#xff08;过程&#xff09;1.3 人力资源管理工具1.4 人力资源管理文件2、沟通…

自动驾驶BEV感知系列算法整理总结

序论 之前一直做的lidar感知&#xff0c;现在感觉大趋势是多传感器融合&#xff0c;所以博主也在向BEV下的融合框架学习&#xff0c;希望大家后面可以多多交流&#xff0c;下面会分为两类进行介绍&#xff0c;后期的文章会在下面两类中以小标题的形式出现&#xff0c;BEV下的两…

《推荐PlumGPT:一款优秀的聊天机器人》

PlumGPT是一款仿照ChatGPT的聊天机器人&#xff0c;它使用了最先进的自然语言处理技术来实现人机交互。PlumGPT不仅能够进行普通的对话&#xff0c;还可以回答各种问题&#xff0c;提供各种建议和信息。它能够对不同的话题做出详细的回答&#xff0c;例如科技、健康、娱乐、新闻…

[element]element-ui框架下载

⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹 ⭐如果觉得文章写的不错&#xff0c;欢迎点个关注一键三连&#x1f609;有写的不好的地方也欢迎指正&#xff0c;一同进步&#x1f601;…

线程同步与互斥【Linux】

文章目录1. 引入2. 前导概念2.1 同步与异步2.2 互斥与并发2.3 原子性操作2.4 临界资源和临界区临界资源临界区如何管理3. 互斥锁3.1 引入3.2 概念3.3 示例pthread_mutex函数家族用法全局锁局部锁3.4 性能损耗3.5 串行执行3.6 补充4. 互斥锁的实现原理4.1 线程的执行和阻塞4.2 自…