# Retrieve TOC from PDF

I am reading 'Python Trick' in PDF type.

To have a big picture of the book, I aim to get a structure dict of TOC from the book. So I copied its TOC from PDF to typora(a markdown editor) and solve the problem with follows:

def read(filename):
with open (filename) as file:
return content
In [108]: content
Out[108]: '\n\n\n\n1.Introduction\n\n 1.1 What’s a Python Trick? . . . . . . . . . . . . . . . . 1.2 What This Book Will Do for You . . . . . . . . . . . 1.3 How to Read This Book . . . . . . . . . . . . . . . .\n\n\n\n2.Patterns for Cleaner Python\n\n2.1 Covering Your A** With Assertions . . . . . . . . . . 2.2 Complacent Comma Placement . . . . . . . . . . . . 2.3 Context Managers and the with Statement . . . . . . 2.4 Underscores, Dunders, and More . . . . . . . . . . . 2.5 A Shocking Truth About String Formatting . . . . . . 2.6 “The Zen of Python” Easter Egg . . . . . . . . . . . .\n\n\n\n3.Effective Functions\n\n3.1 Python’s Functions Are First-Class . . . . . . . . . . 3.2 Lambdas Are Single-Expression Functions . . . . . . 3.3 The Power of Decorators . . . . . . . . . . . . . . . 3.4 Fun With *args and **kwargs . . . . . . . . . . . . 3.5 Function Argument Unpacking . . . . . . . . . . . . 3.6 Nothing to Return Here . . . . . . . . . . . . . . . .'


2.Divide to chapters

chapters = content.split('\n\n\n\n')
chapters = chapters[1:]
In [112]: chapters[0]
Out[112]: '1.Introduction\n\n 1.1 What’s a Python Trick? . . . . . . . . . . . . . . . . 1.2 What This Book Will Do for You . . . . . . . . . . . 1.3 How to Read This Book . . . . . . . . . . . . . . . .'


3.Create single chapter dict

def create_single_chapter(chapter):
key, value = chapter.split('\n\n')
key = key.strip()
value = value.replace('. .', '')
value = value.replace(' .','')
value_list = value.split('  ')
value_list = [i.strip() for i in value_list if i.strip()]
return {key:value_list}
In [124]: x = create_single_chapter_dict(chapters[1])
In [125]: x
Out[125]:
{'2.Patterns for Cleaner Python': ['2.1 Covering Your A** With Assertions',
'2.2 Complacent Comma Placement',
'2.3 Context Managers and the with Statement',
'2.4 Underscores, Dunders, and More',
'2.5 A Shocking Truth About String Formatting',
'2.6 “The Zen of Python” Easter Egg']}


4.Retrieve the TOC using class

class TOC:
def __init__(self, filename):
self.filename = filename

with open (filename) as file:
return content

def divide_to_chapters(self, filename):
chapters = content.split('\n\n\n\n')
#subtract the unqualified part
chapters = chapters[1:]
return chapters

def create_single_chapter_dict(self, chapter):
key, value = chapter.split('\n\n')
key = key.strip()
value = value.replace('. .', '')
value = value.replace(' .','')
value_list = value.split('   ')
values = [i.strip() for i in value_list if i.strip()]
return {key:values}

def get_toc(self):
chapters = self.divide_to_chapters(self.filename)
chapters_dict = {}
for chapter in chapters:
chapter_dict = self.create_single_chapter_dict(chapter)
chapters_dict.update(chapter_dict)
return chapters_dict


Run and get

In [141]: pprint(TOC('pt.md').get_toc())
{'1.Introduction': ['1.1 What’s a Python Trick?',
'1.2 What This Book Will Do for You',
'1.3 How to Read This Book'],
'2.Patterns for Cleaner Python': ['2.1 Covering Your A** With Assertions',
'2.2 Complacent Comma Placement',
'2.3 Context Managers and the with '
'Statement',
'2.4 Underscores, Dunders, and More',
'2.5 A Shocking Truth About String '
'Formatting',
'2.6 “The Zen of Python” Easter Egg'],
'3.Effective Functions': ['3.1 Python’s Functions Are First-Class',
'3.2 Lambdas Are Single-Expression Functions',
'3.3 The Power of Decorators',
'3.4 Fun With *args and **kwargs',
'3.5 Function Argument Unpacking',
'3.6 Nothing to Return Here']}


I have two questions:
1. Is it a best practice to use class instead of def and __main__?
2. How can such a task be accomplished in an elegant way?

The "state" of your class is only its filename. It is never being used except in get_toc where it is read-only.

In this regard, it would be best to use regular functions instead of a class with methods since you already pass everything you need by parameters anyway.

I would also turn divide_to_chapters into a generator to avoid duplicating a (possibly quite long) list, but the trick might also be in how you read and parse your file: instead of reading the whole file at once and splitting at line boundaries, you might be better off reading the file line by line and checking if you are looking for a chapter name or parsing its content. Instead of your approach, I’d go with:

def read(filename):
with open (filename) as book:
for line in book:
line = line.strip()
if line:  # We found a chapter name
yield line, parse_chapter_content(book)

def parse_chapter_content(book):
for line in book:
line = line.strip()
if line:  # We found the content of a chapter
line = line.replace('. .', '').replace(' .', '')
return [
title.strip()
for title in line.split('   ')
if title.strip()
]

def get_toc(filename):

Lastly, I’m not found of the line.replace(..).replace(..).split(..) technique. You might be able to come up with a better approach using regular expressions.