Python 萌新 - 实现 Markdown 图片下载器

栏目: Python · 发布时间: 6年前

内容简介:Python 萌新 - 实现 Markdown 图片下载器

简书支持打包下载所有文章功能,可以方便作者转移或保存。但是图片不支持自动下载,最近在学Python,便写了一个md图片下载器。

目标

本人 Python 新手,欢迎大佬指点。本文主要是对源码进行解读,期望实现以下目标:

  1. 一键下载所有Markdown文件中的图片,并保存到本地。
  2. 图片根据文章分类
  3. 简单易用。

先上最终效果:

Python 萌新 - 实现 Markdown 图片下载器
Python 萌新 - 实现 Markdown 图片下载器

实现步骤

  1. 搜索指定文件夹,找出文件夹及子文件包含的md文件。
  2. 匹配出md文件中所有的图片。
  3. 所有图片异步下载。
  4. 下载报告与GUI。
  5. Python 打包工具。
Python 萌新 - 实现 Markdown 图片下载器

1. 搜索文件夹中md文件

首先我们要根据用户指定的文件夹,搜索出该文件夹及其子文件夹中包含的md文件,并将这些文件的绝对路径加到数组当中。

class Directory(object):

    @classmethod
    def find_sub_path(cls, path):
        # 初始化一个空的文章列表
        article_list = []
        # 获取该文件夹下的所以子文件
        temp_files = os.listdir(path)
        # 遍历子文件
        for temp_file in temp_files:
            # 拼接该文件绝对路径
            full_path = os.path.join(path, temp_file)

            # 匹配.md文件 
            if os.path.isfile(full_path) and os.path.splitext(full_path)[1] == ".md":
                # 如果是.md文件 加入文章列表
                article = Article(full_path)
                article_list.append(article)
            # 如果是文件夹 进行递归继续搜索
            elif os.path.isdir(full_path):
                # 将子文件夹中的文章列表拼接到上级目录的文章列表中
                article_list.extend(cls.find_sub_path(full_path))

        return article_list

2. 匹配出md文件中的图片

为每一篇文章新建一个存放该文章中图片的文件夹,然后利用正则匹配出该文章中的所以图片,并保存到图片数组中。

# 文章类

class Article(object):

    def __init__(self, path):
        # 文章的绝对路径
        self.article_path = path
        # 拼接文章图片下载后保存的路径
        self.article_pic_dir = path.replace(".md", "_Image")
        # 新建保存文章图片的文件夹
        self.mkdir_image_dir()
        # 开始搜索图片
        self.pic_list = self.find_pics(self.article_path)

    # 查找图片
    def find_pics(self, article_path):
        # 打开md文件
        f = open(article_path, 'r')
        content = f.read()
        pics = []

        # 匹配正则 match ![]()
        results = re.findall(r"!\[(.+?)\)", content)

        index = 0
        for result in results:
            temp_pic = result.split("](")
            # 将图片加入到图片数组当中
            if len(temp_pic) == 2:
                pic = Picture(temp_pic[0], temp_pic[1], self.article_pic_dir, index)
                pics.append(pic)
            index += 1
        f.close()
        return pics

    # 新建图片的保存文件夹
    def mkdir_image_dir(self):
        # 如果该文件夹不存在 则新建一个
        if not os.path.exists(self.article_pic_dir):
            os.mkdir(self.article_pic_dir)

3. 下载图片

简单地判断了图片是否有类型,拼接图片的保存路径与命名。检查图片是否重复下载,检查图片链接是否合法,下载并且保存图片,同时做了下载失败的处理和保存失败的处理,保存了下载失败的原因,以便后面生成报告。

