1

I have Excel spreadsheets that have an image per row, scraping the image using this example works. However what I want to do instead of scraping the image of the spreadsheet is I want to extract the url associated with that image. If I open up the Excel file I can click on the image and navigate to the given url. Is it impossible to extract this URL via Python?

I have looked through the documentation on openpyxl to see if there are any examples of scraping embedded urls in images, and I couldn't find anything.

Any help would be much appreciated. Thanks

  • This may not be possible with openpyxl - see: https://stackoverflow.com/questions/73170702/python-openpyxl-read-the-images-present-in-the-excel-sheets – DarkKnight May 26 '23 at 16:52

1 Answers1

2

As a good start, you can read/unzip the spreadsheet :

import zipfile
import pandas as pd

with zipfile.ZipFile("file.xlsx", "r") as zf:
    xmls = [zf.read(fn) for fn in zf.infolist()
            if fn.filename.startswith("xl/drawings/_rels/")]

urls = (
    pd.concat([pd.read_xml(data).assign(SheetNumber=i)
         for i, data in enumerate(xmls, start=1)]).sort_values(by=["SheetNumber", "Id"])
        .loc[lambda x: x["TargetMode"].eq("External"), ["SheetNumber", "Target"]]
        .reset_index(drop=True)
)

Output :

print(urls)

   SheetNumber                           Target
0            1       https://stackoverflow.com/
1            1   https://gis.stackexchange.com/
2            2  https://meta.stackexchange.com/
3            2           https://askubuntu.com/

To go further, we can use and the Styler to put the images next to their urls :

import string
import base64
from collections import defaultdict
from openpyxl import load_workbook   

workbook = load_workbook("file.xlsx")

images = defaultdict(list)
for ws in workbook:
    #https://github.com/ultr4nerd/openpyxl-image-loader
    for image in ws._images:
        row = image.anchor._from.row + 1
        col = string.ascii_uppercase[image.anchor._from.col]
        images[ws.title].append({f'{col}{row}': image._data()})

def tag_img(ser):
    return r'<div style="display: flex; justify-content: center;">'\
            '<img src="data:image/png;base64,{}" width="200" height="50"></div>' \
             .format(base64.b64encode(ser).decode("utf-8"))

imgs = pd.concat(
    [pd.DataFrame(v).stack().apply(tag_img)
        .reset_index(level=1, name="Image")
        .assign(SheetName=k).rename(columns={"level_1": "CellCoord"})
                 for k,v in images.items()], ignore_index=True
)

(
    imgs.join(urls)[["SheetNumber", "SheetName", "CellCoord", "Image", "Target"]].style
        .set_properties(**{"border":"1px solid",
            "text-align": "center", "background-color": "white"})
        .format(hyperlinks="html").pipe(display)
)

Output :

enter image description here

Spreadsheet used (file.xlsx):

enter image description here

Timeless
  • 22,580
  • 4
  • 12
  • 30
  • **NB** : `drawing1.xml.rels` holds the *urls* of the 1st sheet, `drawing2.xml.rels` for the 2nd and so on.. – Timeless May 26 '23 at 17:37