Python合并PDF、操做图片以及生成验证码

在记录今天重点内容的笔记以前,我想要先记录一下匿名函数,由于以前对匿名函数的理解仅停留在了解的状态,以致于实际应用很困难,近两天的内容恰好碰到相似的应用,遂再次深刻的解析一下。html

1、匿名函数

python 使用 lambda 来建立匿名函数。python

所谓匿名,意即再也不使用 def 语句这样标准的形式定义一个函数。linux

  • lambda 只是一个表达式,函数体比 def 简单不少。
  • lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
  • lambda 函数拥有本身的命名空间,且不能访问本身参数列表以外或全局命名空间里的参数。
  • 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增长运行效率。

在一些状况下,咱们一般会须要对一些有规律命名规则的文件进行必定的排序,可是单纯使用 sorted() 获取的文件名列表是按照 ascii 码排序的,例如:1.pdf, 10.pdf, 11.pdf, 2.pdf ··· ···数组

sort 与 sorted 区别:bash

sort 是应用在 list 上的方法,sorted 能够对全部可迭代的对象进行排序操做。app

list 的 sort 方法返回的是对已经存在的列表进行操做,而内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操做。dom

sorted() 的语法是ide

sorted(iterable, key=None, reverse=False)  

'''
参数说明:
iterable -- 可迭代对象。
key      -- 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
reverse  -- 排序规则,reverse = True 降序 , reverse = False 升序(默认)。
'''

当咱们须要把一个可迭代对象进行排序操做的时候,那么 key 值进行比较的元素一般就可使用 lambda 函数获取到。函数

若是咱们须要把下面的列表中的对象以数字的顺序排序,咱们使用 lambda 实现就方便多了字体

File=['1.pdf', '10.pdf', '7.pdf', '2.pdf', '21.pdf', '6.pdf', '3.pdf', '34.pdf']

首先咱们须要肯定数字的下标 

d[0:-4]

'''
个位数以 1.pdf 为例,从左边开始 1 的下标为 0,从右边开始 1 的下标为 -4,[0:-4]取到的值为 1
多位数以 31.pdf 为例,从左边开始 3 的下标为 0,从右边开始 1 的下标为 -4,[0:-4]取到的值为 31
'''

取到用于比较的元素以后,在使用 sorted 排序就简单多了

File=['1.pdf', '10.pdf', '7.pdf', '2.pdf', '22.pdf', '6.pdf', '3.pdf', '31.pdf']
# newFiles = sorted(File, key=lambda d: int(d.split(".pdf")[0])) 此方法亦可行,但不适用于首位不为数字的名称
newFiles = sorted(File, key=lambda d:int(d[0:-4]))
print(newFiles)


''' 运行结果
['1.pdf', '2.pdf', '3.pdf', '6.pdf', '7.pdf', '10.pdf', '22.pdf', '31.pdf']
'''

那么当名称的开头是字母不为数字时,如 'chapter1.pdf', 'chapter10.pdf' 之类,咱们就只能从中间取值,好比

listD=['chapter1.pdf', 'chapter10.pdf', 'chapter11.pdf', 'chapter12.pdf', 'chapter13.pdf', 'chapter14.pdf', 'chapter15.pdf', 'chapter16.pdf', 'chapter17.pdf', 'chapter18.pdf', 'chapter19.pdf', 'chapter2.pdf', 'chapter20.pdf', 'chapter21.pdf', 'chapter22.pdf', 'chapter23.pdf', 'chapter24.pdf', 'chapter25.pdf', 'chapter26.pdf', 'chapter3.pdf', 'chapter4.pdf', 'chapter5.pdf', 'chapter6.pdf', 'chapter7.pdf', 'chapter8.pdf', 'chapter9.pdf']

d[0:-4]

'''
个位数以 chapter1.pdf 为例,从左边开始 1 的下标为 7,从右边开始 1 的下标为 -4,[0:-4]取到的值为 1
多位数以 chapter26.pdf 为例,从左边开始 2 的下标为 7,从右边开始 6 的下标为 -4,[0:-4]取到的值为 26
'''

接着,咱们能够这样实现

files=sorted(listD,key=lambda x:int(x[7:-4]))
print(files)

''' 运行结果
['chapter1.pdf', 'chapter2.pdf', 'chapter3.pdf', 'chapter4.pdf', 'chapter5.pdf', 'chapter6.pdf', 'chapter7.pdf', 'chapter8.pdf', 'chapter9.pdf', 'chapter10.pdf', 'chapter11.pdf', 'chapter12.pdf', 'chapter13.pdf', 'chapter14.pdf', 'chapter15.pdf', 'chapter16.pdf', 'chapter17.pdf', 'chapter18.pdf', 'chapter19.pdf', 'chapter20.pdf', 'chapter21.pdf', 'chapter22.pdf', 'chapter23.pdf', 'chapter24.pdf', 'chapter25.pdf', 'chapter26.pdf']
'''

