彼岸桌面壁纸-二次元
(少爬涩图,健康生活!)python
选择网页元素:Ctrl+Shift+C算法
你学废了吗?学废扣眼珠子,没学废点赞收藏windows
问:为何不是下面那个 "img src=..." 的地址呢?浏览器
答:下面的地址的确是一个图片的地址,可是该图片是800*450(咱们要1920*1080),是用来显示该页面这个小图的(以下图)网络
因此你喜欢 大 仍是喜欢 小?你品你细品app
① 你能够点击 "/desk/22629.htm"函数
② 在浏览器网址输入栏输入 "http://www.netbian.com/desk/22629.htm"测试
(② 这个是重点,由于爬取时咱们须要这个完整地址才能访问下面这个页面)网站
此时的 "img src=..." 就是咱们 大 壁纸的最终下载地址编码
① 访问主页(表层网页)
② 将主页的源代码所有读取出来
查看源代码:
① 右键→查看网页源代码
② Ctrl+U
② 经过正则匹配将读取的网页源代码中 全部 单张壁纸页面的(深层网页)地址 "/desk/*.htm" 筛选出来,做为字符串对象
③ http://www.netbian.com + "/desk/*.htm" 将两个字符串合并为完整的 url
④ 经过上面完整的网址访问每张壁纸的深层网页,并将深层网页的源代码读取出来
⑤ 经过正则匹配将 1920*1080壁纸 的下载地址,筛选出来
⑥ 访问该壁纸的下载地址,以二进制的形式将数据读取,并以 "*.jpg(.png)" 格式进行写入为本地文件
① 如下部分都有标明 重点 与 非重点
重点:核心算法的展示(如何访问网页并经过正则匹配提取所需的url)
非重点:下载功能,显示下载进度,自动为文件名命名
※ 读者看懂重点的核心算法就行了,其他的功能有兴趣本身琢磨
② 建议从主函数开始食用,否则绝对懵逼
③ 你也能够下载这个小项目本身去Van~Van~
该项目下载地址:
连接:https://pan.baidu.com/s/1SVIMVzmPIEJ7kEsvMpK23Q
提取码:fkgz
# 最好把 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
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
def get_deep_url(deep_page): """经过深层网页获取 1920*1080 的壁纸地址 :param deep_page: 深层网页地址 :return: 返回一个 壁纸 下载地址 """ response = requests.get(deep_page) # 此页面已经能找到 1920*1080 jpg图片的下载地址了,咱们经过正则匹配,分组“()”进行提取 # ① 首先锁定目标地址的头 /desk/.+?-1920x1080.htm # ② 接着再往下匹配的地址,即为最终下载地址 # 总结:之因此能够分两部分进行锁定匹配,是由于 # ① search 是能够经过跨行匹配(不过一旦匹配成功一个,即返回对象) # ② .*? 到 <img src... 之间正好没有 \n 换行符,因此能日后一直匹配(由于 . 是匹配除了\n外的全部任意一个字符) url_add = r'/desk/.+?-1920x1080.htm.*?<img src="(http://img.netbian.com/file/.+?.jpg)"' deep_url = re.search(url_add, response.text) # 返回的对象须要,经过 group() 进行提取 # print(deep_url.group(1)) return deep_url.group(1)
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))
① 第一页
② 第二页
③ 第三页(这回懂了吧,改变 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
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
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()
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 模块...")
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]... 所有下载完毕...
先将全部地址爬取完毕再进行下载,方便统计需下载的总图片数量,用于显示下载进度的图片总数
使用协程进行下载,要注意合理分配时间给用于 下载的协程 和用于 显示下载进度的协程
由于协程的时间分配是很智能的,因为下载图片是须要请求且等待的过程,协程会自动将这些多余的时间分配给其余协程,这样显示下载进度的协程老是能分配到不少时间,致使用于下载协程出现阻塞,从而致使下载速度极慢!
因此咱们要让 显示下载进度的协程 每隔0.5s显示一次(gevent.sleep(0.5)),那么这0.5s就能够分配给 用于下载的协程了
最后再附上下载地址:
连接:https://pan.baidu.com/s/1SVIMVzmPIEJ7kEsvMpK23Q 提取码:fkgz 算是简单的项目了(算法很容易),只是附加的功能看起来可能有点复杂,其实也有一大部分代码都是为了防止bug 有不足之处,恳请大佬指出orz! 或有看不懂的朋友也能够评论,我会尽快恢复的啦~ღ( ´・ᴗ・` )比心