# 图片类
class Picture(object):

    def __init__(self, name, url, dir_path, index):
        # 该图片顺序下标  用于设置默认图片名字
        self.index = index
        # 图片名
        self.name = name
        # 图片链接
        self.url = url
        # 图片保存路径
        self.dir_path = dir_path
        # 图片下载失败原因
        self.error_reason = None

    # 开始下载
    def start_download_pic(self, download_pic_callback):
        pic_path = self.build_pic_name()

        # 已存在则不重复下载
        if os.path.exists(pic_path):
            print ('pic has existed:' + self.url)
            self.error_reason = "pic has existed:"
            download_pic_callback(self)
            return

        # 图片链接前缀不包含http
        if not self.url.startswith("http"):
            print ('pic has invalid url:' + self.url)
            self.error_reason = "pic has invalid url"
            download_pic_callback(self)
            return

        header = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36',
            'Cookie': 'AspxAutoDetectCookieSupport=1',
        }

        # 下载图片
        request = urllib2.Request(self.url, None, header)
        try:
            response = urllib2.urlopen(request, timeout=10)
        # 下载失败
        except Exception, error:
            print ('pic cannot download:' + self.url)
            self.error_reason = str(error)
            download_pic_callback(self)
            return

        # 保存图片
        try:
            fp = open(pic_path, 'wb')
            fp.write(response.read())
            fp.close()
        # 保存失败
        except IOError, error:
            print(error)
            self.error_reason = str(error)
            download_pic_callback(self)
            return
        
        # 下载完成回调
        download_pic_callback(self)

    # 组装图片保存命名
    def build_pic_name(self):
        # 剪去图片链接后的参数
        pic_url = self.url.split("?")[0]
        
        # 获取图片格式后缀 如果没有 默认jpg
        urls = pic_url.split(".")
        if len(urls) > 1:
            pic_type = urls[len(urls)-1]
        else:
            pic_type = "jpg"

        # 组装图片命名
        if self.name is not None and len(self.name) > 0:
            pic_name = self.name + "." + pic_type
        else:
            pic_name = "no_name_" + str(self.index) + "." + pic_type
        
        pic_path = os.path.join(self.dir_path, pic_name)
        return pic_path

4. 下载报告与GUI

a. 本来 XTImageDownloaderGUI 应该分开,但是对 Tkinter 理解还有限,所以就直接写,方便调用。

这部分代码主要是 选择文件夹开始搜索并下载 的 UI 及逻辑处理。

Python 萌新 - 实现 Markdown 图片下载器
class XTImageDownloader(object):

    def __init__(self):
        # 数据
        self.download_error_list = []
        self.all_pic_count = 0
        self.current_pic_index = 0
        self.thread_lock = threading.Lock()
        self.search_button = None
        # 图形界面相关
        self.root = Tk()
        self.root.title("XTImageDownloader")
        self.path = StringVar()
        self.title = StringVar()
        self.title.set("请选择Markdown文件所在文件夹")
        self.list_box = None
        Label(self.root, textvariable=self.title).grid(row=0, column=1)
        Label(self.root, text="文件夹路径:").grid(row=1, column=0)
        Entry(self.root, textvariable=self.path).grid(row=1, column=1)
        Button(self.root, text="选择路径", command=self.select_path).grid(row=1, column=2)
        self.root.mainloop()

    # 选择文件夹
    def select_path(self):
        self.path.set(tkFileDialog.askdirectory())
        # 用户选中文件夹之后 显示下载按钮
        if self.path.get() != "":
            self.search_button = Button(self.root, text="开始搜索并下载", command=self.start_search_dir)
            self.search_button.grid(row=2, column=1)
            return self.path

    # 开始搜索文件夹 并且下载
    def start_search_dir(self):
        self.search_button['state'] = DISABLED
        self.search_button['text'] = "正在下载..."
        
        self.all_pic_count = 0
        self.current_pic_index = 0
        self.download_error_list = []
        # 获取Markdown文件列表
        article_list = Directory.find_sub_path(self.path.get())

        # 更新搜索进度 刷新UI
        for article in article_list:
            self.all_pic_count += len(article.pic_list)
        self.change_title(self.all_pic_count, self.current_pic_index)

        # 开始下载图片
        for article in article_list:
            for pic in article.pic_list:
                # 开启异步线程下载图片 并且传入下载完成的回调
                thread = threading.Thread(target=pic.start_download_pic, args=(self.download_pic_callback,))
                thread.start()

b. 开启异步线程下载图片,回调函数作为入参传入。

Python 萌新 - 实现 Markdown 图片下载器
// 异步下载
  thread = threading.Thread(target=pic.start_download_pic,   args=(self.download_pic_callback,))
  thread.start()

