This is a script for taking a list of URLs from a spreadsheet and generating a captioned QR code for each entry.
Specifically, the script reads the ‘LongURLs‘ input file, shortens the URLs, creates QR Codes, adds captions, and saves each QR code as a .PNG image file.
We shorten the URLs to reduce the complexity of the QR code, which makes it less likely to become unreadable from printing imperfections and dirt smudges.
We’ll use:
1. Numpy
2. Pandas
3. PyQRCode
4. pyshorteners
5. PIL
6. PyPNG
We load our URLs and IDs (captions) using the LongURLs template.

Next, we run the script and our QR codes will be output as PNG files in the same directory as our script.
Email links (such as “mailto:test@mailinator.com”) can be used as input URLs, but you’ll need to disable the ValueError:’Please enter a valid url’ that pyshorteners will raise.
import numpy as np import pyqrcode import pandas as pd from pyshorteners import Shortener from PIL import ImageFont from PIL import Image from PIL import ImageDraw shortener=Shortener('Tinyurl',timeout=10) DF = pd.DataFrame(pd.read_excel(r'C:\Users\Craig\Documents\Python Scripts\LongURLs.xlsx', sheetname='LongURLs',parse_cols='A:B')) LongURL=DF.iloc[:,0] ID=DF.iloc[:,1] ShortURL=np.array(LongURL, dtype='str') for i in range(0,len(LongURL)): ShortURL[i]=shortener.short(LongURL[i]) code=pyqrcode.create(ShortURL[i]) code.png(ID[i] + '.png', scale=6, module_color=[0,0,0,128],quiet_zone=7) #Adds caption img=Image.open(ID[i] + '.png') draw=ImageDraw.Draw(img) font = ImageFont.truetype("ariblk.ttf", 20) xcor=100 draw.text((xcor,245),str(ID[i]),font=font) img.save(str(ID[i]) + '.png')
With pyshorteners, we have the option of using a bunch of different URL shorteners – in this case we used TinyURL. See the pyshorteners github for a full list.
The font of your caption can be adjusted by taking the desired font’s .tff file (found in Control Panel > Appearance and Personalization > Fonts), copying it into the same folder as your script, and updating line 25.
You might need to adjust the “xcor” value (based on the length of your IDs) to get your caption centered under the QR image. If your ID lengths are all different, consider adding a few lines of code to detect the ID length and update “xcor” dynamically.