PDF・Word・PowerPoint・Excelファイルのパスを入力すると、ファイルの文字列部分を連結した単一のstrオブジェクトを返すメソッド

##１. 実行時に走らせるコード

以下を実行すると、__引数に渡したファイルパスのファイル__が、以下のいずれかである場合、__ファイルの中にあるテキスト文字列を文字列連結した、単一の*str*型オブジェクトが、返されます。__

1. PDFファイル
2. Wordファイル
3. PowerPointファイル
4. Excelファイル

__返り値__は、__単一の*str*）型オブジェクト__です。


##（ 事前にインストールしておくべき資源 ）

```bash:
pip install python-docx
pip install openpyxl
```

##（ 実行例 ）

```bash:Terminal
% ls
000717009.xlsx			JNGI2019_Zentai.pptx		honbun.docx
29zen10kai2.pdf			__pycache__			text_from_string_files.py       get_text_from_arg_filepath.py
% ipython
```

```Python3:ipython(Python3.9.0)
In [1]: os.listdir()
Out[1]: 
['text_from_string_files.py',
 'JNGI2019_Zentai.pptx',
 '__pycache__',
 '000717009.xlsx',
 '29zen10kai2.pdf',
 'honbun.docx',
 'get_text_from_arg_filepath.py']

In [2]: from get_text_from_arg_filepath import *

In [3]: get_filename_text_dict('29zen10kai2.pdf')
Out[3]: "['平29.６.190－１総1']['近年の納税実務等を巡る環境変化について「アダム・スミスの４原則」について']['納税実務等を巡る近年の環境変化への対応に向けた海外調査について「アダム・スミスの４原則」について～平成29年１月27日政府税制調査会資料（抜粋）～']['規制改革推進に関する第１次答申（平成29年5月23日規制改革推進会議）（電子申告関係部分）']['e-Tax（国税電子申告・納税システム）']['法人税の電子申告の状況について（未定稿）']['規制改革推進に関する第１次答申（平成29年5月23日規制改革推進会議）（年末調整関係部分）']['日本における給与所得者の年末調整事務の流れ']['個人所得税の納税手続に関する諸外国比較（年末調整がある国）「アダム・スミスの４原則」について']['個人所得税の納税手続に関する諸外国比較（年末調整がない国）「アダム・スミスの４原則」について']['被用者の納税に係る事務負担の在り方（イメージ）']['参考資料']['個人のＩＣＴ化の状況']['企業活動におけるＩＣＴ化の状況（１）']['企業活動におけるＩＣＴ化の状況（２）（日本の電子商取引(EC)市場規模の推移）']['ICT化に伴う経済取引の多様化（例：シェアリングエコノミー、ＦｉｎＴｅｃｈ分野における決済・送金）']['所得税に係る税務手続の流れ（個人事業主を例として）']['（施行開始）']['その他の環境変化に対応した主な施策']['日本の所得税の構造（イメージ）']['税額']['【税額調整目的：控除】']['（2017年１月現在）']['税額']['個人単位課税']['世帯単位課税（N分N乗方式）']['個人単位課税']['税額']['「アダム・スミスの４原則」について「アダム・スミスの４原則」について']['「ワグナーの４大原則・９原則」について「アダム・スミスの４原則」について']"

In [4]: result = get_filename_text_dict('honbun.docx')

In [5]: type(result)
Out[5]: str

In [6]: len(result)
Out[6]: 12505

In [7]: print(result[0:200])
2018年６月25日財務省行政情報化推進委員会財務省デジタル・ガバメント中長期計画Ⅰ基本事項（１）目的財務省は、「健全な財政の確保、適正かつ公平な課税の実現、税関業務の適正な運営、国庫の適正な管理、通貨に対する信頼の維持及び外国為替の安定の確保を図ること」（財務省設置法第３条）を任務とし、行政事務の効率化及び国民・事業者の利便性向上を目的として、所掌事務に関連する分野においてシステムを導入し、コス

In [8]: 
```

####ファイル渡しは、__相対パス__と__絶対パス__、どちらでもOK

これまで、テキストファイルは、__Pythonスクリプトファイルを実行しているディレクトリ空の相対パスで__、自作メソッドに渡していました。

```Python3:iPython（Python3.9.0）
In [3]: import pathlib

In [4]: tmp = '29zen10kai2.pdf'

In [5]: p = pathlib.Path(tmp)

In [6]: p
Out[6]: PosixPath('29zen10kai2.pdf')

```
#### __絶対パスに変換__

__（ 参考 )__

