需求

希望提取出xlsx中的图片文件以及他们所在的行和列

openpyxl (2.6.2)

这里使用的是openpyxl,但是官方文档中似乎没有对相应需求的解决办法。

所以本文用了许多不是官方文档API的hack,可能只适用于某些openpyxl的版本。

实现步骤

下面主要介绍一下本文中需要用到的相关API

  1. 安装openpyxl

  2. load_workbook 加载本地的xlsx文件;该函数会返回一个Workbook类型,这是整个xlsx的抽象。

from openpyxl import load_workbook
wb = load_workbook(filename='raw.xlsx')
  1. 接下来我们从Workbook中获取到某个Worksheet,也就是xlsx中的一个表,使用wb[sheetname]
courses = wb['Courses']
  1. **[可以直接看6]**下一步很自然我们希望逐行遍历找到包含image的cell, 于是通过iter_rows这个方法来遍历每一行,不幸的是,这个信息不在这里。Cell
TYPE_STRING = 's'
TYPE_FORMULA = 'f'
TYPE_NUMERIC = 'n'
TYPE_BOOL = 'b'
TYPE_NULL = 'n'
TYPE_INLINE = 'inlineStr'
TYPE_ERROR = 'e'
TYPE_FORMULA_CACHE_STRING = 'str'

VALID_TYPES = (TYPE_STRING, TYPE_FORMULA, TYPE_NUMERIC, TYPE_BOOL,
               TYPE_NULL, TYPE_INLINE, TYPE_ERROR, TYPE_FORMULA_CACHE_STRING)
  1. **[可以直接看6]**那接下来应该怎么找到这些图片?经过一番搜索虽然没找到如何获取image的相关文档,但是文档详细介绍了如何添加图片,我们可以发现这是通过Worksheet的一个_images的成员变量实现的。
def add_image(self, img, anchor=None):
    """
        Add an image to the sheet.
        Optionally provide a cell for the top-left anchor
        """
    if anchor is not None:
        img.anchor = anchor
        self._images.append(img)

因此我们可以通过遍历wb._images来找到所有的图片。

  1. **[可以直接看6]**那么有了这些Image我们应该怎么获取具体的图像数据,和所在位置呢?文档基本上已经没有任何相关信息,但是源代码可以给我们答案!

通过image._data()我们可以获取图像的二进制数据

def _data(self):
    """
        Return image data, convert to supported types if necessary
        """
    img = _import_image(self.ref)
    # don't convert these file formats
    if self.format in ['gif', 'jpeg', 'png']:
        img.fp.seek(0)
        fp = img.fp
        else:
            fp = BytesIO()
            img.save(fp, format="png")
            fp.seek(0)

            return fp.read()

我们也可以通过获取anchor来获取相关的位置信息,在我需要解析的文件中,anchor的类型是OneCellAnchor。其中的位置编码信息存在于_from中,他的类型是AnchorMarker。通过他的col和row的成员变量可以得到所在的行和列。

  1. 所以最后的结果是
for img in courses._images:  
    row = img.anchor._from.row
    col = img.anchor._from.col
    data = img._data()
    do_sth(row, col, data)
  1. data是二进制数据,如果想要查看,可以通过matplotlib来实现
import io
from matplotlib import pyplot as plt
import matplotlib.image as mpimg

def show_binary_image(data):
    i = io.BytesIO(data)
    i = mpimg.imread(i, format='PNG')
    plt.imshow(i, interpolation='nearest')
    plt.show()