{"id":19490710,"url":"https://github.com/sezanzeb/goodreads-recommender","last_synced_at":"2026-01-19T02:02:11.682Z","repository":{"id":252531388,"uuid":"840716076","full_name":"sezanzeb/goodreads-recommender","owner":"sezanzeb","description":"Filters books in lists and shelves, or makes recommendations based on your previous reads.","archived":false,"fork":false,"pushed_at":"2024-08-18T15:33:31.000Z","size":129,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-25T19:36:37.301Z","etag":null,"topics":["audiobooks","books","goodreads","reading","recommendation-engine","recommendation-system","recommender","recommender-engine","recommender-system","scraper"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sezanzeb.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-08-10T13:19:27.000Z","updated_at":"2025-03-10T20:58:44.000Z","dependencies_parsed_at":"2024-08-18T16:37:44.633Z","dependency_job_id":null,"html_url":"https://github.com/sezanzeb/goodreads-recommender","commit_stats":null,"previous_names":["sezanzeb/goodreads-recommender"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/sezanzeb/goodreads-recommender","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sezanzeb%2Fgoodreads-recommender","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sezanzeb%2Fgoodreads-recommender/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sezanzeb%2Fgoodreads-recommender/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sezanzeb%2Fgoodreads-recommender/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sezanzeb","download_url":"https://codeload.github.com/sezanzeb/goodreads-recommender/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sezanzeb%2Fgoodreads-recommender/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28557784,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-19T00:46:33.223Z","status":"online","status_checked_at":"2026-01-19T02:00:08.049Z","response_time":67,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audiobooks","books","goodreads","reading","recommendation-engine","recommendation-system","recommender","recommender-engine","recommender-system","scraper"],"created_at":"2024-11-10T21:14:05.179Z","updated_at":"2026-01-19T02:02:11.676Z","avatar_url":"https://github.com/sezanzeb.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e📖 📚\u003cbr/\u003eAdvanced Goodreads Filters\u003cbr/\u003eand Recommendations\u003c/h1\u003e\n\u003cbr/\u003e\nI'm not happy about the possibilities for filtering and getting recommendations on\ngoodreads. If you know some python, you can use this library to write simple little\nscripts to filter goodreads for you.\n\nFilters books in lists and shelves, or makes recommendations based on your previous\nreads. By using the custom callback for filters, you can have very powerful and\nfine-grained control over which books to include and remove from your recommendations,\nbased on many different custom criteria. An example implementation is available with\n`strict_filter`.\n\nDownloads are cached in the `goodreads_cache` directory, so the next time you run your\nscript, it will be a lot faster.\n\nIf this tool stops working, please try to make a backward-compatible fix, so that old\ncached files are still working, and create a pull request.\n\nWhen `parse_args` of `recommend` or `bootstrap_list_service` is `True`, you can use\n`--help` to display some command line options when running your script.\n\nThere are various methods for your own custom filters available in the `Book` class.\n\nSometimes parsing a page can fail, the scraper should usually continue doing its job\nand ignore that particular page.\n\nRequires python 3.11 or newer.\n\n```bash\n# clone\ngit clone https://github.com/sezanzeb/goodreads-recommender.git\ncd goodreads-recommender\n\n# dependencies\npip install poetry --break-system-packages\npoetry install\n\n# run\npython3 -m poetry run ./examples/recommend.py\n```\n\n# Recommendations Based on Previous Reads\n\n```python\n#!/usr/bin/env python\nfrom goodreads_recommender.bootstrap import recommend\nfrom goodreads_recommender.filters.strict_filter import strict_filter\n\n\ndef main():\n    # Cookie extracted from the browser (Firefox)\n    # - Open goodreads.com and log in\n    # - Go to the developer menu (F12)\n    # - Go to the \"Network\" tab\n    # - Open any page on goodreads\n    # - Find the \"html\" request to any https://www.goodreads.com/... site\n    # - Go to the \"Request Headers\"\n    # - check the switch to view the raw headers, otherwise they are truncated\n    # - Copy the value of the \"Cookie\" header here.\n    cookie = '...'\n\n    # I don't know if goodreads eventually blocks users who scrape their website. Use\n    # at your own risk. The cookie and the `user_id` don't have to be of the same user,\n    # the cookie is just needed to scrape profile pages.\n\n    recommend(\n        user_id=1234,\n        cookie=cookie,\n        output_file=\"./recommendations.txt\",\n        verbose=True,\n        # optional:\n        book_filter=strict_filter(\n            important_genres=[\"fantasy\", \"adult\"],\n            avoid_genres=[\"robots\", \"aliens\"],\n            require_audiobook=True,\n        ),\n        number_of_recommendations=20\n    )\n\n\nif __name__ == \"__main__\":\n    main()\n```\n\nThe result in recommendations.txt is sorted by how much a particular book is\nrecommended, starting with the best one.\n\nTruncated example output from recommendations.txt:\n\n```\n# Raw\n25307.Robin_Hobb                   41452-the-farseer-trilogy (6)      77197.Assassin_s_Apprentice        1995    4.18    fantasy, fiction, h...\n4763.John_Scalzi                   40789-old-man-s-war (15)           51964.Old_Man_s_War                2005    4.23    science-fiction, fi...\n153394.Suzanne_Collins             73758-the-hunger-games (8)         2767052-the-hunger-games           2008    4.34    young-adult, fictio...\n58.Frank_Herbert                   45935-dune (20)                    53764.The_Great_Dune_Trilogy       1979    4.37    science-fiction, fi...\n...\n\n# Filtered\n25307.Robin_Hobb                   41452-the-farseer-trilogy (6)      77197.Assassin_s_Apprentice        1995    4.18    fantasy, fiction, h...\n4763.John_Scalzi                   40789-old-man-s-war (15)           51964.Old_Man_s_War                2005    4.23    science-fiction, fi...\n58.Frank_Herbert                   45935-dune (20)                    53764.The_Great_Dune_Trilogy       1979    4.37    science-fiction, fi...\n346732.George_R_R_Martin           43790-a-song-of-ice-and-fire (11)  13496.A_Game_of_Thrones            1996    4.44    fantasy, fiction, e...\n...\n```\n\n![how-it-works.png](how-it-works.png)\n\n# Filtering Lists and Shelves\n\n```python\nfrom goodreads_recommender.bootstrap import bootstrap_list_service\nfrom goodreads_recommender.filters.strict_filter import strict_filter\n\nlist_service = bootstrap_list_service(\n    book_filter=strict_filter(\n        important_genres=[\"fantasy\", \"adult\"],\n        avoid_genres=[\"robots\", \"aliens\"],\n        minimum_rating=3,\n        require_audiobook=True,\n    ),\n    verbose=True,\n    output_file=\"./output.txt\",\n)\n\n# Add a \"Fantasy\" section to output.txt, using books from various lists and shelves.\n# They are going to be filtered in accordance to above configuration.\n# Repeat this step multiple times with different configurations to extend output.txt.\nlist_service.scan_books(\n    name=\"Fantasy\",\n    list_ids=[\"176302.Best_Cozy_Fantasy_Books\"],\n    shelf_ids=[\"fantasy\"],\n)\n\n# More `list_service.scan_books` calls to your hearts desire may follow. The result\n# will be appended to output.txt.\n```\nThe result in output.txt is sorted by author and series. It looks similar to the output\nof recommendations, with the `name` as the healdine of each section.\n\n## Development\n\n```bash\nsudo apt install mypy pylint black\nblack goodreads_recommender\npylint -E goodreads_recommender\nmypy goodreads_recommender\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsezanzeb%2Fgoodreads-recommender","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsezanzeb%2Fgoodreads-recommender","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsezanzeb%2Fgoodreads-recommender/lists"}