回调函数更新 UI 告知下载进度,由于开启了多线程,这里需要加锁,防止资源竞争。全部下载成功后生成报告。

# 下载图片完成后的回调函数
    def download_pic_callback(self, pic):
        # 获取线程锁
        self.thread_lock.acquire()
        # 如果下载失败 则保存到失败列表
        if pic.error_reason is not None and len(pic.error_reason) > 0:
            self.download_error_list.append(pic)

        self.current_pic_index += 1

        # 更新下载进度 刷新UI
        print('finish:' + str(self.current_pic_index) + '/' + str(self.all_pic_count))
        self.change_title(self.all_pic_count, self.current_pic_index)

        # 全部下载成功 刷新UI 生成失败报告
        if self.all_pic_count == self.current_pic_index:
            self.search_button['text'] = "下载完成"
            self.print_error(self.download_error_list)

        # 释放锁
        self.thread_lock.release()

    # 更新下载进度 刷新UI
    def change_title(self, total_num, current_num):
        self.title.set("已完成" + str(current_num) + "/" + str(total_num))

c. 生成失败报告,利用 list_box 来展示错误列表,并且添加了滑块,可以滑动浏览。

# 生成失败列表
    def print_error(self, download_error_list):
        # python log
        print("-----------------------------------")
        print("some pic download failure:")
        for pic in download_error_list:
            print("")
            print("name:" + pic.name)
            print("url:" + pic.url)
            print("error_reason:" + pic.error_reason)

        Label(self.root, text="部分图片下载失败:").grid(row=4, column=1)

        # GUI
        # 新建listbox
        self.list_box = Listbox(self.root)
        for pic in download_error_list:
            self.list_box.insert(END, pic.url + " -> " + pic.error_reason)
        self.list_box.grid(row=5, column=0, columnspan=3, sticky=W+E+N+S)

        # 垂直 scrollbar
        scr1 = Scrollbar(self.root)
        self.list_box.configure(yscrollcommand=scr1.set)
        scr1['command'] = self.list_box.yview
        scr1.grid(row=5, column=4, sticky=W+E+N+S)
        # 水平 scrollbar
        scr2 = Scrollbar(self.root, orient='horizontal')
        self.list_box.configure(xscrollcommand=scr2.set)
        scr2['command'] = self.list_box.xview
        scr2.grid(row=6, column=0, columnspan=3, sticky=W+E+N+S)

d. 最后贴上引用到的标准库(Python 2.7):

  1. #!/usr/bin/env python# -*- coding:utf-8 -*- 这两行是为了支持中文。
  2. osos.path 用于系统及文件查找。
  3. re 正则匹配。
  4. urllib2 网络库,用于下载图片。
  5. TkintertkFileDialog 是 GUI 库。
  6. threading 多线程库。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import os
import os.path
import re
import urllib2
from Tkinter import *
import tkFileDialog
import threading

5. Python 打包工具

Python 萌新 - 实现 Markdown 图片下载器

这部分主要是讲如何把 Python 脚本打包打包成可以在 Mac 上运行的应用。

PyInstaller

[图片上传失败...(image-fd72fe-1516382674300)]

PyInstaller 是一个打包工具,可以帮助你打包 Python 脚本,生成应用。

安装 PyInstaller

$ pip install pyinstaller

打包后在 dist 文件夹中可找到可执行文件

$ pyinstaller yourprogram.py

生成 app

$ pyinstaller --onedir -y  main.spec

py2app

py2app 也是打包工具,这里只是简单介绍一下,想要深入了解详细内容可以自行搜索。

切换到你的工程目录下

$ cd ~/Desktop/yourprogram

生成 setup.py 文件

$ py2applet --make-setup yourprogram.py

生成你的应用

$ python setup.py py2app

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Advanced Web Metrics with Google Analytics

Advanced Web Metrics with Google Analytics

Brian Clifton / Sybex / 2008 / USD 39.99

Are you getting the most out of your website? Google insider and web metrics expert Brian Clifton reveals the information you need to get a true picture of your site's impact and stay competitive usin......一起来看看 《Advanced Web Metrics with Google Analytics》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具