壁纸爬取——协程应用

(协程)壁纸爬取

一、 算法解析

1.1 进入爬取壁纸的网站(表层网页)

彼岸桌面壁纸-二次元 少爬涩图,健康生活!

1.2 获取显示单张壁纸的页面(深层网页)地址

选择网页元素:Ctrl+Shift+C
你学废了吗?学废扣眼珠子,没学废点赞收藏

壁纸爬取——协程应用
问:为什么不是下面那个 “img src=…” 的地址呢?
答:下面的地址的确是一个图片的地址,但是该图片是800450(我们要19201080),是用来显示该页面这个小图的(如下图)
所以你喜欢 还是喜欢 ?你品你细品
壁纸爬取——协程应用

1.3 进入单张壁纸的页面

① 你可以点击 “/desk/22629.htm”
② 在浏览器网址输入栏输入 http://www.netbian.com/desk/22629.htm
(② 这个是重点,因为爬取时我们需要这个完整地址才能访问下面这个页面)

壁纸爬取——协程应用
此时的 “img src=…” 就是我们 壁纸的最终下载地址

1.4 思路总结

  • ① 访问主页 (表层网页)
  • ② 将主页的源代码全部读取出来

    查看源代码: ① 右键→查看网页源代码 ② Ctrl+U

  • ② 通过 正则匹配将读取的网页源代码中 所有 单张壁纸页面的 (深层网页)地址 “/desk/*.htm” 筛选出来,作为字符串对象
  • http://www.netbian.com + “/desk/*.htm” 将两个字符串合并为完整的 url
  • ④ 通过上面完整的网址访问每张壁纸的 深层网页,并将深层网页的源代码读取出来
  • ⑤ 通过 正则匹配1920*1080壁纸 的下载地址,筛选出来
  • ⑥ 访问该壁纸的下载地址,以二进制的形式将数据读取,并以 “*.jpg(.png)” 格式进行写入为本地文件

1.5 思路代码重现

① 以下部分都有标明 重点 与 非重点
重点:核心算法的展现(如何访问网页并通过正则匹配提取所需的url)
​ 非重点:下载功能,显示下载进度,自动为文件名命名
​ ※ 读者看懂重点的核心算法就好了,其余的功能有兴趣自己琢磨
② 建议从 主函数开始食用,不然绝对懵逼
③ 你也可以下载这个小项目自己去Van~Van~
该项目下载地址:
链接:https://pan.baidu.com/s/1SVIMVzmPIEJ7kEsvMpK23Q
提取码:fkgz

1.5.1 工程目录结构

壁纸爬取——协程应用

1.5.2 导入模块以及定义全局变量

最好把 gevent 相关的模块放最前面,以免出现BUG
import gevent
from gevent import monkey
monkey.patch_all()
import requests
import re
from read_write.read_num import read
from read_write.write_num import write

爬取到的图片下载地址存放列表
url_deep_list = []
以下四个变量并非重点,我们关注核心算法
图片文件的名字
name_jpg = 0
允许开始下载的标志
flag_download = 0
gl_url_ok_num = 0
url_num = 0

1.5.3 访问主页(表层网页)提取 深层网页 地址——重点

def get_surface_url(surface_page):
    """表层网页提取出深层网页地址

    :param surface_page: 表层网页
    :return: 返回一个 深层网页 地址列表
"""
    # 通过目标网站,相当于获取了一个 response 这个可操作对象
    response = requests.get(surface_page)

    # TODO .+? 非贪婪匹配 当遇到 .时前面的非贪婪匹配任务结束,接着匹配htm
    url_add = r'/desk/.+?\.htm'

    # response.text 即代表该对象的文本数据,源代码数据
    # 从源代码数据中匹配 url_add 相关的内容,从而生成一个列表
    url_list_old = re.findall(url_add, response.text)

    # print(url_list_old)

    url_list_new = []

    for str_old in url_list_old:
        str_new = 'http://www.netbian.com' + str_old
        url_list_new.append(str_new)

    # print(url_list_new)

    return url_list_new

1.5.4 访问深层网页地址,提取下载地址——重点

def get_deep_url(deep_page):
    """通过深层网页获取 1920*1080 的壁纸地址

    :param deep_page: 深层网页地址
    :return: 返回一个 壁纸 下载地址
"""
    response = requests.get(deep_page)
    # 此页面已经能找到 1920*1080 jpg图片的下载地址了,我们通过正则匹配,分组"()"进行提取
    # ① 首先锁定目标地址的头 /desk/.+?-1920x1080.htm
    # ② 接着再往下匹配的地址,即为最终下载地址
    # 总结:之所以可以分两部分进行锁定匹配,是因为
    #      ① search 是可以通过跨行匹配(不过一旦匹配成功一个,即返回对象)
    #      ② .*? 到

1.5.5 下载图片——非重点

def download_jpg(local_path):
    """通过获取的地址列表下载壁纸

    :param local_path: 下载到的本地位置
"""

    global name_jpg
    global gl_url_ok_num

    # for url in url_list:
    while True:

        # print("开始下载图片...")
        # url = q_url.get()
        if flag_download == 1:
            if url_deep_list:
                url = url_deep_list[0]
                url_deep_list.pop(0)

                # name_jpg += 1

                response = requests.get(url)
                # 请求成功以后,正式开始下载时,才给它 +1 起名字,因为 +1 操作不需要等待,
                # 所以不会切换协程,直接通过 open 已经为文件起好名字了,即使下载等待,
                # 也不会影响已经有名字的文件了
                # 如果 name_jpg += 1 放上面会出现当 协程遇到 requests 时网络请求,在等待时间,会让第二个协程来执行,
                # 这样 name_jpg += 1,这样等待第一个协程开始下载时,名字就会变为 3,
                # 而且极有可能出现各种BUG,经过试验:名字会从3开始,而且最终少了两个文件
                name_jpg += 1

                with open('%s/%d.jpg' % (local_path, name_jpg), 'wb') as ft:
                    ft.write(response.content)

                # print("成功下载一张壁纸...")

                gl_url_ok_num += 1

                if not url_deep_list:
                    # print("全部下载完毕...")
                    break
            else:
                break
                # print(url)
        else:
            print("还没有爬取到地址...无法开始下载")
            gevent.sleep(2.5)
            continue

    # write("%s/num.txt" % local_path, str(name_jpg))

1.5.6 控制爬取的页面(从第几页开始,爬取若干页)——非重点

① 第一页

壁纸爬取——协程应用

② 第二页

壁纸爬取——协程应用

③ 第三页(这回懂了吧,改变 url 的部分参数便可以访问不同页面)

壁纸爬取——协程应用
def crawl_url(page_num, page_start):
    """爬取出一个个下载地址,并加入列表 url_deep_list 当中

    :param page_num: 需要爬取的网页数
    :param page_start: 从第几个页面开始爬取
"""
    global flag_download
    global url_num

    print("开始爬取地址...")

    j = 0
    flag = 0
    while j < page_num:

        if flag == 0:
            if page_start == 1:
                url_list_surface = get_surface_url('http://www.netbian.com/erciyuan/index.htm')
            else:
                url_list_surface = get_surface_url('http://www.netbian.com/erciyuan/index_%d.htm' % (page_start + j))
            flag = 1
        else:
            url_list_surface = get_surface_url('http://www.netbian.com/erciyuan/index_%d.htm' % (page_start + j))

        for url_surface in url_list_surface:

            url_deep = get_deep_url(url_surface)
            # TODO url_deep 是单个图片的最终的下载地址,把它存入队列
            # print(url_deep)
            url_deep_list.append(url_deep)

        print()
        j += 1
    # 等所有网页爬取完毕,才开始下载,是为了能让下载进度能读取到总下载图片数量
    url_num = len(url_deep_list)
    flag_download = 1

    print("爬取地址完毕...")
    # return url_list_deep

1.5.7 显示下载进度(附加功能)——非重点

def download_rate():

    global url_num
    global gl_url_ok_num

    while True:
        if flag_download == 1:

            url_ok_num = gl_url_ok_num

            print("\r下载进度: %.2f%%---[%d/%d]..." %
                  (url_ok_num * 100 / url_num, url_ok_num, url_num), end="")

            gevent.sleep(0.5)

            if url_ok_num == url_num:
                print("\n全部下载完毕...")
                break
        else:
            print("还未爬取完地址...无法显示下载进度")
            gevent.sleep(3)
            continue

1.5.8 主函数(看不懂就从主函数看起)

def craw_jpg():
"""
    主函数
"""

    global name_jpg

    page_start = int(input("你希望从第几页开始爬取:"))
    page_num = int(input("请输入你要爬取的页数:"))
    local_path = str(input("请输入你要保存的地址:"))

    num_txt_path = local_path + "/num.txt"

    # TODO 不能通过 windows 来创建 num.txt 因为这样创建的 utf-8 文本不纯净,是BOM编码格式的,
    #  前面会有几个字符,导致 int() 转换出现问题。所以应该让 python 来自行创建

    try:  # 先尝试打开 num.txt ,无需赋值,只是为了了解是否存在 num.txt 这个文件,下面还是会进行读取的赋值的
        read(num_txt_path)

    # 如果 发现异常——没有num.txt这个文件,则会 write 自动创建文件并写入数据
    except FileNotFoundError:
        print("没有发现命名文件[num.txt] ...")
        name_jpg = input("请输入你要为文件命名的开头:")
        write(num_txt_path, str(name_jpg))
    else:
        print(" num.txt 文件已存在...")
        print()
    finally:
        name_jpg = int(read(num_txt_path)) - 1

    # C:/Users/Administrator/Desktop/爬取图片
    # name_jpg = int(read(num_txt_path))
    print(name_jpg)

    # TODO 生成5个协程 1个爬取网址和3个下载,1个显示下载进度
    gevent.joinall([
        gevent.spawn(crawl_url, page_num, page_start),
        gevent.spawn(download_jpg, local_path),
        gevent.spawn(download_jpg, local_path),
        gevent.spawn(download_jpg, local_path),
        gevent.spawn(download_rate,),
    ])

    # 是为了使下一次下载的名字是正常顺序(非重点)
    write("%s/num.txt" % local_path, str(name_jpg + 1))

if __name__ == "__main__":

    # 爬取网址: http://www.netbian.com/erciyuan/index.htm
    # 下载到本地地址: C:/Users/Administrator/Desktop/爬取图片
    craw_jpg()

1.5.9 这是txt文件写入与读取部分(用于为图片名称自动命名)——非重点

1.5.9.1 txt 读取
def read(file):

    # 打开文件
    num_file = open(file, "r", encoding="Utf-8")

    # 现将指针放回开头
    num_file.seek(0, 0)

    num_read = num_file.readline()

    # 关闭文件
    num_file.close()

    return num_read

if __name__ == "__main__":
    num = read("num.txt")
    print(num)
    print()
    print("正在测试 read_num 模块...")
1.5.9.2 txt 写入
def write(file, i):

    # 打开文件
    num_file = open(file, "w", encoding="Utf-8")

    # 现将指针放回开头
    num_file.seek(0, 0)

    num_file.write(i)

    # 关闭文件
    num_file.close()

if __name__ == "__main__":
    write("num.txt", str(2))

    print("正在测试 read_num 模块...")

程序运行结果:

你希望从第几页开始爬取:1
请输入你要爬取的页数:2
请输入你要保存的地址:C:/Users/Administrator/Desktop/爬取图片
没有发现命名文件[num.txt] ...

请输入你要为文件命名的开头:1
0
开始爬取地址...

还没有爬取到地址...无法开始下载
还没有爬取到地址...无法开始下载
还没有爬取到地址...无法开始下载
还未爬取完地址...无法显示下载进度

爬取地址完毕...

下载进度: 100.00%---[34/34]...

全部下载完毕...

二、总结

一、算法思想

  1. 每个表层网页有若干张图片,是我们最终需要下载的图片(19201080)的 缩小版用于展示的小图片。*
  2. 但我们可以通过爬取该页面的这些图片的 链接url_suface,通过 get_deep_url( ) 进入 深层页面,深层页面中间有个大图
  3. 再通过爬取该页面的 最终下载网址得到一个个的 url_deep,最终将这些网址放入 url_deep_list 列表中
  4. 最后通过 url_deep_list 一个个取出下载

二、注意事项

  1. 先将所有地址爬取完毕再进行下载, 方便统计需下载的总图片数量,用于显示下载进度的图片总数
  2. 使用协程进行下载,要注意合理分配时间给用于 下载的协程 和用于 显示下载进度的协程

    因为协程的时间分配是很智能的,由于下载图片是需要请求且等待的过程,协程会自动将这些多余的时间分配给其他协程, 这样显示下载进度的协程总是能分配到很多时间,导致用于下载协程出现阻塞, 从而导致下载速度极慢!

  3. 所以我们要让 显示下载进度的协程 每隔0.5s显示一次 (gevent.sleep(0.5)),那么这0.5s就可以分配给 用于下载的协程

最后再附上下载地址:
链接:https://pan.baidu.com/s/1SVIMVzmPIEJ7kEsvMpK23Q
提取码:fkgz
算是简单的项目了(算法很容易),只是附加的功能看起来可能有点复杂,其实也有一大部分代码都是为了防止bug
有不足之处,恳请大佬指出orz!
或有看不懂的朋友也可以评论,我会尽快恢复的啦~ღ( ´・ᴗ・` )比心

Original: https://www.cnblogs.com/fry-hell/p/12859562.html
Author: 油炸地狱
Title: 壁纸爬取——协程应用

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/612512/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 2020年12月-第02阶段-前端基础-CSS Day04

    浮动(float) *记忆 能够说出 CSS 的布局的三种机制 *理解 能够说出普通流在布局中的特点能够说出我们为什么用浮动能够说出我们为什么要清除浮动 *应用 能够利用浮动完成导…

    Linux 2023年6月8日
    083
  • sql server的简单分页

    — 显示前条数据 select top(4) * from students; –pageSize: 每页显示的条数 –pageNow: 当前页…

    Linux 2023年6月7日
    0117
  • redis压力测试【转】

    本文转自: https://segmentfault.com/a/1190000015571891 redis自带的redis-benchmark工具 Redis 自带了一个叫re…

    Linux 2023年5月28日
    072
  • 使用 shell 脚本自动对比两个安装目录并生成差异补丁包

    问题的提出 公司各个业务线的安装包小则几十兆、大则几百兆,使用自建的升级系统向全国百万级用户下发新版本时,流量耗费相当惊人。有时新版本仅仅改了几个 dll ,总变更量不过几十 K …

    Linux 2023年6月6日
    082
  • 记录一次shell脚本环境全局变量在函数内部生效问题

    背景 计划核对内网IP的使用情况,所以写了个小脚本扫描有哪些IP还在使用。执行脚本过程中发现函数中一直获取不到变量的值,排查后将结论记录下来。 问题现象 全局变量已配置,但在函数中…

    Linux 2023年5月27日
    079
  • 剑指offer计划28(搜索与回溯算法困难)—java

    1.1、题目1 剑指 Offer 37. 序列化二叉树 1.2、解法 这题给我笑死了,我看到题解有个解法,我愿称之为神。 public class Codec { private …

    Linux 2023年6月11日
    0104
  • 从磁盘删除Ubuntu出现的问题

    问题描述:Win10+Ubuntu双系统,利用磁盘管理工具删除了Ubuntu占用的磁盘,导致开机直接进入Grub界面,并且启动项仍有Ubuntu。 问题解决: 开机进入BIOS或启…

    Linux 2023年6月14日
    099
  • 预处理

    在前面的学习中经常遇到用 #define命令定义符号常量的情况,其实使用 #define命令就是要定义一个可替换的宏。 宏定义是预处理命令的一种,它提供了一种可以替换源代码中字符串…

    Linux 2023年6月13日
    092
  • Django中orm的双重方法

    orm中的双重方法 更新或创建 Draw2DDevice.objects.update_or_create( defaults={‘x’: 777, ‘y’: 777,}, dev…

    Linux 2023年6月14日
    077
  • Ajax 技术(四)

    目的: 熟练掌握AJAX基础和XMLHttpRequest对象及其方法。 重点掌握AJAX发送请求的具体过程,及过程中的不同状态。 要求: 实现用户注册表单中,使用AJAX技术根据…

    Linux 2023年6月13日
    0106
  • WPF 将控件放入到 UserControl 里获取 HwndSource 为空的情况

    本文记录将 WPF 控件放入到 UserControl 里,如果此 UserControl 没有被设置 Visibility 为可见过,那么放在此 UserControl 内的控件…

    Linux 2023年6月6日
    088
  • Xbox分辨率突然变成640p

    今天XBox突然抽风还是发什么神经,输出分辨率突然变得非常模糊。一开始以为是HDMI线出现问题,后来用一条新的也是一样,所以就怀疑系统出了什么幺蛾子。 进入【电视和显示选项】——【…

    Linux 2023年6月13日
    0127
  • 新手如何引入Echart图标

    1.首先需要到Echart官网去下载配置文件 (官网地址:https://echarts.apache.org/zh/index.html) a.点击下载 b.点击下载后进入到这个…

    Linux 2023年6月13日
    090
  • tar压缩提示file changed as we read it

    压缩文件夹,过程中某个文件有变化,会提示 file changed as we read it 。不太确定是压缩到这里就中断了,还是压缩完,才提示的这个错误。 测试一下 做个实验,…

    Linux 2023年6月8日
    0168
  • PYTORCH: 60分钟 | TORCH.AUTOGRAD

    torch.autograd 是PyTorch的自动微分引擎,用以推动神经网络训练。在本节,你将会对autograd如何帮助神经网络训练的概念有所理解。 背景 神经网络(NNs)是…

    Linux 2023年6月16日
    0168
  • shell实现group by聚合操作统计

    在sql中,我们可以方便的使用group by及相应的聚合函数如sum avg count来实现分组统计需求,那当我们面对一个文本,在shell中也可以实现相应的功能吗? 在she…

    Linux 2023年5月28日
    0129
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球