・ [note.nkmk.me 「Python, pathlibで絶対パスと相対パスを相互変換・判定」](https://note.nkmk.me/python-pathlib-absolute-relative/)
・ [Hbk project 「【Python】絶対パス・相対パスの取得方法（os.pathモジュール）」](https://hibiki-press.tech/python/os_path_abspath/1021)
・ [Python活用　パスの取り扱い（絶対パスと相対パスを操る）](https://itstudio.co/2020/07/14/10844/)

```Python3:iPython（Python3.9.0）
In [7]: print(p.resolve())
/Users/ocean/Desktop/files_test/29zen10kai2.pdf

In [10]: abs_path = p.resolve()

In [11]: abs_path
Out[11]: PosixPath('/Users/ocean/Desktop/files_test/29zen10kai2.pdf')

```

#### __絶対パスで、テキストファイルを自作メソッドの引数に渡す__

__うまく動いた。__

```Python3:iPython（Python3.9.0）
In [12]: get_filename_text_dict(abs_path)
Out[12]: "['平29.６.190－１総1']['近年の納税実務等を巡る環境変化について「アダム・スミスの４原則」について']['納税実務等を巡る近年の環境変化への対応に向けた海外調査について「アダム・スミスの４原則」について～平成29年１月27日政府税制調査会資料（抜粋）～']['規制改革推進に関する第１次答申（平成29年5月23日規制改革推進会議）（電子申告関係部分）']['e-Tax（国税電子申告・納税システム）']['法人税の電子申告の状況について（未定稿）']['規制改革推進に関する第１次答申（平成29年5月23日規制改革推進会議）（年末調整関係部分）']['日本における給与所得者の年末調整事務の流れ']['個人所得税の納税手続に関する諸外国比較（年末調整がある国）「アダム・スミスの４原則」について']['個人所得税の納税手続に関する諸外国比較（年末調整がない国）「アダム・スミスの４原則」について']['被用者の納税に係る事務負担の在り方（イメージ）']['参考資料']['個人のＩＣＴ化の状況']['企業活動におけるＩＣＴ化の状況（１）']['企業活動におけるＩＣＴ化の状況（２）（日本の電子商取引(EC)市場規模の推移）']['ICT化に伴う経済取引の多様化（例：シェアリングエコノミー、ＦｉｎＴｅｃｈ分野における決済・送金）']['所得税に係る税務手続の流れ（個人事業主を例として）']['（施行開始）']['その他の環境変化に対応した主な施策']['日本の所得税の構造（イメージ）']['税額']['【税額調整目的：控除】']['（2017年１月現在）']['税額']['個人単位課税']['世帯単位課税（N分N乗方式）']['個人単位課税']['税額']['「アダム・スミスの４原則」について「アダム・スミスの４原則」について']['「ワグナーの４大原則・９原則」について「アダム・スミスの４原則」について']"

In [13]:
```


##実装コード

```Python3:get_text_from_arg_filepath.py
import docx, openpyxl, pptx, os.path
from pdfminer.converter import PDFPageAggregator
from pdfminer.layout import LAParams, LTContainer, LTTextBox
from pdfminer.pdfinterp import PDFPageInterpreter, PDFResourceManager
from pdfminer.pdfpage import PDFPage

####指定したディレクトリから、テキストファイルを抽出できるファイルを検索するメソッド
def get_text_matching_file_names(ext_pattern_list, search_directory_path) -> list:
    file_names_list = os.listdir(search_directory_path)
    # 上記のいずれかの拡張子を持つファイルのファイル名を格納する配列を用意
    available_filename_list = [] 
    # 上記のいずれかの拡張子を持つファイルのファイル名を配列に格納する
    for file_name in file_names_list:
        ext_check_result = []
        for ext in ext_pattern_list:
            boolean = ext in file_name
            ext_check_result.append(boolean)
        if True in ext_check_result:
            available_filename_list.append(file_name)
    return  available_filename_list

####PDFファイル（拡張子：.pdf)からテキスト抽出するメソッド
def get_text_list_recursively(layout) -> list:
    if isinstance(layout, LTTextBox):
        return [layout.get_text()]
    if isinstance(layout, LTContainer):
        text_list = []
        for child in layout:
            text_list.extend(get_text_list_recursively(child))
            return text_list

def get_text_from_pdf(filepath: str) -> str:
    laparams = LAParams(detect_vertical=True)
    resource_manager = PDFResourceManager()
    device = PDFPageAggregator(resource_manager, laparams=laparams)
    interpreter = PDFPageInterpreter(resource_manager, device)
    results = []
    with open(filepath, "rb") as file:
        for page in PDFPage.get_pages(file):
            interpreter.process_page(page)
            layout = device.get_result()
            text = str(get_text_list_recursively(layout))
            text = text.replace("\\n", "").replace("\n", "").replace(" ", "").replace("　", "")
            results.append(text)        

    output = "".join(results)
    return output


####PowerPointファイル（拡張子：.pptx)からテキスト抽出するメソッド
def get_text_from_powerpoint(filepath: str) -> str:
    prs = pptx.Presentation(filepath)
    output = ""
    for i, sld in enumerate(prs.slides, start=1):
        for shp in sld.shapes:
            if shp.has_text_frame:
                text = shp.text
                output += text.replace("\n", "").replace("\n", "").replace(" ", "").replace("　", "")

    return output


####Wordファイル（拡張子：.pptx)からテキスト抽出するメソッド
def get_text_from_word(filepath: str) -> str:
    document = docx.Document(filepath)
    text_list = list(map(lambda par: par.text, document.paragraphs))
    text = "".join(text_list)
    output = text.replace(" ", "").replace("　", "")
    return output

####Excelファイル（拡張子：.xlsx)からテキスト抽出するメソッド
def get_text_from_excel(filepath: str) -> str:
    output_text = ""
    book = openpyxl.load_workbook(filepath)
    sheet_list = book.sheetnames
    for sheet_name in sheet_list:
        sheet = book[sheet_name]
        for cells in tuple(sheet.rows):
            for cell in cells:
                data = cell.value
                if data is None:
                    continue
                else:
                    output_text += str(data).replace(" ", "").replace("　", "")
    return output_text


####Key: ファイルの拡張子に応じて、Value: 適切なメソッドを選択する際に用いる辞書
func_dict = {
    'pdf' : get_text_from_pdf,
    'docx'  : get_text_from_word,
    'xlsx'  : get_text_from_excel,
    'pptx' : get_text_from_powerpoint
         }

####mainメソッド
def get_filename_text_dict(file_path):
	# パスからファイルの「ファイル名.拡張子」を取得
	basename = os.path.basename(file_path)
	filename_with_ext = basename
	available_ext_list = list(func_dict.keys())
    # ファイルの拡張子に応じた適切なテキストデータ抽出メソッドを実行
	for ext in available_ext_list:
		if ext in filename_with_ext:
			text = func_dict[ext](filename_with_ext)
    	
	return  text

```

___

##（ リファクタリング版 ）

####__（ 修正点１ ）__
以下のWebページを参考に、__引数として受け取ったファイルパスから、ファイルの拡張子を取得する部分__を修正。

・ [ファイルの拡張子を取得する](https://www.gesource.jp/programming/python/code/0010.html)

####__（ 修正点２ ）__
メソッド名から、*filename*および*dict*の文字列を削除。

> 返り値は*str*型であり、返り値にはfilenameは含まれない為。このスクリプトファイルは当初、ファイル名とファイル名の中身のテキスト文字列を、*key*と*value*に収めた*dict*型オブジェクトを返す仕様であった。その当時の名残が、残ったままになってしまっていた。


```Python3:
def get_text_from_file(file_path):
    # パスからファイルの「ファイル名.拡張子」を取得
    # https://www.gesource.jp/programming/python/code/0010.html
    root, ext = os.path.splitext(file_path)
    available_ext_list = list(func_dict.keys())
    # ファイルの拡張子に応じた適切なテキストデータ抽出メソッドを実行
    for ok_ext in available_ext_list:
        if ext == ok_ext:
            text = func_dict[ext](file_path)
    
    if not(ext in available_ext_list):
        text = ""
        
    return text

```

###修正後のスクリプトファイルの使い方

```Python3:ipython（Python3.9.0）
In [4]: from get_text_from_arg_filepath import *

In [5]: result = get_text_from_file('29zen10kai2.pdf')

In [6]: print(type(result))
<class 'str'>

In [7]: print(len(result))
815

In [8]: print(result[0:200])
['平29.６.190－１総1']['近年の納税実務等を巡る環境変化について「アダム・スミスの４原則」について']['納税実務等を巡る近年の環境変化への対応に向けた海外調査について「アダム・スミスの４原則」について～平成29年１月27日政府税制調査会資料（抜粋）～']['規制改革推進に関する第１次答申（平成29年5月23日規制改革推進会議）（電子申告関係部分）']['e-Tax（国税電子申告・納税

In [9]: result2 = get_text_from_file('honbun.docx')

In [10]: print(type(result2))
<class 'str'>

In [11]: print(len(result2))
12505

In [12]: print(result2[0:200])
2018年６月25日財務省行政情報化推進委員会財務省デジタル・ガバメント中長期計画Ⅰ基本事項（１）目的財務省は、「健全な財政の確保、適正かつ公平な課税の実現、税関業務の適正な運営、国庫の適正な管理、通貨に対する信頼の維持及び外国為替の安定の確保を図ること」（財務省設置法第３条）を任務とし、行政事務の効率化及び国民・事業者の利便性向上を目的として、所掌事務に関連する分野においてシステムを導入し、コス

In [13]: 
```

###修正後のスクリプトファイル

```Python3:get_text_from_arg_filepath.py
import docx, openpyxl, pptx, os.path
from pdfminer.converter import PDFPageAggregator
from pdfminer.layout import LAParams, LTContainer, LTTextBox
from pdfminer.pdfinterp import PDFPageInterpreter, PDFResourceManager
from pdfminer.pdfpage import PDFPage

####指定したディレクトリから、テキストファイルを抽出できるファイルを検索するメソッド
def get_text_matching_file_names(ext_pattern_list, search_directory_path) -> list:
    file_names_list = os.listdir(search_directory_path)
    # 上記のいずれかの拡張子を持つファイルのファイル名を格納する配列を用意
    available_filename_list = []
    # 上記のいずれかの拡張子を持つファイルのファイル名を配列に格納する
    for file_name in file_names_list:
        ext_check_result = []
        for ext in ext_pattern_list:
            boolean = ext in file_name
            ext_check_result.append(boolean)
        if True in ext_check_result:
            available_filename_list.append(file_name)
    return  available_filename_list

####PDFファイル（拡張子：.pdf)からテキスト抽出するメソッド
def get_text_list_recursively(layout) -> list:
    if isinstance(layout, LTTextBox):
        return [layout.get_text()]
    if isinstance(layout, LTContainer):
        text_list = []
        for child in layout:
            text_list.extend(get_text_list_recursively(child))
            return text_list

def get_text_from_pdf(filepath: str) -> str:
    laparams = LAParams(detect_vertical=True)
    resource_manager = PDFResourceManager()
    device = PDFPageAggregator(resource_manager, laparams=laparams)
    interpreter = PDFPageInterpreter(resource_manager, device)
    results = []
    with open(filepath, "rb") as file:
        for page in PDFPage.get_pages(file):
            interpreter.process_page(page)
            layout = device.get_result()
            text = str(get_text_list_recursively(layout))
            text = text.replace("\\n", "").replace("\n", "").replace(" ", "").replace("　", "")
            results.append(text)

    output = "".join(results)
    return output


####PowerPointファイル（拡張子：.pptx)からテキスト抽出するメソッド
def get_text_from_powerpoint(filepath: str) -> str:
    prs = pptx.Presentation(filepath)
    output = ""
    for i, sld in enumerate(prs.slides, start=1):
        for shp in sld.shapes:
            if shp.has_text_frame:
                text = shp.text
                output += text.replace("\n", "").replace("\n", "").replace(" ", "").replace("　", "")

    return output


####Wordファイル（拡張子：.pptx)からテキスト抽出するメソッド
def get_text_from_word(filepath: str) -> str:
    document = docx.Document(filepath)
    text_list = list(map(lambda par: par.text, document.paragraphs))
    text = "".join(text_list)
    output = text.replace(" ", "").replace("　", "")
    return output

####Excelファイル（拡張子：.xlsx)からテキスト抽出するメソッド
def get_text_from_excel(filepath: str) -> str:
    output_text = ""
    book = openpyxl.load_workbook(filepath)
    sheet_list = book.sheetnames
    for sheet_name in sheet_list:
        sheet = book[sheet_name]
        for cells in tuple(sheet.rows):
            for cell in cells:
                data = cell.value
                if data is None:
                    continue
                else:
                    output_text += str(data).replace(" ", "").replace("　", "")
    return output_text

####txtファイル（拡張子：.txt)からテキスト抽出するメソッド
def get_text_from_textfile(filepath: str) -> str:
    output_text = ""
    f = open(filepath, 'r', encoding='UTF-8')
    output_text = f.read()
    f.close()
    return output_text

####Key: ファイルの拡張子に応じて、Value: 適切なメソッドを選択する際に用いる辞書
func_dict = {
    '.pdf' : get_text_from_pdf,
    '.docx'  : get_text_from_word,
    '.xlsx'  : get_text_from_excel,
    '.pptx' : get_text_from_powerpoint,
    '.txt'  : get_text_from_textfile
         }

####mainメソッド
def get_text_from_file(file_path):
    # パスからファイルの「ファイル名.拡張子」を取得
    # https://www.gesource.jp/programming/python/code/0010.html
    root, ext = os.path.splitext(file_path)
    available_ext_list = list(func_dict.keys())
    # ファイルの拡張子に応じた適切なテキストデータ抽出メソッドを実行
    for ok_ext in available_ext_list:
        if ext == ok_ext:
            text = func_dict[ext](file_path)
    
    if not(ext in available_ext_list):
        text = ""
        
    return text

```