2、合并PDF

首先咱们须要安装 PyPDF2 模块

pip install PyPDF2

具体实现代码以下

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Time    : 2018/6/10 20:07
# @Author  : zhouyuyao
# @File    : demon1.py

import codecs
import PyPDF2
import os

filename="E:/GitHub/Python-Learning/LIVE_PYTHON/2018-06-07/deal-with-pdf/aminglinux"
files = list()
for fileName in os.listdir(filename):
    if fileName.endswith(".pdf"):
        files.append(fileName)

newFiles = sorted(files,key=lambda x:int(x[7:-4]))
print(newFiles)

os.chdir(filename)
pdfWriter = PyPDF2.PdfFileWriter()                 # 生成一个空白的pdf
for item in newFiles:
    pdfReader = PyPDF2.PdfFileReader(open(item, "rb"))
    for page in range(pdfReader.numPages):
        pdfWriter.addPage(pdfReader.getPage(page))

with codecs.open("aminglinux.pdf", "wb") as f:     # 命名一个PDF文件 并以二进制写入的方式打开
    pdfWriter.write(f)                             # 将内容写进PDF

运行结果

'''
运行结果,生成了一个aminglinux.pdf的文件,内容为所选 pdf 文件内容的汇总
'''

3、处理图片

PIL (Python Image Library) 是 Python 平台处理图片的事实标准,兼具强大的功能和简洁的 API。

3.1 安装

Pillow 库则是 PIL 的一个分支,维护和开发活跃,Pillow 兼容 PIL 的绝大多数语法,在这里咱们也是推荐使用pillow。

pip install pillow

3.2 新建一个 Image 类的实例

PIL 的主要功能定义在 Image 类当中,而 Image 类定义在同名的 Image 模块当中。使用 PIL 的功能,通常都是重新建一个 Image 类的实例开始。新建 Image 类的实例有多种方法。你能够用 Image 模块的 open() 函数打开已有的图片档案,也能够处理其它的实例,或者从零开始构建一个实例。

from PIL import Image
sourceFileName = "source.png"
avatar = Image.open(sourceFileName)

上述代码引入了 Image 模块,并以 open() 方法打开了 source.png 这个图像,构建了名为 avatar 的实例。若是打开失败,则会抛出 IOError 异常。

接下来你可使用 show() 方法来查看实例。

注意,PIL 会将实例暂存为一个临时文件,然后打开它,具体实现代码以下

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Time    : 2018/6/11 16:38
# @Author  : zhouyuyao
# @File    : demon4.py

from PIL import Image
sourceFileName = "E:/GitHub/Python-Learning/LIVE_PYTHON/2018-06-10/theWayToGo.png"
avatar = Image.open(sourceFileName)    # 打开图片,并构建 avatar 为名的实例
avatar.show()                          # 查看实例

运行以后将会打开图片

3.3 查看实例的属性

Image 类的实例有 5 个属性,分别是:

1)format

以 string 返回图片档案的格式(JPG, PNG, BMP, None, etc.);若是不是从打开文件获得的实例,则返回 None。

2)mode

 以 string 返回图片的模式(RGB, CMYK, etc.);完整的列表参见 官方说明·图片模式列表

3)size

以二元 tuple 返回图片档案的尺寸 (width, height)

4)palette

仅当 mode 为 P 时有效,返回 ImagePalette 示例

5)info

以字典形式返回示例的信息

若是咱们想要获得图片的格式、尺寸和模式,则能够加入以下操做

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Time    : 2018/6/11 16:38
# @Author  : zhouyuyao
# @File    : demon4.py
from PIL import Image
sourceFileName = "E:/GitHub/Python-Learning/LIVE_PYTHON/2018-06-10/theWayToGo.png"
avatar = Image.open(sourceFileName)
# avatar.show()

'''获取图片的格式、尺寸和模式'''
print("The picture's format is {0}, the size is {1}, the mode is {2}.".format(avatar.format, avatar.size, avatar.mode))

运行结果

The picture's format is PNG, the size is (718, 726), the mode is RGBA.

这里咱们看到返回了图片的格式 PNG、图片的大小 (718, 726) 和图片的模式 RGBA。

3.4 实例的方法

Image 类定义了许多方法,官方说明取自 http://effbot.org/imagingbook/image.htm

