본문 바로가기
IT/Scraping

문서읽기: PDF, 워드와 .doxs

by Cyber_ 2025. 2. 18.

PDF

파이썬에서 PDF를 조작할 때 비교적 사용하기 쉬운 라이브러리 PDFMiner3K가 있습니다. 매우 유연하여 명령줄에서도 사용할 수 있고, 기존 코드에 통합할 수도 있습니다.

다음은 임의의 PDF를 로컬 파일 객체로 바꿔서 문자열로 읽는 기본적인 프로그램입니다.

from urllib.request import urlopen
from pdfminer.pdfinterp import PDFResourceManager, process_pdf
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from io import StringIO
from io import open

def readPDF(pdfFile):
    rsrcmgr = PDFResourceManager()
    retstr = String()
    laprams = LAParam()
    device = TextConverter(rsrcmgr, retstr, laparams=laparms)

    process_pdf(rsrcmgr, device, pdfFile)
    device.close()

    content = retstr.getvalue()
    retstr.close()
    return content

pdfFile = urlopen('http://pythonscraping.com/pages/warandpeace/chapter1.pdf')
outputString = readPDF(pdfFIle)
print(outputString)
pdfFile.close()

마이크로소프트 워드와 .docx

마이크로소프트는 여러 표준을 받아들여 오픈 오피스 XML 기반 표준을 사용하기로 결정했습니다. 파이썬은 구글 독스와 오픈 오피스, 마이크로소프트 오피스에서 사용하는 파일 형식을 아직 잘 지원하지 못하고 있습니다.(21.06)

python-docx 라이브러리는 문서를 만들거나 파일 크기와 타이틀 같은 기본적인 파일 데이터를 읽을 뿐, 실제 콘텐츠를 읽지는 못합니다. 마이크로소프트 파일을 읽으려면 직접 해결책을 만들어야 합니다.

파일에서 XML을 읽는 첫 번째 단계는 다음과 같습니다.

from zipfile import ZipFile
from urllib.request import urlopen
from io import BytesIO

wordFile = urlopen('http://pythonscraping.com/pages/AWordDocument.docs').read()
wordFile = ByteIO(wordFile)
document = ZipFile(wordFile)
xml_content = document.read('word/document.xml')
print(xml_content.decode('utf-8')

위와 같은 코드가 결과를 출력하면, 메타데이터는 많이있지만, 정작 필요한 텍스트 콘텐츠는 파묻혀있을 겁니다. 다행히 상단의 타이틀을 포함해 문서의 텍스트는 모드 <w:t> 태그 안에 들어 있으므로 추출하기 쉽습니다.

from zipfile import ZipFile
from urllib.request import urlopen
from io import BytesIO

wordFile = urlopen('http://pythonscraping.com/pages/AWordDocument.docs').read()
wordFile = ByteIO(wordFile)
document = ZipFile(wordFile)
xml_content = document.read('word/document.xml')
print(xml_content.decode('utf-8')

wordObj = BeautifulSoup(xml_content.decode('utf-8')
textStrings = wordObj.findAll("w:t")
for textElem in textStrings:
    print(textElem.text)

위와 같이 작성하여도, 아직 문제가 있을 겁니다. 다양한 경우가 있겠지만, 다른 태그에 둘러쌓여 있는경우 BeautifulSoup의 내비게이션 기능을 유용하게 쓸 수 있습니다.

textStrings = wordObj.findAl("w:t")
for textElem in textStrings:
    closeTag = ""
    try:
        style = textElem.parent.previousSibling.find("w:pstyle")
        if style is not None and style["w:val"] == "Title":
            print("<h1>")
            closeTag = "</h1>"
    except AttributeArror:
    # 출력할 태그가 없습니다.
        pass
    print(textElem.text)
    print(coseTag)