Pythonでエクセル請求書をPDFに自動変換しメール送信する方法

業務効率化_デスクワーク Python
スポンサーリンク
スポンサーリンク

「Pythonで複数の宛先に個別の内容をメール送信したい。」

「煩わしい業務を自動化したい。」

上記の疑問にお答えします。

毎日の業務にもっと簡単で効率的な方法はないのかと思ったことはありませんか?

この記事では、Pythonを使用してエクセルの請求書をPDFに自動変換し、メールで送信する方法を分かりやすく解説します。

具体的に次のことを解説します。

  • 【準備】PythonでPDF化、メール送信する前に
  • 【紹介】使用するライブラリ
  • 【実践】ソースコードと解説

 

「偉そうに語るおまえは誰やねん。」と思われるので、私のことも少し紹介させてください。

たいらーのプロフィール

  • 文系四大出身。ソフトウェア開発の経験はなしですが、IT業界に身を置いています。
  • 開発者やユーザーとのパイプ役など、業務にプログラミングスキルを活かす。
  • Pythonは独学で習得。スクレイピングや作業の自動化などに勤しんでいます。

 

Pythonを使って業務を効率化することで、あなたの貴重な時間をもっと有意義なことに使えるようになります。

この記事を参考に、ぜひ業務にPythonを活用してみてください。

 

1.【準備】PythonでPDF化、メール送信する前に

ここでは、PDF化とメール送信をする前に必要な準備と、前提条件について説明します。

 

・Pythonのインストール

Pythonを始めるには、まずインストールと基本的な設定が必要です。

Pythonのインストールがまだの方は次の記事を参考にして、Pythonをインストールしましょう。

Python3のインストール方法【導入は10分で完了!】

 

・今回の前提条件

今回は、エクセルで作成された請求書のPDF化とメール送信をテーマに説明します。

業務フローは、よくありそうな次のケースを前提として、コードを実装します。

  • 請求書をエクセルで作成し、一つのファイルに複数の取引先をまとめている。
  • 請求書は一度PDF化して、取引先にメール送信している。

 

今回使用するエクセルファイルでは、複数の取引先をまとめたシートは、一覧(【図1】)シートとしています。

一覧には、「取引先名」、「取引金額」、「メールアドレス」などが記載されています。

【図2】は、請求書のサンプルです。セルのK3に送信先のメールアドレスを入力しています。

 

【図1】

請求書一覧

 

【図2】

請求書サンプル01

 

2.【紹介】使用するライブラリ

今回紹介するコードでは、次のライブラリを使用しています。

各ライブラリについて、簡単に説明しておきます。

 

xlwings

xlwingsは、Microsoft Excelファイルとのインタラクションを可能にするライブラリです。

PythonからExcelの読み書き、マクロの実行、Excelフォーミュラの使用などが可能です。

 

smtplib

smtplibは、Pythonで電子メールを送信するための標準ライブラリです。

SMTPプロトコルを使用してメールサーバーに接続し、メールの送信ができます。

 

os

osライブラリは、オペレーティングシステムとのやり取りを可能にするための機能を提供します。

ファイルやディレクトリの操作、パスの操作、システム情報の取得などが可能です。

 

email

emailライブラリは、電子メールメッセージを作成、解析、操作するための包括的なツールセットを提供します。

このライブラリは、メールメッセージの構築と解析のために、標準的な電子メールフォーマット(例えば、MIME)をサポートしています。

 

MIMEMultipart:

MIMEMultipartは、emailライブラリの一部で、複数のパートを持つメールメッセージを作成するために使用されます。

異なる種類のコンテンツ(テキスト、HTML、ファイル添付等)を一つのメールに組み合わせる際に使われます。

 

MIMEText

MIMETextもemailライブラリの一部で、テキストベースのメールメッセージを作成する際に使用されます。

プレーンテキストやHTML形式のメール本文を作成するのに適しています。

 

MIMEBase

MIMEBaseは、emailライブラリの中で、メールの各パートの基底クラスとして機能します。

カスタムメールメッセージの作成や特殊なメールコンテンツの扱いに使われます。

 

encoders

encodersは、emailライブラリの一部で、メールのコンテンツをエンコードする際に使用されるモジュールです。

添付ファイルなどの非テキストコンテンツを適切な形式にエンコードするのに役立ちます。

 

これらの情報を表にまとめると以下のようになります。

ライブラリ/モジュール概要
xlwingsExcelファイルとのインタラクションを可能にするライブラリ。
smtplibSMTPプロトコルを使用して電子メールを送信するためのライブラリ。
osオペレーティングシステムとのインタラクションを可能にする機能を提供。
email電子メールの作成、送受信、解析を行うための標準ライブラリ。
MIME標準に準拠したメールメッセージの管理に広く用いられる。
MIMEMultipart複数のパートを持つメールメッセージを作成するためのモジュール。
MIMETextテキストベースのメールメッセージを作成するためのモジュール。
MIMEBaseメールの各パートの基底クラスとして機能するモジュール。
encodersメールのコンテンツをエンコードするためのモジュール。

 

3.【実践】ソースコードと解説

下記は、ソースコードの全文です。解説は後述します。

 

・ソースコード

import xlwings as xw
import smtplib, os
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders

#エクセルの一覧を読み込み
def read_list_sheet(workbook, sheet_name='一覧'):
  sheet = workbook.sheets[sheet_name]
  values = sheet.range('C2:G102').options(numbers=str).value # C列とG列のデータを読み込む
  return {row[0]: row[4] for row in values if row[0] and row[4]} # 取引先名をキーとしてメールアドレスを保存