# image的方法
# image.show()
# image.open(file)
# image.save(outputfile)
# image.crop(left, upper, right, lower)            #抠图

1)图片 IO - 转换图片格式

Image 模块提供了 open() 函数打开图片档案,Image 类则提供了 save() 方法将图片实例保存为图片档案。

save() 函数能够以特定的图片格式保存图片档案。好比 save('target.jpg', 'JPG') 将会以 JPG 格式将图片示例保存为 target.jpg。不过,大多数时候也能够省略图片格式。此时,save() 方法会根据文件扩展名来选择相应的图片格式。

咱们以一个转换图片格式的脚本进行分析。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Time    : 2018/6/11 16:38
# @Author  : zhouyuyao
# @File    : demon4.py

import os, sys
from PIL import Image

for infile in sys.argv[1:]:
    f, e = os.path.splitext(infile)
    outfile = f + ".jpg"
    if infile != outfile:
        try:
            Image.open(infile).save(outfile)
        except IOError:
            print("cannot convert", infile)

这里,f 是除去扩展名以外的文件名。在 try 语句中,咱们尝试打开图片档案,而后以 .jpg 为扩展名保存图片档案。save() 方法会根据扩展名,将图片以 JPG 格式保存为档案。若是图片档案没法打开,则在终端上打印没法转换的消息。

代码中须要咱们进行传参,在 PyCharm 中以下所示

运行以后的结果,能够生成图片,可是图片错误不能打开,查了内外网大部分都只是展示了代码,官网给出的解释比较笼统,具体缘由待查证

后来查了下,是由于选取的那张图片没法转换格式,遂从网上下载了一个图片,则能够转换成功。

2)制做缩略图

Image 类的 thumbnail() 方法能够用来制做缩略图。它接受一个二元数组做为缩略图的尺寸,而后将示例缩小到指定尺寸。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Time    : 2018/6/11 22:15
# @Author  : zhouyuyao
# @File    : demon5.py

import os, sys
from PIL import Image

for infile in sys.argv[1:]:
    outfile = os.path.splitext(infile)[0] + ".thumbnail"
    if infile != outfile:
        try:
            im   = Image.open(infile)
            x, y = im.size
            im.thumbnail((x//2, y//2))
            im.save(outfile, "JPEG")
        except IOError:
            print("cannot create thumbnail for", infile)

这里咱们用 im.size 获取原图档的尺寸,而后以 thumbnail() 制做缩略图,大小则是原先图档的四分之一。一样,若是图档没法打开,则在终端上打印没法执行的提示。

3)剪裁图档

按照 horizon 和 vertic 两个变量切割当前目录下全部图片(包括子目录)

import Image as img
import os

imgTypes = ['.png','.jpg','.bmp']

horizon = 8
vertic  = 1

for root, dirs, files in os.walk('.'):
    for currentFile in files:
        crtFile = root + '\\' + currentFile
        if crtFile[crtFile.rindex('.'):].lower() in imgTypes:
            crtIm = img.open(crtFile)
            crtW, crtH = crtIm.size
            hStep = crtW // horizon
            vStep = crtH // vertic
            for i in range(vertic):
                for j in range(horizon):
                    crtOutFileName = crtFile[:crtFile.rindex('.')] + \
                        '_' + str(i) + '_' + str(j)\
                        + crtFile[crtFile.rindex('.'):].lower()
                    box = (j * hStep, i * vStep, (j + 1) * hStep, (i + 1) * vStep)
                    cropped = crtIm.crop(box)
                    cropped.save(crtOutFileName)

4)变形与粘贴

transpose() 方法能够将图片左右颠倒、上下颠倒、旋转 90°、旋转 180° 或旋转 270°。paste() 方法则能够将一个 Image 示例粘贴到另外一个 Image 示例上。

咱们尝试将一张图片的左半部分截取下来,左右颠倒以后旋转 180°;将图片的右半边不做更改粘贴到左半部分;最后将修改过的左半部分粘贴到右半部分。
 

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Time    : 2018/6/11 22:53
# @Author  : zhouyuyao
# @File    : demon6.py
from PIL import Image

imageFName = 'E:/GitHub/Python-Learning/LIVE_PYTHON/2018-06-10/theWayToGo.png'

