URLからQRコードを一括生成するスクリプト
URLから PNG / JPG / SVG / PDF / Excel / Word / PowerPoint の7形式でQRコードを生成するPythonスクリプトです。`qrcode` ライブラリを基盤に、印刷用PDFや資料配布用Office文書にも直接埋め込める形で書き出せるため、名刺裏のQRから社内資料への掲載までそのまま使えます。
名刺裏に載せる1枚のQRから、営業資料のパワポ、配布用PDFまで、URL1本を7形式のファイルに一括書き出しできるスクリプトです。`qrcode` ライブラリを基盤にPNG / JPG / SVGの画像3形式と、PDF / Excel / Word / PowerPointの4形式に対応し、用途ごとに同じQRを作り直す手間を省きます。
このスクリプトでできること
- URLからのQRコード生成(誤り訂正レベルM、版数自動選択)
- PNG・JPG・SVGの画像3形式への書き出し
- PDF・Excel・Word・PowerPointのOffice4形式への埋め込み出力
- SVGモジュール未搭載時のPNGへの自動フォールバック
- タイムスタンプ付きファイル名による上書き防止
.pybes ファイルをPybesにインポートすると、スクリプトと設定フィールドが自動で読み込まれます。
設定フィールド
このスクリプトで使用する設定フィールドです。Pybes上でGUIから値を入力できます。
url テキスト 必須 URL
QRコードに埋め込むURL(例: https://example.com)
output_format ドロップダウン 必須 出力形式
画像3形式(jpg / png / svg)とOffice4形式(pdf / xlsx / docx / pptx)から選択
デフォルト: png
output_dir フォルダ 必須 出力先フォルダ
生成したQRコードファイルの保存先フォルダ
コード解説
import sys
import json
import os
import io
from datetime import datetime
with open(sys.argv[1], encoding="utf-8") as f:
inputs = json.load(f)
url = inputs["URL"]
output_format = inputs["出力ファイル"]
output_dir = inputs["出力先フォルダ"]
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
print(f"QRコード生成開始: {url}")
print(f"出力形式: {output_format}")
try:
import qrcode
def make_qr():
qr = qrcode.QRCode(
version=None,
error_correction=qrcode.constants.ERROR_CORRECT_M,
box_size=10,
border=4,
)
qr.add_data(url)
qr.make(fit=True)
return qr
def make_png_bytes(qr):
"""PNG形式のバイト列を返す"""
img = qr.make_image(fill_color="black", back_color="white")
buf = io.BytesIO()
img.save(buf, format="PNG")
buf.seek(0)
return buf
if output_format == "jpg":
filename = f"qrcode_{timestamp}.jpg"
filepath = os.path.join(output_dir, filename)
qr = make_qr()
img = qr.make_image(fill_color="black", back_color="white")
# 前提: JPEGはRGB必須のためRGBに変換
img.convert("RGB").save(filepath, "JPEG", quality=95)
elif output_format == "png":
filename = f"qrcode_{timestamp}.png"
filepath = os.path.join(output_dir, filename)
qr = make_qr()
img = qr.make_image(fill_color="black", back_color="white")
img.save(filepath, "PNG")
elif output_format == "svg":
filename = f"qrcode_{timestamp}.svg"
filepath = os.path.join(output_dir, filename)
try:
import qrcode.image.svg
qr = make_qr()
img = qr.make_image(image_factory=qrcode.image.svg.SvgPathImage)
img.save(filepath)
except ImportError:
# 前提: SVGモジュールが使えない場合はPNGにフォールバック
print("警告: SVG出力が使用できないためPNGで代替出力します", file=sys.stderr)
filename = f"qrcode_{timestamp}.png"
filepath = os.path.join(output_dir, filename)
qr = make_qr()
img = qr.make_image(fill_color="black", back_color="white")
img.save(filepath, "PNG")
elif output_format == "pdf":
from reportlab.pdfgen import canvas
from reportlab.lib.units import mm
from reportlab.lib.utils import ImageReader
filename = f"qrcode_{timestamp}.pdf"
filepath = os.path.join(output_dir, filename)
qr = make_qr()
buf = make_png_bytes(qr)
img_reader = ImageReader(buf)
qr_size = 80 * mm
page_w = qr_size + 20 * mm
page_h = qr_size + 20 * mm
c = canvas.Canvas(filepath, pagesize=(page_w, page_h))
c.drawImage(img_reader, 10 * mm, 10 * mm, width=qr_size, height=qr_size)
c.save()
elif output_format == "xlsx":
from openpyxl import Workbook
from openpyxl.drawing.image import Image as XLImage
filename = f"qrcode_{timestamp}.xlsx"
filepath = os.path.join(output_dir, filename)
qr = make_qr()
buf = make_png_bytes(qr)
wb = Workbook()
ws = wb.active
ws.title = "QRコード"
ws["A1"] = url
xl_img = XLImage(buf)
xl_img.anchor = "A2"
ws.add_image(xl_img)
wb.save(filepath)
elif output_format == "docx":
from docx import Document
from docx.shared import Mm
filename = f"qrcode_{timestamp}.docx"
filepath = os.path.join(output_dir, filename)
qr = make_qr()
buf = make_png_bytes(qr)
doc = Document()
doc.add_heading("QRコード", level=1)
doc.add_paragraph(url)
doc.add_picture(buf, width=Mm(60))
doc.save(filepath)
elif output_format == "pptx":
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.enum.text import PP_ALIGN
filename = f"qrcode_{timestamp}.pptx"
filepath = os.path.join(output_dir, filename)
qr = make_qr()
buf = make_png_bytes(qr)
prs = Presentation()
slide_layout = prs.slide_layouts[6] # 前提: 空白レイアウトを使用
slide = prs.slides.add_slide(slide_layout)
# QR画像を中央に配置
qr_size = Inches(3)
left = (prs.slide_width - qr_size) / 2
top = (prs.slide_height - qr_size) / 2
slide.shapes.add_picture(buf, left, top, width=qr_size, height=qr_size)
# URLをQR下部にテキストで追加
txBox = slide.shapes.add_textbox(left, top + qr_size, qr_size, Inches(0.4))
tf = txBox.text_frame
tf.text = url
tf.paragraphs[0].alignment = PP_ALIGN.CENTER
tf.paragraphs[0].runs[0].font.size = Pt(10)
prs.save(filepath)
else:
print(f"エラー: 未対応の出力形式です: {output_format}", file=sys.stderr)
sys.exit(1)
print(f"保存完了: {filepath}")
except Exception as e:
print(f"エラーが発生しました: {e}", file=sys.stderr)
sys.exit(1)
print("完了") 先頭で標準ライブラリ(sys / json / os / io / datetime)を読み込み、Pybesが sys.argv[1] に渡してくるJSON設定を inputs 辞書に展開します。フィールドは url / output_format / output_dir の3つだけで、チェックボックスや数値のような型変換は不要なため、inputs からそのまま代入するシンプルな構造です。datetime.now().strftime で秒単位のタイムスタンプを生成し、出力ファイル名の接尾辞として使うことで、連続実行しても過去ファイルを上書きしません。
全体を try で包むことで、下段の処理がどこで失敗しても末尾の except Exception 側で一元的に拾える設計にしています。make_qr() は qrcode.QRCode の初期化を共通化する関数で、誤り訂正レベル M、モジュール1個あたり10px、枠の余白4モジュールという標準的な設定です。make_png_bytes() は io.BytesIO にPNG画像を書き出すヘルパーで、PDF / Excel / Word / PowerPoint の各分岐がこの関数を通してQR画像を「メモリ上だけで」取り出せるようにしています。一時ファイルを作らずに済むため、処理後の片付けも不要です。
画像系3形式(JPG / PNG / SVG)の分岐です。JPGは img.convert("RGB") を挟んでからJPEG保存している点に注目してください。qrcode が返す画像は1ビットモードでアルファチャンネルを持たないことがあり、JPEGはRGB前提のため事前変換が必要です。SVGは qrcode.image.svg.SvgPathImage を試み、モジュールが入っていない環境では except ImportError でPNGに自動フォールバックします。実行時に初めてモジュール不足に気付いて「何も出力されなかった」という事態を避けるための保険です。
Office系4形式(PDF / Excel / Word / PowerPoint)の分岐です。どの分岐も make_qr() と make_png_bytes() で生成したPNGバイト列を、各ライブラリ(reportlab / openpyxl / python-docx / python-pptx)の画像挿入APIに流し込むだけの素直な構造です。PowerPointだけは prs.slide_layouts[6](空白レイアウト)を使い、中央にQR画像、直下にURLのテキストボックスを配置しています。最後の print("完了") は sys.exit(1) で落ちなかった場合にのみ実行されるため、ログに完了行が出ていれば保存成功と判断できます。
仕組みの詳細
誤り訂正レベルはMが標準
qrcode.QRCode の error_correction に ERROR_CORRECT_M を指定しており、QRコード全体の約15%が欠損しても復元できます。レベルはL(7%)/ M(15%)/ Q(25%)/ H(30%)の4段階で、Mは規格上のデフォルト値。印刷物の汚れや折れ程度であればこのレベルで十分読み取れますが、中央にロゴを重ねるなど意図的に情報を隠す場合はHまで上げておくと失敗しにくくなります。
共通関数でQR生成処理を1回にまとめる
QRコードの生成そのものは make_qr()、PNGバイト列の生成は make_png_bytes() という形で関数に切り出しています。PDF / Excel / Word / PowerPoint の4形式はいずれもPNG画像を文書内に埋め込む構造のため、共通関数を通すことで同じQR画像を毎回作り直さずに済みます。io.BytesIO に書き出した画像は各ライブラリに直接渡せるので、一時ファイルを作ってOSに書き込んで消す、といった余計な処理も不要です。
出力形式ごとの分岐はif / elifで単純展開
output_format の値で if / elif の長い分岐を組み、画像系(JPG / PNG / SVG)はそのまま保存、Office系(PDF / XLSX / DOCX / PPTX)はPNGを生成してから文書に配置します。JPGの img.convert("RGB") はJPEGがアルファチャンネルを扱えないための必須処理、PowerPointの prs.slide_layouts[6] は空白レイアウトのインデックスで、いずれも各フォーマット固有の決まりごとです。
応用・カスタマイズ
QRコードの大きさを変える
make_qr 内の box_size=10 を増減すると、1モジュール(QRコードの最小ドット)のピクセル数が変わります。5ならコンパクト、15なら大きめ。border=4 はQR周囲の余白モジュール数で、規格上は4が推奨値。小さくすると読み取り失敗が増えるので、基本は触らない方が無難です。
色を変える(モノクロ以外にする)
qr.make_image(fill_color="black", back_color="white") の2色を "#1E40AF" や "navy" のようなPillow互換の色指定に書き換えればブランドカラーのQRコードになります。ただしコントラスト比が低いとスマホカメラの読み取り精度が落ちるため、背景を明るく、前景を十分暗く保ってください。
ロゴを中央に重ねたい
まず ERROR_CORRECT_M を ERROR_CORRECT_H に変更して最大30%の欠損を許容できるようにします。そのうえで make_png_bytes 内で生成したQR画像 img に対し、Pillowの Image.paste() で中央にロゴを重ねる処理を追加してください。誤り訂正レベルがHなら、多少情報が隠れても読み取りに影響しません。
よくあるエラーと対処
qrcode.exceptions.DataOverflowError が出て失敗する
URLが長すぎてQRコード規格の最大容量(version 40 / 約4,000バイト)を超えたケースです。誤り訂正レベル M の英数字モードでは約2,300文字が上限で、それを超えると DataOverflowError: Code length overflow が発生します。bit.ly や TinyURL などの短縮URLサービスでリンクを短くしてから再実行するか、どうしても長いURLをそのまま埋めたい場合は ERROR_CORRECT_M を ERROR_CORRECT_L に下げると容量に余裕が出ます。
日本語URLをQR化するとスマホで読み取れない
qrcode はUTF-8のバイト列としてデータを格納するため、日本語URL自体のQR化は問題なく行えます。読めないのは受信側のQRリーダーがShift_JIS想定の古い実装であるケースが多いです。互換性を広げたい場合は、add_data(url) の前に url = urllib.parse.quote(url, safe=":/?#&=") を挟んでパーセントエンコード後の文字列を埋め込むと、ほとんどのリーダーで安定します。
PermissionError が出て出力ファイルが保存できない
同名ファイルが Excel / Word / PowerPoint などで開かれていると、Windowsのファイルロックで wb.save / doc.save / prs.save が失敗します。該当ファイルを閉じてから再実行してください。このスクリプトはファイル名に %Y%m%d_%H%M%S のタイムスタンプを含めるため、同じ秒で連続実行しない限り同名衝突は起きません。出力先フォルダに書き込み権限が無い場合も同じエラーになるので、C:\Users\<自分>\Documents 配下など書き込み可能な場所を指定してください。
FAQ
なぜSVGだけフォールバックが用意されているのですか?
qrcode.image.svg サブモジュールは環境によってはランタイムまで不足に気付けない性質のためです。PNG / JPG は Pillow ベースで本体に同梱されており、インストール時点で欠落が判明します。SVGはPython環境依存で実行時まで気付けない構造のため、ImportError を握って「何も出力されなかった」事故を防ぐ設計にしています。
誤り訂正レベルはなぜMがデフォルトですか?
読み取り性能とサイズのバランスが良いためです。L(7%)は汚れに弱く名刺印刷などで失敗しやすい一方、H(30%)はQRコードの情報密度が上がってサイズが大きくなります。Mは規格上のデフォルト値で、紙への印刷程度の劣化なら安定して読み取れます。
複数のURLをまとめて処理できますか?
このスクリプトは「1URL → 1ファイル」の設計なので、まとめ処理は対応範囲外です。URL一覧を一気に処理したい場合は、外側に for url in urls: のループを足して、ファイル名に連番を付与するカスタマイズが必要になります。CSVやExcelからの一括QR化は別スクリプトに分けた方が運用上きれいです。
Officeファイル内でQRコードの位置を動かせますか?
はい。生成される PPTX / DOCX / XLSX はそのまま各アプリで開ける通常のファイルなので、出力後にPowerPoint / Word / Excelでドラッグ移動できます。コード側でも add_picture / drawImage / xl_img.anchor の引数を変更すれば、初期位置やサイズをスクリプト実行時点で任意に調整可能です。
.pybes ファイルをPybesにインポートすると、スクリプトと設定フィールドが自動で読み込まれます。