基幹システムや EC サイトの管理画面から商品データを CSV で取り込み、内容が 1 列の表として読み込まれると、「A-101,ノートPC,128000,12」のように、複数の項目が 1 つのセルにまとまってしまうことがあります。このままでは商品コードや単価を項目ごとに並べ替えたり集計したりしにくく、作業のたびに手作業で列を分ける必要があります。 Excel の[データ]タブにある[区切り位置]を使えば、区切り文字を指定して列を分割できます。ただし、同じ形式のファイルが毎日届く場合や、同じ処理を何度も繰り返したい場合には、Python スクリプトで自動化するのが便利です。 本記事では、Spire.XLS for Python を使って、1 列にまとまったテキストを複数の列に分割する方法を紹介します。カンマ区切りの基本から、セミコロン・読点などが混在するデータ、分割した金額を数値セルとして書き込む方法まで、実際の出力を確認しながら解説します。 クイックナビゲーション 実行環境とサンプルデータ 1 列のテキストを複数列に分ける仕組み カンマ区切りの商品リストを分割する 区切り文字が複数混在する取引先リスト 分割した金額と数量を数値セルにする 見出し・罫線・列幅を整える まとめ FAQ 実行環境とサンプルデータ 本記事のコードは Spire.XLS for Python(動作確認は 16.8.2)と plum-dispatch v1.7.4 で検証しています。Excel 本体は必要なく、次の pip コマンドだけで環境が整います。 pip install Spire.XLS スクリプトでは、次の 2 行をインポートします。 from spire.xls import * from spire.xls.common import * サンプルは、商品リストと取引先リストの 2 つのシートを持つ TextList.xlsx です。商品リストの A 列には「商品コード,商品名,単価,在庫数」がカンマでつながった文字列が入っています。 以降のコードでは、この A 列を読み取り、B 列以降に項目を書き出します。元の A 列は確認用に残しておくため、分割前と分割後を並べて見比べることができます。 1 列のテキストを複数列に分ける仕組み Excel の[区切り位置]ウィザードでは、対象の列を選び、区切り文字を指定してプレビューを確認しながら列を分けます。Python でも考え方は同じで、次の 3 ステップに分けられます。 取り出す — セルから文字列を取得する 分割する — 区切り文字で split() する 書き込む — 分割した文字列を B 列以降に代入する それぞれの処理を、Excel の操作と Python のコードで対応させると次のようになります。 処理 Excel の操作 Python のコード 元の文字列を取得 セルを選択して内容を確認 sheet.Range[row, 1].Text カンマで分割 [区切り位置]でカンマを選択 text.split(",") 複数の区切り文字で分割 区切り文字を追加 re.split() に文字クラスを渡す 列に書き込む [完了]で分割を実行 sheet.Range[row, col].Text = value 列幅を調整 列の境界をダブルクリック AutoFitColumns() / ColumnWidth CellRange.Text は、セルに入っている文字列を返すプロパティです。分割した結果を文字列として書き込むときは CellRange.Text、金額や数量として計算に使いたいときは CellRange.NumberValue に代入します。 まずは最小限のコードで、1 行分の分割を確認してみます。 text = sheet.Range[2, 1].Text # 'A-101,ノートPC,128000,12' fields = text.split(",") # ['A-101', 'ノートPC', '128000', '12'] for j, value in enumerate(fields, start=2): sheet.Range[2, j].Text = value.strip() enumerate(..., start=2) を使うと、分割した 1 つ目の値を B 列、2 つ目を C 列へと順番に書き込めます。strip() は、カンマの後ろに入った半角スペースを取り除くためのものです。 なお、Worksheet.Range[行, 列] の行・列は 1 始まりです。一方、Worksheet.Columns[i] のインデックスは 0 始まりなので、A 列は Columns[0]、B 列は Columns[1] になります。列幅を指定するときに混同しやすいので注意してください。 カンマ区切りの商品リストを分割する 実際のシート全体を処理するコードは次のとおりです。ここでは 1 行目を元の見出し(商品情報)に使い、2 行目から最終行までをループします。 ブックを読み込む 最初のワークシートを取得する B〜E 列に分割後の見出しを書く 2 行目から LastRow までループする A 列の文字列をカンマで分割し、B〜E 列に書き込む 列幅を整えて別名で保存する from spire.xls import * from spire.xls.common import * # Workbook オブジェクトを作成し、ファイルを読み込む workbook = Workbook() workbook.LoadFromFile("TextList.xlsx") # 最初のワークシートを取得 sheet = workbook.Worksheets[0] # 分割後の見出し headers = ["商品コード", "商品名", "単価", "在庫数"] for j, header in enumerate(headers, start=2): sheet.Range[1, j].Text = header # 2 行目から最終行まで、A 列のテキストを分割して B〜E 列に書き込む for i in range(2, sheet.LastRow + 1): text = sheet.Range[i, 1].Text fields = text.split(",") for j, value in enumerate(fields, start=2): sheet.Range[i, j].Text = value.strip() # 列幅を整える sheet.AllocatedRange.AutoFitColumns() sheet.Columns[0].ColumnWidth = 32 sheet.Columns[1].ColumnWidth = 14 sheet.Columns[2].ColumnWidth = 18 sheet.Columns[3].ColumnWidth = 12 sheet.Columns[4].ColumnWidth = 10 # 別名で保存 workbook.SaveToFile("SplitByComma.xlsx", ExcelVersion.Version2016) workbook.Dispose() 実行すると、A 列の文字列が B〜E 列に分かれ、元の A 列はそのまま残ります。分割結果と元の文字列を見比べられるので、変換が正しく行われたかを確認しやすくなります。 range(2, sheet.LastRow + 1) の +1 は、range() の終端が含まれないためです。見出し行がないシートなら range(1, sheet.LastRow + 1) にします。sheet.LastRow は、データが入っている最終行を返します。 この時点では、単価と在庫数も文字列として書き込まれています。Excel の画面では、数値が文字列として保存されていることを示すエラーチェックの緑の三角が、セルの左上に表示されることがあります(2 枚目の画像)。合計や並べ替えに使う場合は、次の「分割した金額と数量を数値セルにする」の方法を使ってください。 区切り文字が複数混在する取引先リスト 取引先リストのように、データによって区切り文字が異なることもあります。次の例では、セミコロン(;)、カンマ(,)、縦棒(|)、読点(、)が混在しています。 株式会社サクラ;東京都千代田区;03-1234-5678 株式会社アオバ, 大阪府大阪市, 06-2345-6789 株式会社ミドリ|名古屋市中区|052-345-6789 株式会社ホシノ、福岡県福岡市、092-456-7890 このような場合は、Python の re モジュールの re.split() を使い、区切り文字を文字クラスでまとめて指定します。 import re from spire.xls import * from spire.xls.common import * workbook = Workbook() workbook.LoadFromFile("TextList.xlsx") # 2 番目のワークシート(取引先)を取得 sheet = workbook.Worksheets[1] headers = ["会社名", "所在地", "電話番号"] for j, header in enumerate(headers, start=2): sheet.Range[1, j].Text = header for i in range(2, sheet.LastRow + 1): text = sheet.Range[i, 1].Text fields = [f.strip() for f in re.split(r"[,;|、]+", text)] for j, value in enumerate(fields, start=2): sheet.Range[i, j].Text = value sheet.AllocatedRange.AutoFitColumns() sheet.Columns[0].ColumnWidth = 46 sheet.Columns[1].ColumnWidth = 20 sheet.Columns[2].ColumnWidth = 20 sheet.Columns[3].ColumnWidth = 16 workbook.SaveToFile("SplitMultiDelimiter.xlsx", ExcelVersion.Version2016) workbook.Dispose() r"[,;|、]+" の + は「1 文字以上の区切り文字」を意味します。カンマの後に半角スペースが入っていても、各要素に strip() をかけることで前後の空白を落とせます。 区切り文字が 1 種類だけなら str.split() で十分です。空白で区切られたテキストは、引数なしの text.split() を使うと、連続する空白を 1 つの区切りとして扱えます。 fields = text.split() # 空白(連続する空白も 1 つ)で分割 一方、text.split(",") のように区切り文字を指定した場合は、連続するカンマの間に空の要素が残ります。空欄を詰めたいときは、[f for f in fields if f] で空文字を除いてから書き込むとよいでしょう。 元データが CSV ファイルなら、Python で CSV をリストへ変換する方法 で一度 Python のリストに読み込んでから、必要な列だけを Excel に書き出す構成も取れます。 分割した金額と数量を数値セルにする 分割した単価や在庫数を、Excel 上で数値として扱いたい場合があります。CellRange.Text に代入すると文字列として保存されます。合計や並べ替えに使うときは、CellRange.NumberValue で数値として書き込みます。 from spire.xls import * from spire.xls.common import * workbook = Workbook() workbook.LoadFromFile("TextList.xlsx") sheet = workbook.Worksheets[0] headers = ["商品コード", "商品名", "単価", "在庫数"] for j, header in enumerate(headers, start=2): sheet.Range[1, j].Text = header for i in range(2, sheet.LastRow + 1): fields = [f.strip() for f in sheet.Range[i, 1].Text.split(",")] # 商品コードと商品名は文字列のまま sheet.Range[i, 2].Text = fields[0] sheet.Range[i, 3].Text = fields[1] # 単価と在庫数は数値セルとして書き込む sheet.Range[i, 4].NumberValue = int(fields[2]) sheet.Range[i, 5].NumberValue = int(fields[3]) # 金額と数量の表示形式を設定する last = sheet.LastRow sheet.Range[2, 4, last, 4].NumberFormat = "¥#,##0" sheet.Range[2, 5, last, 5].NumberFormat = "#,##0" sheet.AllocatedRange.AutoFitColumns() sheet.Columns[0].ColumnWidth = 32 sheet.Columns[1].ColumnWidth = 14 sheet.Columns[2].ColumnWidth = 18 sheet.Columns[3].ColumnWidth = 12 sheet.Columns[4].ColumnWidth = 10 workbook.SaveToFile("SplitAsNumbers.xlsx", ExcelVersion.Version2016) workbook.Dispose() NumberValue に int() で変換した値を渡すと、セルは数値として保存されます。NumberFormat を "¥#,##0" にすると、画面には「¥128,000」のように表示されますが、セルの中身は 128000 の数値のままです。合計や並べ替え、条件付き書式の対象にできます。 商品コードのように先頭の 0 を残したい項目は、CellRange.Text で書き込みます。CellRange.NumberValue は数値として扱うため、001 は 1 になります。項目ごとに「文字列として残すか、数値として扱うか」を決めておくと、あとから集計しやすくなります。 分割した値を Excel の数値や日付に変換する考え方については、Python で Excel のテキストと数値を相互変換する方法 で詳しく解説しています。 見出し・罫線・列幅を整える 最後に、分割後の表を見やすく整えます。列幅は Worksheet.AllocatedRange.AutoFitColumns() でデータに合わせて自動調整し、日本語の見出しが詰まって見える場合は Worksheet.Columns[i].ColumnWidth で微調整します。列番号は 0 始まりなので、A 列は Columns[0]、B 列は Columns[1] です。 見出し行の体裁は CellRange.Style、表全体の罫線は CellRange.Borders で設定できます。ここまでの処理をまとめると、次のようになります。 # 列幅を自動調整し、日本語の見出しに合わせて微調整する sheet.AllocatedRange.AutoFitColumns() sheet.Columns[0].ColumnWidth = 32 sheet.Columns[1].ColumnWidth = 14 sheet.Columns[2].ColumnWidth = 18 sheet.Columns[3].ColumnWidth = 12 sheet.Columns[4].ColumnWidth = 10 # 見出し行(B1〜E1)の体裁 head = sheet.Range[1, 2, 1, 5] head.Style.Color = Color.get_Black() head.Style.Font.Color = Color.get_White() head.Style.Font.IsBold = True head.Style.HorizontalAlignment = HorizontalAlignType.Center # 表全体(A1〜E7)に細い罫線を引く used = sheet.Range[1, 1, sheet.LastRow, 5] for edge in (BordersLineType.EdgeLeft, BordersLineType.EdgeRight, BordersLineType.EdgeTop, BordersLineType.EdgeBottom): used.Borders[edge].LineStyle = LineStyleType.Thin 記事内の分割結果の画像では、ここで紹介した見出しの体裁と罫線も適用しています。ここまでの処理を関数にまとめておくと、シートが増えても同じ手順で分割できます。分割したデータをいったん Python のリストにまとめてから書き込む場合は、Python のリストを Excel に変換する方法 が参考になります。 まとめ 本記事で扱った内容を整理します。 やりたいこと 指定する区切り文字 / API ポイント カンマで分割 text.split(",") strip() で前後の空白を除去 セミコロン・読点など複数 re.split(r"[,;、]+", text) + で連続する区切り文字をまとめる 空白で分割 text.split() 連続する空白を 1 つとして扱う 数値として書き込む CellRange.NumberValue NumberFormat で表示を整える 文字列として残す CellRange.Text 先頭の 0 を保てる 列幅を整える AutoFitColumns() / ColumnWidth 日本語の見出しは微調整 縦棒(|)も区切りに含める場合は、文字クラスに | を追加します。 1 列にまとまったテキストは、「取り出す → 分割する → 書き込む」の 3 ステップで複数の列に変換できます。元の列を残しておけば、分割結果と見比べながら確認できます。処理を関数化して、定期的に届く CSV の整形に役立ててください。 Spire.XLS for Python の全機能を評価したい場合は、30 日間の無料ライセンスを申請できます。なお無償の試用版では、保存したファイルに評価用の警告シートが追加されます。ライセンスを適用するとこれも出力されなくなります。 FAQ カンマ以外の区切り文字でも分割できますか? はい。text.split(";") のように区切り文字を変えられます。複数の区切り文字が混在する場合は、re.split(r"[,;|、]+", text) のように正規表現の文字クラスを使います。区切り文字は、データの形式に合わせて指定してください。 連続する空白を 1 つの区切りとして扱うには? 引数なしの text.split() を使います。"A-101 ノートPC 128000" のように空白が続いても、空の要素を挟まずに分割できます。区切り文字を明示したい場合は text.split(" ") ですが、この場合は連続する空白の間に空の要素が残ります。 分割した金額を合計できる数値にするには? CellRange.NumberValue に int() または float() で変換した値を代入します。CellRange.Text は文字列として書き込むため、数値計算に使うときは NumberValue を選んでください。表示形式は NumberFormat で設定できます。 商品コードの先頭の 0 を残すには? CellRange.Text で書き込みます。NumberValue は数値として扱うため、001 は 1 になります。商品コードや電話番号など、桁数を保ちたい項目は文字列のままにしておきましょう。 行によって列数が違う場合はどうすればよいですか? あらかじめ列数を決めておき、足りない分を空文字で補うと、後続の処理が安定します。 COLUMNS = 4 for i in range(2, sheet.LastRow + 1): fields = [f.strip() for f in sheet.Range[i, 1].Text.split(",")] fields += [""] * (COLUMNS - len(fields)) for j, value in enumerate(fields[:COLUMNS], start=2): sheet.Range[i, j].Text = value 余分な列がある場合は fields[:COLUMNS] で切り捨てます。列数を固定しておくと、見出し行とデータ行の対応が崩れにくくなります。数値として扱う項目がある場合は、sheet.Range[i, j].Text の代わりに NumberValue を使ってください。 元の列を残したまま、別のシートに分割結果を書き出せますか? はい。分割結果を書き込むシートを別に用意し、元のシートは読み取り専用として扱います。Worksheets.Add() で分割結果用のシートを追加すると、元のシートをそのまま残せます。 src = workbook.Worksheets[0] dst = workbook.Worksheets.Add("分割結果") for i in range(2, src.LastRow + 1): fields = [f.strip() for f in src.Range[i, 1].Text.split(",")] for j, value in enumerate(fields, start=2): dst.Range[i, j].Text = value workbook.SaveToFile("SplitToNewSheet.xlsx", ExcelVersion.Version2016) workbook.Dispose() 追加したシートは末尾に作られるため、元のシートの内容と突き合わせながら分割結果を確認できます。シートの追加方法は、Python:Excel ファイルにワークシートを追加する方法 でも詳しく解説しています。