def iamge_transpose(image):
    '''
        Input: a Image instance
        Output: a transposed Image instance
        Function:
            * switches the left and the right part of a Image instance
            * for the left part of the original instance, flips left and right\
                and then make it upside down.
    '''
    xsize, ysize = image.size
    xsizeLeft    = xsize // 2 # while xsizeRight = xsize - xsizeLeft

    boxLeft      = (0, 0, xsizeLeft, ysize)
    boxRight     = (xsizeLeft, 0, xsize, ysize)
    boxLeftNew   = (0, 0, xsize - xsizeLeft, ysize)
    boxRightNew  = (xsize - xsizeLeft, 0, xsize, ysize)

    partLeft     = image.crop(boxLeft).transpose(Image.FLIP_LEFT_RIGHT).\
        transpose(Image.ROTATE_180)
    partRight    = image.crop(boxRight)

    image.paste(partRight, boxLeftNew)
    image.paste(partLeft, boxRightNew)
    return image

avatar = Image.open(imageFName)
avatar = iamge_transpose(avatar)
avatar.show()

运行以后,图片经过代码实现了另外一种展现效果

4、生成验证码

首先须要安装模块 “Pillow”

pip install pillow

代码实现以下

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Time    : 2018/6/11 15:11
# @Author  : zhouyuyao
# @File    : demon3.py

import random
import string
from PIL import Image, ImageDraw, ImageFont, ImageFilter

class VerCode(object):
    def __init__(self):
        # 字体的位置,不一样版本的系统会有不一样
        self.font_path = 'E:/GitHub/Python-Learning/LIVE_PYTHON/2018-06-10/msyh.ttc'
        # 生成几位数的验证码
        self.number = 4
        # 生成验证码图片的高度和宽度
        self.size = (100, 30)
        # 背景颜色,默认为白色
        self.bgcolor = (255, 255, 255)
        # 字体颜色,默认为蓝色
        self.fontcolor = (0, 0, 255)
        # 干扰线颜色。默认为红色
        self.linecolor = (255, 0, 0)
        # 是否要加入干扰线
        self.draw_line = True
        # 加入干扰线条数的上下限
        self.line_number = 20

    # 用来随机生成一个字符串
    def gene_text(self):
        self.source = list(string.ascii_letters)
        for self.index in range(0, 10):
            self.source.append(str(self.index))
        return ''.join(random.sample(self.source, self.number))     # number是生成验证码的位数

    # 用来绘制干扰线
    def gene_line(self, draw, width, height):
        self.begin = (random.randint(0, width), random.randint(0, height))
        self.end = (random.randint(0, width), random.randint(0, height))
        draw.line([self.begin, self.end], fill=self.linecolor)

    # 生成验证码
    def gene_code(self):
        self.width, self.height = self.size                                      # 宽和高
        self.image = Image.new('RGBA', (self.width, self.height), self.bgcolor)  # 建立图片
        self.font = ImageFont.truetype(self.font_path, 25)                       # 验证码的字体
        self.draw = ImageDraw.Draw(self.image)                                   # 建立画笔
        self.text = self.gene_text()                                             # 生成字符串
        self.font_width, self.font_height = self.font.getsize(self.text)
        self.draw.text(((self.width - self.font_width) / self.number, (self.height - self.font_height) / self.number),
                       self.text, font=self.font, fill=self.fontcolor)           # 填充字符串
        if self.draw_line:
            for i in range(self.line_number):
                self.gene_line(self.draw, self.width, self.height)

    def effect(self):
        # self.image = self.image.transform((self.width + 20, self.height + 10), Image.AFFINE, (1, -0.3, 0, -0.1, 1, 0), Image.BILINEAR)                                            # 建立扭曲
        self.image = self.image.filter(ImageFilter.EDGE_ENHANCE_MORE)            # 滤镜,边界增强
        self.image.save('validateCodePic.png')                                   # 保存验证码图片
        # self.image.show()

if __name__ == "__main__":
    # 进行封装
    vco = VerCode()
    vco.gene_code()
    vco.effect()

运行以后,咱们获得的一个验证码图片以下所示,实际应用中咱们每次调用都会从新生成

 

参考资料

1. http://www.runoob.com/python3/python3-function.html 菜鸟教程

2. http://www.runoob.com/python3/python3-func-sorted.html 菜鸟教程

3. http://blog.51cto.com/286577399/2062340 51. Python 数据处理(2)

4. http://www.cnblogs.com/alex3714/articles/6662365.html Django之路 - 实现登陆随机验证码

5. https://liam0205.me/2015/04/22/pil-tutorial-basic-usage/ PIL 简明教程 - 基本用法

6. https://liam0205.me/2014/01/27/Py-Incise-Images/ 【Life on Python】批量切割图片

7. https://www.cnblogs.com/lianzhilei/archive/2017/06/02/6932894.html Python开发【笔记】:sort排序大法

相关文章
相关标签/搜索