#!/usr/bin/python3 import json import os import re import sys import typing from time import sleep import jinja2 import lxml.html import requests parser = lxml.html.HTMLParser(encoding="utf-8") env = jinja2.Environment(loader=jinja2.FileSystemLoader("templates")) re_book = re.compile( r"function refreshGroupBox(group_id, book_id) \{(.*?)\n *\}", re.DOTALL ) re_book = re.compile( r"function refreshGroupBox\(group_id, book_id\) \{(.*?)\n *\}", re.DOTALL ) re_tip = re.compile(r'var newTip = new Tip\(\$\(\'[^\']+\'\), "(.*?)", {') dirs = ["shelf", "genre"] start = "https://www.goodreads.com/book/show/" existing = {book["title"] for book in json.load(open("calibre_book_list.json"))} def parse_book_div(div: lxml.html.HtmlElement) -> dict[str, typing.Any]: """Parse book div.""" link = div.find(".//a") assert link is not None rating = div.find('.//span[@class="minirating"]') description = div.find('./div[@class="bookDescription"]') assert rating is not None and rating[0] is not None r = rating[0].tail assert r div_next = div.getnext() assert div_next is not None cover_img = div_next.find(".//img") assert cover_img is not None cover = cover_img.get("src") book = { "title": link.text, "url": link.get("href"), "rating": r, "r": float(r[:3].strip()), "cover": cover, "authors": [a.text for a in div.find_class("authorName")], } if description is not None: index = 1 if len(description) == 3 else 0 book["description"] = description[index].text return book def iter_books(): for d in dirs: for f in sorted(os.listdir(d)): filename = os.path.join(d, f) root = lxml.html.parse(filename, parser=parser).getroot() for div in root.find_class("bookInformation"): book = parse_book_div(div) yield d, f, book continue # print(filename) for line in open(filename, "rb"): if b"var newTip" not in line: continue print(line) m = re_tip.search(line) tip = m.group(1).decode("unicode_escape").replace(r"\/", "/") # tip = m.group(1) # .replace('\/', '/') # print(tip) if '<ul class="formatting_tips recommendation_tip">' in tip: continue if "Recommendations are disabled for that shelf." in tip: continue if "Customize by selecting your" in tip: continue print(tip) yield (d, f, lxml.html.fromstring(tip)) def main() -> None: """Download books.""" template = env.get_template("books.html") seen = set() books = [] first_authors = set() for d, f, book in sorted(iter_books(), key=lambda i: i[2]["r"], reverse=True): # pprint(book) # print(repr(book.get('description'))) # continue # title_link = book.find_class('bookTitle')[0] url = book["url"] url = url[: url.find("?")] if url in seen: continue seen.add(url) title = book["title"] authors = book["authors"] first_authors.add(authors[0]) main_title = title # for sep in ['(']: for sep in ":", " - ", "(", ",": if sep in title: main_title = title[: title.find(sep)] break # print((main_title + ' by ' + u', '.join(authors)).encode('utf-8')) if len(main_title) < 10: continue if main_title in existing: continue # print(u'{} by {}'.format(main_title, authors[0]).encode('utf-8')) print("{}".format(main_title)) # print(main_title.encode('utf-8')) assert url.startswith(start) filename = "books/" + url[len(start) :] + ".html" # print(filename) if False and not os.path.exists(filename): open(filename, "w").write(requests.get(url).content) sleep(1) books.append( { "dir": d, "file": f[:-5], "title": title, "main_title": main_title, "authors": authors, "url": url, "rating": book["rating"], "cover": book["cover"], "description": book.get("description"), } ) page = template.render(books=books) open("book_list.html", "w").write(page) sys.exit(0) for a in sorted(first_authors): print(a) # authors = u' OR '.join(u'"{}"'.format(a) for a in sorted(first_authors) # if a not in {'Hugh Howey', 'Elizabeth Moon', 'Max Hastings'}) # print(authors.encode('utf-8')) if __name__ == "__main__": main()