Python Programs / Snippits

I'm going through Automate the Boring Stuff and taking notes for future reference.
x

Programs


Built-In Functions

Import by typing "import moduleName." Import multiple by separating by commas.
Import all of a module by typing "from moduleName import *," the asterisk being a wildcard. Allows you to avoid typing the module name everything.

First Party Modules

logging # Used for error heirarchy and logging.
sys # System. Allows the use of 'sys.argv' variables.
traceback # Allows the recording of machine-generated errors.
webbrowser # Provides an interface to allow displaying web-based documents to users.

Third Party Modules

bs4 # Beautiful Soup. Import as bs4. Allows parsing of page HTML.
openpyxl # Allows for the reading, creating and editing of xlsx files.
PyPDF2 # Add, remove or reoder PDF pages. Can't change individual text, color, font, etc.
pyperclip # Allows for the copying and pasting of the clipboard.
python-docx # Import as just 'docx.' Allows for editing and creation of docx files.
requests # Allows you to get information off of the Internet.
selenium # Import as 'from selenium import webdriver'. Lets you take control a browser.

Error Logging

assert # Provide a condition and it will raise an error if that condition is met.
open('error_log.txt', 'a') # Create a new text document. 'a' = Append mode.
raise # raise Exception('Text.')
logging.debug('Text.') | info | warning | error | critical # Logs information at varying severities.
logging.basicConfig(filename='myProgramLog.txt', level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') # Needed line to produce logs. Filename optional.
logging.disable(logging.CRITICAL) # Disables logging messages at 'CRITICAL' or less severity.
# Allows the recording of machine-generated errors. import traceback

# Exceptions are raised with the 'raise' statement. raise Exception('This is the error message.')

# Example raised error and storing: try:
    raise Exception('This is the error message.'
except:     # Open a new file to store the error in.     errorFile = open('error_log.txt', 'a')     # Use 'traceback.format_exc()' to write the error to the file.     errorFile.write(traceback.format_exc())     # Close the file.     errorFile.close()     # Inform the user the error has been saved.     print('The traceback info was written to error_log.txt')

# Assert are sanity checks to make sure your program isn't doing something obviously wrong. # Example syntax: assert False, 'This is the error message.'
# Used for error hierarchy and logging. import logging

# Need this at the top of the program. (filename is optional) logging.basicConfig(filename='filename.txt', level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')

# There are 5 levels of logging: [debug, info, warning, error, critical] # Can disable logging by having the following: logging.disable(logging.CRITICAL)

Excel

openpyxl.load_workbook('file.xlsx') # Opens an existing file. Assign to a Workbook object.
openpyxl.Workbook # Creates a new Workbook file in memory. Assign to a Workbook object.
wb['Sheet'] # Assign to a Sheet object.
wb.sheetnames # List of all Sheets in a Workbook.
wb.create_sheet(index=0, title='My Other Sheet') # Create, name and position a sheet.
sheet['A1'] # A Cell's value. Assign to a Cell object.
sheet['A1'].value # A specific Cell's value.
sheet.cell(row=1, column=2) # Another way to view a cell's value.
import openpyxl

# Load an existing file... wb = openpyxl.load_workbook('Excel_Filename.xlsx')
# ...or create a new Workbook in memory. wb = openpyxl.Workbook

# View all Sheets in Workbook. wb.sheetnames

# Create a new Sheet, assign its position and title. wb.create_sheet(index=0, title='My Other Sheet')
# Or create a new Sheet object... sheet2 = wb.create_sheet()
# ...and use '.title' to change the Sheet's name. sheet2.title = 'My New Sheet Name'

# Create a Sheet object. sheet = wb['Sheet_Name']

# Create a Cell object. cell = sheet['A1']

# View the value stored in a specific Cell. cell.value

# Store value in a Cell. cell = 42

# Another way to view Cell data. sheet.cell(row=1, column=2)

# Save Workbook wb.save('Excel_Filename.xlsx')

PDFs

open('example.pdf', 'rb') # Open a PDF. Pass into a 'pdfFile' object. 'rb' = Read Binary mode.
open('new_pdf_name.pdf', 'wb') # Open a PDF. Pass to an 'outputFile' object. 'wb' = Write Binary mode.
writer.write(outputFile) # Saves the PDF.
PyPDF2.PdfFileReader(pdfFile) # Pass file to PyPDF2. Pass into a 'reader' object.
PyPDF2.PdfFileWriter() # Allows you to write PDFs. Pass to a 'writer' object.
writer.addPage(page) # Adds PDF pages to the 'writer' object.
reader.numPages # Displays the number of pages in the PDF.
reader.getPage(0) # Gets a specific page. Pass into a 'page' object.
page.extractText() # Rips the text from the 'page' object.
import PyPDF2

# Open existing PDF. 'rb' = Read Binary pdfFile = open('filename.pdf', 'rb')

# Pass file to a Reader object. reader = PyPDF2.PdfFileReader(pdfFile)

# Can see how many pages a PDF is with '.numPages' reader.numPages

# We can store a Page for use with '.getPage()' page = reader.getPage(0)

# When we have a page stored, we can extract text from it with '.extractText()' page.extractText()

Web Scraping

requests.get('File or Page') # Gets a copy of a file. Pass into a 'Response' object.
res.status_code # Checks if file downloaded. 200 for success, 404 for missing, etc.
res.raise_for_status() # Causes an error if there's a problem with the downloaded ojbect.
res.text # Displays the text stored in the 'Response' object.
res.iter_content(Byte Amount) # Iterates through a 'Chunk' of content by the amount of Byte's you choose.
sys.argv # Captures a list of command line arguments.
webbrowser.open('URL') # Opens up the specified web page.
file = open('filename', 'wb') # Opens a file in a 'File' object. 'wb' = Write Binary mode.
bs4.BeautifulSoup(res.text, 'html.parser') # Creates a 'Soup' object for HTML content. 'html.parser' to avoid warning.
soup.select('CSS Selector') # Pass to an 'Element' object. Returns a list of matched elements.
elem[0].text # Displays the text of a desired indexed position in an 'Element' object.
webdriver.Chrome() # Opens up a Chrome browser window. Pass to a 'browser' object.
browser.get('URL') # Opens a specific page in the opened browser.
elem.click() # Click on a stored 'Element' object acquired from a CSS selector.
searchElem.send_keys('Text') # Put text in an input field.
searchElem.submit() # Submits a form.
browser.back() # Presses the browser's 'back' button.
browser.forward() # Presses the browser's 'forward' button.
browser.refresh() # Presses the browser's 'refresh' button.
browser.quit() # Closes the browser.
# Allows you to open web pages. import webbrowser

# '.open()' method opens the URL in a browser. webbrowser.open('https://automatetheboringstuff.com')

# Can open a file as well, and can pass 'wb' to it (Write Binary mode). # Writes to a 'File' object. playFile = open('filename.txt'. 'wb')
# Lets you grab a copy of web pages. import requests

# Download a web page and pass it to a 'Response' object. res = requests.get('URL')

# Check if the file downloaded properly with '.status_code' # 200 for success, 404 for file not found, etc. res.status_code

# Can raise an error if there's a problem downloading a page. res.raise_for_status

# Can display text with the '.text' member variable. res.text

# '.iter_content()' can iterate through content with a specified number of Bytes. res.iter_content(100000)

# '.write' to add to file. playFile.write(chunk)

# '.close' to close the file. playFile.close()
# Allows the parsing of HTML. import bs4
import requests

# '.get()' grabs a page. Throw it into a 'Response' object. res = requests.get('URL')

# Returns a 'BeautifulSoup' object of the page. 'html.parser' to avoid warning. soup = bs4.BeautifulSoup(res.text, 'html.parser')

# Can select element(s) of a page with '.select()'. Returns a list of found element(s). elem = soup.select('CSS Selector')

# View elements based on index number with '.text', and '.strip()' to remove excess characters. elem[0].text.strip()
# Allows you to take actions on a web page. from selenium import webdriver

# Opens a Chrome browser window. browser = webdriver.Chrome()

# '.get' to grab a particular page. browser.get('https://automatetheboringstuff.com')

# Grab an element and throw it into an 'Element' object. elem = browser.find_element_by_css_selector('CSS Selector')

# Can grab a list of objects with... elems = browser.find_elements_by_css_selector('General HTML')

# Can click on Elements with '.click()' elem.click()

# Grab an input field with... searchElem = browser.find_element_by_css_selector('input field')

# ...and pass text to it with '.send_keys()' searchElem.send_keys('Text')

# Submit the form with '.submit()' searchElem.submit()

# Can control general browser actions. browser.back()
browser.forward()
browser.refresh()
browser.quit()

Word

docx.Document('filename.docx') # Opens a document. Pass to a 'document' object. Can use '.Document()' to create a new one.
document.paragraphs # Displays a list of all paragraphs. Can save a specific index to a 'paragraph' object.
document.add_paragraph('Text.') # Adds a paragraph to the document.
document.paragraphs[0].text # Reads the text of the first paragraph in the 'document' object.
paragraph.style # Can assign the paragraph a specific style, e.g. p.style = 'Title'
paragraph.runs # Displays a list of all runs. Can save a specific index to a 'runs' object.
paragraph.add_run('Text.') # Adds a new run to the paragraph.
paragraph.runs[0].bold | .italic | .underline # Returns a boolean value. Can also set these with normal variable assignments.
document.save('filename.docx') # Commits the document changes to a file.
# 'Document' object contains 'Paragraph' objects. # 'Paragraph' objects contain 'Run' objects. # A new 'Run' object occurs whenever there's a change in style (e.g. bold, italics, etc.)
# Import python-docx with just docx. import docx

# Creates a new Document object. document = docx.Document('filename.docx')

# Can view a specific paragraph's text with '.text' document.paragraphs[0].text

# Save second paragraph to a new variable. paragraph = document.paragraphs[1]

# Can use the '.runs' member variable to call a list of run objects. paragraph.runs # 'Run' objects can also use the 'text' member variable. paragraph.runs[0].text # 'Run' objects also have 'bold,' 'italic,' and 'underline' member variables. You can set these variables to True or False. paragraph.runs[1].bold # You can also set text with normal variable assignment. paragraph.runs[3].text = 'italic and underlined'

# 'Paragraph' and 'Run' objects also have a '.style' variable. paragraph.style = 'Title'

# And you can call '.save' method to commit changes. document.save('new_word_filename.docx')
Section 12, Part 35
Debugging
# Exceptions are raised with the 'raise' statement. raise Exception('This is the error message.')

'''
********* *             * *             * *             * *********
'''

# Example: def boxPrint(symbol, width, height):     # Print the symbol 'x' number of times. * = string replicator.     print(symbol * width)

# For every row (minus the top and bottom rows)... for i in range(height - 2):     # Print the symbol, the amount of spaces in the middle, and then the symbol again.     print(symbol + (' ' * (width - 2)) + symbol)

# And then print the bottom row. print(symbol * width)

# Works as intended. boxPrint('*', 15, 5)

# Visual bug by passing it 2 characters. boxPrint('**', 15, 5)

# Also a bug by having only a width / height of 1. boxPrint('*', 1, 1)

# Check for it if len(symbol) != 1:
    raise Exception('"Symbol" needs to be a string of length 1.')
if (width < 2) or (height < 2):
    raise Exception('Width and Height must be larger than 1.')
# First party import traceback

try:
    raise Exception('This is the error message.')
except:     # Open a new file to store the error in.     errorFile = open('error_log.txt', 'a')     # Use 'traceback.format_exc()' to write the error to the file.     errorFile.write(traceback.format_exc())     # Close the file.     errorFile.close()     # Inform the user the error has been saved.     print('The traceback info was written to error_log.txt')
# An assertion is a sanity check to make sure your program isn't doing something # obviously wrong. Performed by 'assert' statements. assert False, 'This is the error message.'

market_2nd = {'ns': 'green', 'ew': 'red'}

def switchLights(intersection):
    for key in intersection.keys():
        if intersection[key] == 'green':
            intersection[key] = 'yellow'
        elif intersection[key] == 'yellow':
            intersection[key] = 'red'
    elif intersection[key] == 'red':
            intersection[key] = 'green'

# 'Red' changes to 'Green' while there's a 'Yellow' going the other direction. # Fix with an 'assert' statement. assert 'red' in intersection.values(), 'Neither light is red!' + str(intersection)

switchLights(market_2nd)

# Assertion are programmer errors, not user errors.
Section 12, Part 36
Logging
import logging

# Put this at beginning of program. logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')

# Buggy code def factorial(n):
    total = 1
    for i in range(n + 1):
        total *= i;
    return total

print(factorial(5))

# Result is 0. # We track with logging.
logging.debug('Start of program.')

def factorial(n):
    logging.debug('Start of factorial(%s)', (n))
    total = 1
    for i in range(1, n + 1): # This is the issue. Needs to start at 1.
        total *= i;
        logging.debug('i is %s, total is %s' % (i, total))
        logging.debug('Return value is %s' % (total))
    return total

print(factorial(5))

# Can disable logging by having the following at the top: logging.disable(logging.CRITICAL)

# 5 levels of logging. [debug, info, warning, error, critical]
# Can write logging to a text file with 'filename='FILENAME' logging.basicConfig(filename='myProgramLog.txt', level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
Section 13, Part 38
The webbrowser Module
import sys
import pyperclip
import webbrowser

# Captures a list of command line arguments from the Run command. sys.argv

# Launches a browser to a specified URL webbrowser.open('URL.com')
# Creating a program that takes an address and opens Google Maps. # Check if command linen arguments were passed. if len(sys.argv) > 1:     # Combine list into a single string.     address = ' '.join(sys.argv[1:]) else:     # Otherwise we use what's in the clipboard.     address = pyperclip.paste()

webbrowser('https://www.google.com/maps/place/' + address)
# Save to batch file for ease of use. # %* forwards any command line arguments to script. @py.exe C:\script location\filename %*
Section 13, Part 39
Downloading from the Web
import requests

# Downloading a file with '.get' and passing to a 'Response' object. res = requests.get('https://automatetheboringstuff.com/files/rj.txt')

# Can check if file downloaded successfully with '.status_code' res.status_code

# We now have an object with a huge amount of text, which we can read with '.text' res.text

# Can print out a small number of the text with a slice. print(res.text[:500])

# Will raise an exception if there's an error when downloading a file, # will do nothing if it's successful. res.raise_for_status()

# Can save the file to a file object with .open('filename', 'wb'). 'wb' = Write Binary mode. playFile = open('RomeoAndJuliet.txt', 'wb')

# To write the webpage to a file, # you can use a for loop with the response object's '.iter_content' method. # '.iter_content' method returns Chunks of the content on each iteration through the loop. # Each Chunk is of the 'Bytes' data type, and we can specify how many bytes each chunk will contain. for chunk in res.iter_content(100000):     # '.write' method will return an integer of how many 'Bytes' it wrote to the file.     playFile.write(chunk)

# Can close the 'File' object when we're done with it. playFile.close()
Section 13, Part 40
Parsing HTML with the Beautiful Soup Module
import bs4
import requests

# Throw a web page into a 'Response' object. res = requests.get('https://www.newegg.com/seagate-barracuda-st2000dm008-2tb/p/N82E16822184773?Item=N82E16822184773&cm_sp=Homepage_dailydeals-_-P0_22-184-773-_-01292021')

# Make sure it worked. res.raise_for_status()

# Returns a 'BeautifulSoup' object of the page we downloaded. soup = bs4.BeautifulSoup(res.text, 'html.parser')

# Now we can find HTML elements in the downloaded web page with the '.select' method. # Returns a list of found elements. Stored in an 'Element' object. elem = soup.select('#app > div.page-content > div.page-section > div > div > div.row-side > div.product-buy-box > div.product-pane > div.product-price > ul > li.price-current > strong')

# Can view the first element with index 0 and the '.text' member variable. elem[0].text

# If there's excess characters like spaces and line breaks you can use '.strip()' elem[0].text.strip()
import bs4, requests

# Return the price of a product by passing in a URL. def getNeweggPrice(productURL):     # First we request to download the page.     res = requests.get(productURL)     # Check to make sure it downloaded properly     res.raise_for_status()     # Create a 'Soup' object out of the HTML code.     soup = bs4.BeautifulSoup(res.text, 'html.parser')     # Select the Price element. Returns a list of elements.     elems = soup.select('#app > div.page-content > div.page-section > div > div > div.row-side > div.product-buy-box > div.product-pane > div.product-price > ul > li.price-current > strong')     # We only want the first one, so index 0, strip out unneeded characters.     return elems[0].text.strip()
Section 13, Part 41
Controlling the Browser with the Selenium Module
from selenium import webdriver

# Opens a Chrome browser window. browser = webdriver.Chrome()

# Open a particular page. browser.get('https://automatetheboringstuff.com')

# Grab a particular element and throw it into an 'Element' object. elem = browser.find_element_by_css_selector('body > div.main > div:nth-child(1) > ul:nth-child(21) > li:nth-child(1) > a')

# We can then click on the found element with the '.click()' method. elem.click()

# We can also do a more general search and grab all of the matched elements # by using the '.find_elements_by_css_selector()' method. Returns a list of objects. elems = browser.find_elements_by_css_selector('p')
# A list of methods for finding elements. # Elements that use the CSS class name. browser.find_element_by_class_name(name)
browser.find_elements_by_class_name(name)

# Elements that match the CSS selector. browser.find_element_by_css_selector(selector)
browser.find_elements_by_css_selector(selector)

# Elements with a matching id attribute value. browser.find_element_by_id(id)
browser.find_elements_by_id(id)

# <a> elements that completely match the text provided. browser.find_element_by_link_text(text)
browser.find_elements_by_link_text(text)

# <a> elements with a matching name attribute value. browser.find_element_by_name(name)
browser.find_elements_by_name(name)

# Elements with a matching tag name # (case insensitive; an <a> element is matched by 'a' and 'A') browser.find_element_by_tag_name(name)
browser.find_elements_by_tag_name(name)
# Can type into a field with... searchElem = browser.find_element_by_css_selector('.search_field')

# the '.send_keys()' method searchElem.send_keys('text')

# We can then submit the form with the '.submit()' method searchElem.submit()

# You can also control the browser itself. browser.back()
browser.forward()
browser.refresh()
browser.quit()

# To get text from a page... browser.get('https://automatetheboringstuff.com')

# Grab a particular element... elem = browser.find_element_by_css_selector('body > div.main > div:nth-child(1) > p:nth-child(9)')

# Display text inside of that element. elem.text

# If we want the entire text of the website, we can snag just the 'html' or 'body' selector. elem = browser.find_element_by_css_selector('html')

# For more information on Selenium: https://selenium-python.readthedocs.org
Section 14, Part 42
Reading Excel Spreadsheets
# The whole of the excel file is called a 'Workbook.'
# Each 'Workbook' contains multiple sheets called 'Worksheets.'
# Inside each sheet there are 'Columns' and 'Rows', the intersection being a 'Cell.'

# Allows for reading, creating and editing of excel files. import openpyxl
# Create a 'Workbook' object by opening an xlsx file. wb = openpyxl.load_workbook('Test.xlsx')
# Can check the type of object with the 'type()' function. type(wb)
# Can select and store a specific sheet with square brackets. sheet = wb['Sheet']
# Can view all sheets in a Workbook by calling '.sheetnames' wb.sheetnames
# Can create a cell object with square brackets as well. cell = sheet['A1']
# Can view values from a cell with '.value' sheet['A1'].value
# Can select a specific row / column's cell with '.cell' sheet.cell(row=1, column=2)
Section 14, Part 43
Editing Excel Spreadsheets
# Allows for reading, creating and editing of excel files. import openpyxl
# 'load_workbook' function opens an existing excel file. wb = openpyxl.load_workbook('existing_excel_file.xlsx')
# Creates a new excel sheet. wb = openpyxl.Workbook
# Can view existing sheets with '.sheetnames' wb.sheetnames
# Create a 'sheet' object. Can view by using 'wb.sheetnames' sheet = wb['Sheet']
# Can read a cell's value with '.value' sheet['A1'].value
# To add or change the cell's value, we can use the standard assignment statement. sheet['A1'] = 42 sheet['A2'] = 'Hello'
# To commit changes, we have to call the '.save()' method. wb.save('new_excel_filename.xlsx')
# Can add new sheets to file. sheet2 = wb.create_sheet()
# Can change the name of a sheet with the '.title' command. sheet2.title = 'My New Sheet Name'
# To add sheet to different position... wb.create_sheet(index=0, title='My Other Sheet')
Section 14, Part 44
Reading and Editing PDFs
# PyPDF2 - Add pages, remove pages, reorder pages. Can't change individual text, color, font, etc. import PyPDF2 import os
# Change directory to the location of the example PDF. os.chdir('C:\\Users\\USERNAME\\Documents')
# Open and store the test PDF. 'rb' = Read Binary mode. pdfFile = open('example_file_name', 'rb')
# Pass file object to PyPDF2. Returns PDF Reader object, which we store in 'reader'. reader = PyPDF2.PdfFileReader(pdfFile)
# Reader object has a member variable called 'numPages'. Displays how many pages are in the PDF. reader.numPages
# Using getPage, we store a page object. page = reader.getPage(0)
# Once we have a page object, we can display the text from said object. page.extractText()
# To get all of the text out of a PDF, we can loop.
# Loop through the number of pages in the PDF...
for pageNum in range(reader.numPages):     #...And print the extracted text from each page.     print(reader.getPage(pageNum).extractText())
# How to combine two PDFs... First, create two PDF file objects. pdf1File = open('pdf_file_name1.pdf', 'rb') pdf2File = open('pdf_file_name2.pdf', 'rb')
# Create two reader objects, one for each file. reader1 = PyPDF2.PdfFileReader(pdf1File) reader2 = PyPDF2.PdfFileReader(pdf2File)
# Loop through all of the pages in reader1, add to PDF. Then loop through reader2, and add those pages to PDF.
# Create writer object...
writer = PyPDF2.PdfFileWriter()
# Writer objects have a method called "addPage" which will let us add pages to the end of the document.
# Loop through all pages in reader1.
for pageNum in range(reader1.numPages):     # Get all pages from file.     page = reader1.getPage(pageNum)     # Add page to our writer object.     writer.addPage(page)
# Do the same for the second PDF. for pageNum in range(reader1.numPages):     page = reader1.getPage(pageNum)     writer.addPage(page)
# Now that we have all these pages added, we need to save the file. 'wb' = Write Binary mode. outputFile = open('new_pdf_name.pdf', 'wb')
# Then we call the 'writer' object's 'write' method and pass it the new file. writer.write(outputFile)
# Then we close all opened PDFs. outputFile.close() pdf1File.close() pdf2File.close()
Section 14, Part 45
Reading and Editing Word Documents
# 'Document' object contains 'Paragraph' objects.
# 'Paragraph' objects contain 'Run' objects.
# A new 'Run' object occurs whenever there's a change in style (e.g. bold, italics, etc.)

# Import python-docx with just docx. import docx
# Returns new document object. d = docx.Document('word_document_filename.docx')
# Document objects has a paragraphs member variable, has list of paragraph objects. d.paragraphs
# Look at first paragraph object at index 0 with the text member variable which contains string of text. d.paragraphs[0].text
# Save second paragraph to a new variable. p = d.paragraphs[1]
# Can use the 'runs' member variable to call a list of run objects. p.runs
# 'Run' objects can also use the 'text' member variable. p.runs[0].text
# 'Run' objects also have 'bold,' 'italic,' and 'underline' member variables. p.runs[1].bold # Will return True or False
# You can also set those variables to True or False, and change the text. p.runs[3].underline = True p.runs[3].text = 'italic and underlined'
# Can call save method to commit changes. d.save('new_word_filename.docx')
# 'Paragraph' and 'Run' objects also have a 'style' variable. p.style = 'Title' d.save('new_word_filename.docx')
# Creating a new Word document. d = docx.Document()
# All 'Document' objects also have an 'add_paragraph' method. Adds lines to file. d.add_paragraph('Hello, this is a paragraph.') d.add_paragraph('Hello, this is another paragraph.')
# Save the file so it can be opened. d.save('new_word_filename.docx')
# If we want to more text after the first paragraph... p = d.paragraphs[0]
# Just like 'Document' objects have an 'add_paragraph' method,
# 'Paragraph' objects have an 'add_run' method to add more text.
p.add_run('This is a new run.')
# Grab the 'Run' at index 1 and make it bold. p.runs[1].bold = True
# Save the file so it can be opened. d.save('new_word_filename.docx')
# Getting all of the text from a Word document inside of a string. import docx
# Define a function that accepts a filename. def getText(filename):     # Reads the passed-in file.     doc = docx.Document(filename)     # Blank list.     fullText = []     # List of paragraph objects inside of doc...     for para in doc.paragraphs:         # Add text to the end of fullText.         fullText.append(para.text)     # Join the text into a single string.     return '\n'.join(fullText)