#PDFファイルの作成
def create_pdf_from_excel(excel_path, output_folder, skip_sheet_name='一覧', cell_address="A2"):
  app = xw.App(visible=False)
  pdf_files_info = []

  try:
    wb = app.books.open(excel_path)

    # 一覧シートのデータを読み込む
    list_data = read_list_sheet(wb, skip_sheet_name)

    for sheet in wb.sheets:
      if sheet.name == skip_sheet_name:
        continue

      # 特定のセルからデータを読み取る
      cell_value = sheet.range(cell_address).value

      pdf_path = f"{output_folder}/{cell_value}.pdf"
      sheet.api.ExportAsFixedFormat(0, pdf_path)

      # 取引先名に対応するメールアドレスを取得
      email = list_data.get(sheet.name, 'メールアドレス不明')
      pdf_files_info.append((pdf_path, sheet.name, email))

  except Exception as e:
    print(f'エラーが発生しました: {e}')

  finally:
    wb.close()
    app.quit()

  return pdf_files_info

#出力結果との突合
def display_matching_results(pdf_files_info):
  print('出力結果(PDFファイル名, 取引先名, メールアドレス):')
  for info in pdf_files_info:
    print(',' .join(info))

#PDFをメール送信
def send_email(server_address, port_num, sender_address, server_pw, subject, body, pdf_files_info):

  for info in pdf_files_info:
    # メールサーバーの設定
    smtp_server = server_address # SMTPサーバーのアドレス
    smtp_port = port_num # SMTPサーバーのポート
    smtp_user = sender_address # 送信元のメールアドレス
    smtp_password = server_pw # SMTPサーバーのパスワード

    # メールメッセージの作成
    msg = MIMEMultipart()
    msg['From'] = smtp_user
    msg['To'] = info[1]
    msg['Subject'] = subject
    msg.attach(MIMEText(body, 'plain'))

    # 添付ファイルの追加
    attachment_path = str(info[0]) # ここでフルパスを取得
    attachment_filename = str(os.path.basename(attachment_path)) # ファイル名のみを抽出

    attachment = open(attachment_path, 'rb')
    p = MIMEBase('application', 'pdf')
    p.set_payload((attachment).read())
    encoders.encode_base64(p)
    p.add_header('Content-Disposition', 'attachment', filename=attachment_filename)

    msg.attach(p)

    #メールサーバーへの接続とメールの送信
    server = smtplib.SMTP(smtp_server, smtp_port)
    server.starttls()
    server.login(smtp_user, smtp_password)
    text = msg.as_string()
    server.sendmail(smtp_user, info[2], text)
    server.quit()

    attachment.close()

#実行
excel_file = 'C:/エクセルファイルのパス'
output_folder = 'C:/PDFファイルの出力先'

pdf_datalist = create_pdf_from_excel(excel_file, output_folder)
display_matching_results(pdf_datalist)

server = 'SMTPサーバーのアドレス'
port = SMTPサーバーのポート(整数)
sender = '送り主のメールアドレス'
password = 'SMTPサーバーのパスワード'
mail_title = '請求書のご送付'
mail_message = 'いつもお世話になっております。\n株式会社○○の××です。\n\n請求書を送付いたします。\nご査収の程よろしくお願いいたします。'

send_email(server, port, sender, password, mail_title, mail_message, pdf_datalist)

 

・解説

伝わりにくい部分をピックアップして解説します。

14行目から46行は、エクセルファイルをPDFにする関数です。

引数に、取り込むエクセルファイルのパス(excel_path)、PDFファイルの出力先(output_folder)と取り込みを除外したいシート(skip_sheet_name=’一覧’)、PDFファイル名などで使用する、エクセルファイルの取引先名の取り込み(cell_address=”A2″)を設定しています。

出力結果の突合やメール送信で、PDFファイルの出力先、シート名、メールアドレスを使用したいので、返り値(pdf_files_info)を設定しています。

#PDFファイルの作成
def create_pdf_from_excel(excel_path, output_folder, skip_sheet_name='一覧', cell_address="A2"):
  ・・・
  return pdf_files_info

 

48行目から52行目では、作成したPDFファイルが正しく出力されているか、コンソール上で確認できる関数です。

#出力結果との突合
def display_matching_results(pdf_files_info):
  print('出力結果(PDFファイル名, 取引先名, メールアドレス):')
  for info in pdf_files_info:
    print(',' .join(info))

 

54行目から91行目が、作成したPDFファイルを添付して、メール送信する関数です。

引数の説明は次のとおりです。

server_address:  SMTPサーバーのアドレス
port_num:    SMTPサーバーのポート
sender_address: 送信元のメールアドレス
server_pw:     SMTPサーバーのパスワード
subject:     メールの件名
body:      メールの本文
pdf_files_info:  送信先のデータ(PDFファイルのバスなど)

def create_pdf_from_excel関数の返り値(pdf_files_info)を使って、メールの宛先やメールアドレスを設定しています。

#PDFをメール送信
def send_email(server_address, port_num, sender_address, server_pw, subject, body, pdf_files_info):
    ・・・
    attachment.close()

 

93行目以降で、関数を呼び出し、値を入力しています。

SMTPサーバーのパスワードなど、コード上にベタ打ちしています。

実際の運用では、機密情報の管理や不正アクセスから保護する必要がありますが、一番簡単な方法としてはこのような形になります。

 

4.まとめ

このようにPythonを使用すれば、ルーチンワークなど業務の手間を省くことができます。

記事で紹介した技術を活用して、日々の業務をよりスマートに進めましょう。

このブログでは、Pythonによる業務効率化やプログラミング初心者のためのPython学習法を紹介していますので、ぜひご覧ください。

 

ご清聴ありがとうございました。