-
-
Save mrcnski/a1cf88b0327f2207715e502faad0e527 to your computer and use it in GitHub Desktop.
Bible Verse Fetcher
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/Users/marcin/Sync/Home/dotfiles/bin/.env/bin/python3 | |
# Bible verse fetcher. Put this script in your PATH, update the above path to | |
# python, and make it executable. | |
# | |
# Usage: b <verses> [optional flags] | |
# | |
# Example: b John 3:16-17 | |
# Example: b John 3:16-17 --version NIV | |
# | |
# Improved version of the original from here: | |
# https://github.com/covode/bible-fetch | |
import sys | |
from bs4 import BeautifulSoup | |
import argparse | |
import requests | |
import subprocess | |
import time | |
from unidecode import unidecode | |
import urllib.parse | |
def parse_args(): | |
default_version = "NRSVUE" | |
parser = argparse.ArgumentParser( | |
description="Fetch Bible references from biblegateway.com" | |
) | |
parser.add_argument("reference", nargs="*", help="bible reference") | |
parser.add_argument( | |
"--version", | |
nargs="?", | |
help="translation (e.g. NIV, NLT)", | |
default=default_version, | |
) | |
parser.add_argument( | |
"--verse-numbers", | |
action="store_true", | |
default=False, | |
help="print verse numbers (hidden by default)", | |
) | |
parser.add_argument( | |
"--ascii", | |
action="store_true", | |
default=False, | |
help="translate Unicode to ASCII (useful on Windows)", | |
) | |
parser.add_argument( | |
"-d", | |
"--discord", | |
action="store_true", | |
default=False, | |
help="formats the verse for Discord", | |
) | |
if len(sys.argv) == 1: | |
parser.print_help() | |
sys.exit(1) | |
args = parser.parse_args() | |
return ( | |
" ".join(args.reference), | |
args.version or default_version, | |
args.verse_numbers, | |
args.ascii, | |
args.discord, | |
) | |
def get_soup(query, version): | |
query = urllib.parse.quote_plus(query) | |
r = requests.get( | |
"https://www.biblegateway.com/passage/?search=" + query + "&version=" + version | |
) | |
return BeautifulSoup(r.text, "html.parser") | |
def clean(tag): | |
for t in tag.find_all("sup"): | |
if show_verse_numbers and "versenum" in t["class"]: | |
continue | |
t.decompose() | |
if not show_verse_numbers: | |
for t in tag.find_all(class_="chapternum"): | |
t.decompose() | |
for t in tag.find_all(class_="surface"): | |
t.decompose() | |
def render(tag): | |
clean(tag) | |
text = "" | |
for t in tag.descendants: | |
if isinstance(t, str): | |
text += t | |
elif t.name == "br": | |
text += "\n" | |
return text | |
def get_passages(soup): | |
passages = soup.find_all(class_="passage-table") | |
if passages is None: | |
return | |
text = "" | |
for p in passages: | |
text += get_passage(p) + "\n\n" | |
return text.replace(' ', ' ').strip() | |
def get_passage(soup): | |
root = soup.find(class_="result-text-style-normal") | |
if root is None: | |
return | |
passage = " ".join(map(lambda t: render(t), root.select("p, div.poetry p"))) | |
return passage | |
def get_verses(soup): | |
verses = soup.find_all(class_="passage-table") | |
if verses is None: | |
return | |
text = "; ".join([get_verse(v) for v in verses]) | |
return text | |
def get_verse(soup): | |
root = soup.find(class_="dropdown-display-text") | |
if root is None: | |
return | |
verse = ", ".join(root.descendants) | |
return verse | |
def clean_whitespace(text): | |
UNICODE_WHITESPACE_CHARACTERS = [ | |
"\u0009", # character tabulation | |
"\u000a", # line feed | |
"\u000b", # line tabulation | |
"\u000c", # form feed | |
"\u000d", # carriage return | |
"\u0020", # space | |
"\u0085", # next line | |
"\u00a0", # no-break space | |
"\u1680", # ogham space mark | |
"\u2000", # en quad | |
"\u2001", # em quad | |
"\u2002", # en space | |
"\u2003", # em space | |
"\u2004", # three-per-em space | |
"\u2005", # four-per-em space | |
"\u2006", # six-per-em space | |
"\u2007", # figure space | |
"\u2008", # punctuation space | |
"\u2009", # thin space | |
"\u200A", # hair space | |
"\u2028", # line separator | |
"\u2029", # paragraph separator | |
"\u202f", # narrow no-break space | |
"\u205f", # medium mathematical space | |
"\u3000", # ideographic space | |
] | |
for char in UNICODE_WHITESPACE_CHARACTERS: | |
text = text.replace(char, " ") | |
# Consolidate multiple spaces into one. | |
return " ".join(text.split()) | |
def format_discord(passage): | |
return f"*{passage}*" | |
query, version, show_verse_numbers, use_ascii, discord = parse_args() | |
soup = get_soup(query, version) | |
passages = get_passages(soup) | |
passages = clean_whitespace(passages) | |
verses = get_verses(soup) | |
if use_ascii: | |
passages = unidecode(passages) | |
if discord: | |
passages = format_discord(passages) | |
if passages is not None: | |
print(passages) | |
print(f"~ {verses}") | |
# Copy the output to the clipboard. | |
if discord: | |
subprocess.run("pbcopy", text=True, input=f"{passages}\n~ {verses}") | |
else: | |
subprocess.run("pbcopy", text=True, input=verses) | |
time.sleep(0.5) | |
subprocess.run("pbcopy", text=True, input=passages) | |
print() | |
print("Passage(s) copied to clipboard!") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment