I've written several Python scripts to help parse and handle scripture material. Once you have raw access to the scripture text, you can do all sorts of fun analysis.
Because of NIV copyright, I can't share the full scripture text. However, I can share a tool that will help you generate your own copy of the scripture text. The result of the below script is a compressed pickle'd format, which is easy to unpack and parse in other Python scripts.
It legally scrapes an online source, which can only be used for personal use. You can still share lists that you derive from the text, but not the full text itself.
# scrape niv books from biblegateway.com for personal use only # you are responsible for following niv copyright # unpack using pickle in other scripts # released gplv3 by jeffrey sharkey import re, pycurl, pickle, StringIO, time #import zipfile # set the books you want to pull down # default set is gepcp books = [55,56,57,58,64] def fetch(url): raw = StringIO.StringIO() curl = pycurl.Curl() curl.setopt(curl.URL, url) curl.setopt(curl.USERAGENT, 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.12) Gecko/20080209 Firefox/2.0.0.12') curl.setopt(curl.WRITEFUNCTION, raw.write) curl.setopt(curl.FOLLOWLOCATION, 1) curl.perform() curl.close() return raw.getvalue() redead = re.compile('No results found',re.I|re.S|re.M) rechapter = re.compile('<h4>([^<]+)</h4>',re.I|re.S|re.M) resection = re.compile('<h5>([^<]+)</h5>.*?<span.+?class="sup">([^<]+)</span>',re.I|re.S|re.M) reverse = re.compile('<span.+?class="sup">([^<]+)</span>([^<]+)',re.I|re.S|re.M) refootnote = re.compile('<sup>.+?</sup>',re.I|re.S|re.M) #pickled = StringIO.StringIO() pickled = open('scraped.txt', 'w') # for each book continue pulling down chapters until error for book in books: chapnum = 0 while True: chapnum += 1 print "fetching chapter=%d of book=%d" % (chapnum, book) raw = fetch("http://www.biblegateway.com/passage/?book_id=%s&chapter=%s&version=31" % (book, chapnum)) if redead.search(raw): print "\tchapter doesnt exist, moving to next book" break # strip out all footnotes and identify chapter raw = refootnote.sub("", raw) chapter = rechapter.search(raw).group(1) # pull out all section titles and verses sections = resection.findall(raw) verses = reverse.findall(raw) # save all scraped information for later use pickle.dump(chapter, pickled) pickle.dump(sections, pickled) pickle.dump(verses, pickled) print "\tfound sections=%d and verses=%d" % (len(sections), len(verses)) time.sleep(2) # pack up all saved chapters #save = zipfile.ZipFile('scraped.zip', 'w', zipfile.ZIP_DEFLATED) #save.writestr('scraped.txt', pickled.getvalue()) #save.close() pickled.close() print "done, all scraped data available in scraped.txt"
Quoting bees are fun, but generating the lists can be painful. This Python script automatically builds a PDF while following rules, such as minimum and maximum word-count for verses, and minimum distance between verses. It assumes that you've built the scripture text library using the script above.
The reportlib library is quite flexible, and you could format the quoting bee output in any way you'd like with only slight code editing.
# build a quoting bee list # released gplv3 by jeffrey sharkey pageheader = 'Set A-1: GEPCP verses 25-40 words long, at least 100 verses apart' numverses = 30 # number of verses for the list minlen = 25 # minimum verse length in words maxlen = 40 # maximum verse length in words distance = 100 # minimum distance between two consecutive verse import pickle, re, random from reportlab.lib import units, pagesizes, colors, styles from reportlab.platypus import Table, TableStyle from reportlab.platypus import SimpleDocTemplate, Paragraph pickled = open("scraped.txt") collection = [] actual = 0 wordbound = re.compile("[^A-Za-z1-9]+",re.I|re.S|re.M) # unpack all verses try: while True: chapter = pickle.load(pickled) sections = pickle.load(pickled) verses = pickle.load(pickled) for verse in verses: # make sure that this verse is within word counts actual += 1 words = len(wordbound.findall(verse[1]))+1 if words < minlen or words > maxlen: continue reference = "%s:%s" % (chapter, verse[0]) collection.append((actual, reference, verse[1])) except: pass print "only %d of %d verses met your length requirements" % (len(collection), actual) # make random selection of verses verses = [] lastactual = -distance while len(verses) < numverses: # pick a random verse from those remaining picked = random.randint(0, len(collection)-1) # check to make sure we have enough distance thisactual = collection[picked][0] if abs(lastactual - thisactual) < distance: continue lastactual = thisactual # insert selected verse and remove from list verses.append(collection.pop(picked)) # do actual printing margin = 0.5*units.inch width = pagesizes.letter[0]-(margin*2) def header(canvas, doc): global margin canvas.saveState() canvas.setFont('Times-Bold',11) canvas.drawString(margin, pagesizes.letter[1] - margin*1.2, pageheader) canvas.drawString(margin, margin, "Page %d" % (doc.page)) canvas.restoreState() # convert verses into pdf paragraphs style = styles.getSampleStyleSheet()["Normal"] style.leading = style.leading*1.3 verses = map(lambda verse: (verse[1], Paragraph(verse[2], style)), verses) # build pdf table style = TableStyle() style.add('VALIGN', (0,0), (-1,-1), 'TOP') style.add('GRID', (0,0), (-1,-1), 1, colors.black) table = Table(verses, [width*0.2,width*0.8]) table.setStyle(style) # build pdf output doc = SimpleDocTemplate("quotingbee.pdf", pagesize=pagesizes.letter, topMargin=margin*1.5, leftMargin=margin, bottomMargin=margin*1.5, rightMargin=margin) doc.build([table], onFirstPage=header, onLaterPages=header)
There are quite a few words omitted from the official concordance which can be useful to study. One of the biggest requests is creating an "of list" that has all phrases of the type "... of something" or "something of ...".
This script uses the scripture text generated earlier to create these "of" lists for your own study use when creating lists. It could easily be adapted to build an entire concordance by running over each word.
# build an "of" list from the scripture text # released gplv3 by jeffrey sharkey import pickle, re, codecs from reportlab.lib import units, pagesizes, colors, styles from reportlab.platypus import Table, TableStyle from reportlab.platypus import SimpleDocTemplate, Paragraph pickled = open('scraped.txt', 'r') reword = re.compile('\\b(\\w+)\\b( of )\\b(\\w+)\\b',re.I|re.S|re.M) dashes = re.compile('\xe2\x80\x94') collection = [] # unpack all verses, keeping track of all "of" references try: while True: chapter = pickle.load(pickled) sections = pickle.load(pickled) verses = pickle.load(pickled) for verse in verses: numb, text = verse text = dashes.sub("--", text) reference = "%s:%s" % (chapter, numb) # for this verse, find each "of" reference start = 0 while True: match = reword.search(text, start) if match is None: break start = match.start()+1 before = text[0:match.start()] preword = match.group(1) word = match.group(2) postword = match.group(3) after = text[match.end():-1] collection.append((reference, before, preword, word, postword, after)) except: pass # build prefix and postfix lists prefix = map(lambda ref: (ref[2], ref[1], ref[3], ref[4], ref[5], ref[0]), collection) postfix = map(lambda ref: (ref[4], ref[5], ref[3], ref[2], ref[1], ref[0]), collection) prefix.sort() postfix.sort() # do actual printing margin = 0.5*units.inch width = pagesizes.letter[0]-(margin*2) # convert verses into pdf paragraphs style = styles.getSampleStyleSheet()["Normal"] prefixprint = map(lambda ref: (ref[5], (ref[1]+ref[0]+ref[2])[-50:-1], (' '+ref[3]+ref[4])[1:50]), prefix) postfixprint = map(lambda ref: (ref[5], (ref[4]+ref[3]+' ')[-50:-1], (ref[2]+ref[0]+ref[1])[1:50]), postfix) # build pdf table def buildTable(data): style = TableStyle() style.add('VALIGN', (0,0), (-1,-1), 'TOP') style.add('GRID', (0,0), (-1,-1), 1, colors.black) style.add('ALIGN', (1,0), (1,-1), 'RIGHT') table = Table(data, [width*0.2,width*0.4,width*0.4]) table.setStyle(style) return table # build pdf output doc = SimpleDocTemplate("prefixlist.pdf", pagesize=pagesizes.letter, topMargin=margin, leftMargin=margin, bottomMargin=margin, rightMargin=margin) doc.build([buildTable(prefixprint)]) doc = SimpleDocTemplate("postfixlist.pdf", pagesize=pagesizes.letter, topMargin=margin, leftMargin=margin, bottomMargin=margin, rightMargin=margin) doc.build([buildTable(postfixprint)])
Quotation completion questions are usually pretty easy if you know the scripture text, but at higher levels of competition some question writers can choose to leave out any contextual information. There could be a quotation completion over all of the material, which means you need to know all the verses that have unique first-words.
This script creates a list of unique first-word verses at a variety of levels. First, the list of all 76 verses across the entire scripture text that have unique first-words. Then, to show the power of the script, we create the unique first-word lists for each book, chapter, and section title.
Only the first three words of each verse are included, because they are usually enough to link to the correct verse. This script also shows how we can change the page size to fold and fit nicely inside a quiz book--when printing be sure to print two-to-a-page. It also shows how easy it is to create two-column lists.
# build list of unique verses across books, book, chapters, and sections # released gplv3 by jeffrey sharkey import pickle, re from reportlab.lib import units, pagesizes, colors, styles from reportlab.platypus import Table, TableStyle, Spacer, Paragraph, Frame from reportlab.platypus import BaseDocTemplate, PageTemplate, NextPageTemplate, PageBreak pickled = open("scraped.txt") refirstword = re.compile('(\\w+)\\b',re.I|re.S|re.M) rechapter = re.compile('(\\d+)',re.I|re.S|re.M) redashes = re.compile('\xe2\x80\x94') refirstthree = re.compile('(\\w+[^\\w]+\\w+[^\\w]+\\w+)[^\\w]',re.I|re.S|re.M) # a grouping of verses class Group: def __init__(self, name): self.name = name self.firstwords = {} # report seeing a firstword and verse in this group def reportword(self, firstword, verse): if not self.firstwords.has_key(firstword): self.firstwords[firstword] = [] self.firstwords[firstword].append(verse) # strip our entire list of firstwords down to only unique ones def makeunique(self): self.firstwords = dict([ (firstword,verses) for firstword,verses in self.firstwords.items() if len(verses)==1]) if len(self.firstwords) == 0: return None return self # return a sorted list for later printing def printable(self): set = [verses[0] for firstword,verses in self.firstwords.items()] set.sort() set = map(lambda verse: (refirstthree.search(verse[0]).group(1), verse[1]), set) return set books = []; chapters = []; sections = [] entiretext = Group('Entire text') thisbook = Group(None); thischapter = Group(None); thissection = Group(None) # unpack all verses, counting up the firstword of each verse try: lastbookname = None while True: chapter = pickle.load(pickled) sectionlist = pickle.load(pickled) verses = pickle.load(pickled) # we know we are walking over a chapter boundary chapters.append(thischapter.makeunique()) thischapter = Group(chapter) # check if this chapter walks over a book boundary bookname = rechapter.sub("", chapter) chapnum = rechapter.search(chapter).group(1) if not bookname == lastbookname: books.append(thisbook.makeunique()) thisbook = Group(bookname) lastbookname = bookname for verse in verses: numb, text = verse text = redashes.sub("--", text) reference = "%s %s:%s" % (bookname[0:4], chapnum, numb) fullverse = (text, reference) # check if this verse walks over a section boundary for section in sectionlist: if numb == section[1]: sections.append(thissection.makeunique()) thissection = Group(section[0]) # count up this firstword in various contexts firstword = refirstword.search(text).group(1).lower() entiretext.reportword(firstword, fullverse) thisbook.reportword(firstword, fullverse) thischapter.reportword(firstword, fullverse) thissection.reportword(firstword, fullverse) except: pass # save any trailing groups books.append(thisbook.makeunique()) chapters.append(thischapter.makeunique()) sections.append(thissection.makeunique()) entiretext.makeunique() # do actual printing halfsheet = (pagesizes.letter[1]/2, pagesizes.letter[0]) margin = 0.3*units.inch width = (halfsheet[0]-(margin*3))/2 def maketable(printable): style = TableStyle() style.add('VALIGN', (0,0), (-1,-1), 'TOP') style.add('GRID', (0,0), (-1,-1), 1, colors.black) table = Table(printable, [width*0.65,width*0.35]) table.setStyle(style) return table def maketitle(document, title): style = styles.getSampleStyleSheet()["Normal"] document.append(Spacer(0, margin/3)) document.append(Paragraph('<b>%s</b>' % (title), style)) document.append(Spacer(0, margin/3)) document = [] maketitle(document, "Unique across entire scripture text") document.append(maketable(entiretext.printable())) def dumpset(title, set): document.append(PageBreak()) maketitle(document, title) for item in set: if item is None: continue maketitle(document, '<i>%s</i>' % (item.name)) document.append(maketable(item.printable())) dumpset("Unique across a book", books) dumpset("Unique across a chapter", chapters) dumpset("Unique across a section", sections) # build pdf output doc = BaseDocTemplate("unique.pdf", pagesize=halfsheet, topMargin=margin, leftMargin=margin, bottomMargin=margin, rightMargin=margin) # make default two-column output frame1 = Frame(doc.leftMargin, doc.bottomMargin, width, doc.height, id='col1') frame2 = Frame(doc.leftMargin+width+margin, doc.bottomMargin, width, doc.height, id='col2') doc.addPageTemplates([PageTemplate(id='twocolumn',frames=[frame1,frame2])]) doc.build(document)