├── .gitignore ├── .rspec ├── Gemfile ├── lib ├── ratings.rb ├── gr_scraper.rb └── book_data.rb ├── .stickler.yml ├── spec ├── book_data_spec.rb ├── ratings_spec.rb ├── gr_scraper_spec.rb └── spec_helper.rb ├── Gemfile.lock ├── .rubocop.yml ├── bin └── main.rb ├── README.md └── csv_output └── sample_output.csv /.gitignore: -------------------------------------------------------------------------------- 1 | .byebug_history 2 | Gemfile.lock -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --require spec_helper 2 | --format documentation 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org/' 2 | 3 | gem 'byebug' 4 | gem 'nokogiri' 5 | gem 'rspec' 6 | gem 'ruby-progressbar' 7 | gem 'selenium-webdriver' 8 | -------------------------------------------------------------------------------- /lib/ratings.rb: -------------------------------------------------------------------------------- 1 | class Ratings 2 | attr_reader :avg_rating, :num_of_ratings 3 | def initialize(driver) 4 | @driver = driver 5 | end 6 | 7 | def avg_rating 8 | @driver.find_element(:xpath, '/html/body/div[2]/div[3]/div[1]/div[2]/div[2]/table/tbody/tr[1]/td[2]/div[1]/span/span').text.split[0].to_f 9 | end 10 | 11 | def num_of_ratings 12 | @driver.find_element(:xpath, '/html/body/div[2]/div[3]/div[1]/div[2]/div[2]/table/tbody/tr[1]/td[2]/div[1]/span/span').text.split[4].gsub(/,/, '').to_i 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/gr_scraper.rb: -------------------------------------------------------------------------------- 1 | class GReadsScraper 2 | attr_reader :reset_browser, :scrap_goodreads 3 | def initialize(driver, title_index = nil, all_books = nil) 4 | @title_index = title_index 5 | @all_books = all_books 6 | @driver = driver 7 | end 8 | 9 | def reset_browser 10 | @driver.navigate.back 11 | @driver.find_element(id: 'sitesearch_field').clear 12 | end 13 | 14 | def scrap_goodreads 15 | @driver.find_element(id: 'sitesearch_field').send_keys @all_books[@title_index][1], :return 16 | @driver 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /.stickler.yml: -------------------------------------------------------------------------------- 1 | # add the linters you want stickler to use for this project 2 | linters: 3 | rubocop: 4 | display_cop_names: true 5 | # indicate where is the config file for stylelint 6 | config: "./rubocop.yml" 7 | 8 | files: 9 | ignore: 10 | - "Guardfile" 11 | - "Rakefile" 12 | - "node_modules/**/*" 13 | 14 | # PLEASE DO NOT enable auto fixing options 15 | # if you need extra support from you linter - do it in your local env as described in README for this config 16 | # find full documentation here: https://stickler-ci.com/docs 17 | -------------------------------------------------------------------------------- /spec/book_data_spec.rb: -------------------------------------------------------------------------------- 1 | require 'nokogiri' 2 | require 'open-uri' 3 | require 'selenium-webdriver' 4 | require_relative '../lib/book_data.rb' 5 | require_relative '../lib/gr_scraper.rb' 6 | require_relative '../lib/ratings.rb' 7 | 8 | describe '.BookData' do 9 | scrap_nytimes = Nokogiri::HTML(URI.open('https://www.nytimes.com/books/best-sellers/')) 10 | NYTPAGE_CONST = scrap_nytimes.css('section.e8j42380') 11 | it '#book_data_array is returned containing book data' do 12 | expect(BookData.new(0, NYTPAGE_CONST[0]).book_data_array).to eq(['Combined Print & E-Book Fiction', 'CAMINO WINDS', 'by John Grisham', 3, 'The line between fact and fiction becomes blurred when an author of thrillers is found dead after a hurricane hits Camino Island.', 'https://www.amazon.com/dp/0385545932?tag=NYTBSREV-20&tag=NYTBS-20']) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/ratings_spec.rb: -------------------------------------------------------------------------------- 1 | require 'nokogiri' 2 | require 'open-uri' 3 | require 'selenium-webdriver' 4 | require_relative '../lib/book_data.rb' 5 | require_relative '../lib/gr_scraper.rb' 6 | require_relative '../lib/ratings.rb' 7 | 8 | describe '.ratings' do 9 | let(:test_ratigns) { Ratings.new(gr_web_page) } 10 | 11 | context '#avg_rating' do 12 | let(:gr_web_page) { Nokogiri::HTML(open('https://www.goodreads.com/search?utf8=%E2%9C%93&query=CAMINO+WINDS')) } 13 | it 'returns the average rating' do 14 | expect(gr_web_page.at_css('span.minirating').text.split[0].to_f).to be_a(Float) 15 | end 16 | end 17 | context '#num_of_ratings' do 18 | let(:gr_web_page) { Nokogiri::HTML(open('https://www.goodreads.com/search?utf8=%E2%9C%93&query=CAMINO+WINDS')) } 19 | it 'returns the number of ratings' do 20 | expect(gr_web_page.at_css('span.minirating').text.split[4].gsub(/,/, '').to_i).to be_a(Integer) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | byebug (11.1.3) 5 | childprocess (3.0.0) 6 | diff-lcs (1.3) 7 | mini_portile2 (2.4.0) 8 | nokogiri (1.10.9) 9 | mini_portile2 (~> 2.4.0) 10 | rspec (3.9.0) 11 | rspec-core (~> 3.9.0) 12 | rspec-expectations (~> 3.9.0) 13 | rspec-mocks (~> 3.9.0) 14 | rspec-core (3.9.2) 15 | rspec-support (~> 3.9.3) 16 | rspec-expectations (3.9.2) 17 | diff-lcs (>= 1.2.0, < 2.0) 18 | rspec-support (~> 3.9.0) 19 | rspec-mocks (3.9.1) 20 | diff-lcs (>= 1.2.0, < 2.0) 21 | rspec-support (~> 3.9.0) 22 | rspec-support (3.9.3) 23 | ruby-progressbar (1.10.1) 24 | rubyzip (2.3.0) 25 | selenium-webdriver (3.142.7) 26 | childprocess (>= 0.5, < 4.0) 27 | rubyzip (>= 1.2.2) 28 | 29 | PLATFORMS 30 | ruby 31 | 32 | DEPENDENCIES 33 | byebug 34 | nokogiri 35 | rspec 36 | ruby-progressbar 37 | selenium-webdriver 38 | 39 | BUNDLED WITH 40 | 1.17.2 41 | -------------------------------------------------------------------------------- /lib/book_data.rb: -------------------------------------------------------------------------------- 1 | class BookData 2 | attr_reader :book_data_array 3 | def initialize(book_info_index, section_index) 4 | @book_info_index = book_info_index 5 | @section_index = section_index 6 | end 7 | 8 | def book_data_array 9 | [section_name, title, author, time, description, link] 10 | end 11 | 12 | private 13 | 14 | def section_name 15 | @section_index.css('a.css-nzgijy').text 16 | end 17 | 18 | def book_info 19 | NYTPAGE_CONST.css('li.css-1mr03gh') 20 | end 21 | 22 | def title 23 | book_info[@book_info_index].css('h3.css-i1z3c1').text 24 | end 25 | 26 | def author 27 | book_info[@book_info_index].css('p.css-1nxjbfc').text 28 | end 29 | 30 | def time 31 | book_info[@book_info_index].css('p.css-t7cods').text.gsub(/\D/, '').to_i 32 | end 33 | 34 | def description 35 | book_info[@book_info_index].css('p.css-5yxv3r').text 36 | end 37 | 38 | def link 39 | book_info[@book_info_index].at_css('a.css-hndxeu').attributes['href'].value 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | Exclude: 3 | - "README.md" 4 | - "Guardfile" 5 | - "Rakefile" 6 | 7 | DisplayCopNames: true 8 | 9 | Layout/LineLength: 10 | Max: 350 11 | Metrics/MethodLength: 12 | Max: 20 13 | Metrics/AbcSize: 14 | Max: 50 15 | Metrics/ClassLength: 16 | Max: 150 17 | Metrics/BlockLength: 18 | ExcludedMethods: ["describe"] 19 | Max: 30 20 | 21 | Style/Documentation: 22 | Enabled: false 23 | Style/ClassAndModuleChildren: 24 | Enabled: false 25 | Style/EachForSimpleLoop: 26 | Enabled: false 27 | Style/AndOr: 28 | Enabled: false 29 | Style/DefWithParentheses: 30 | Enabled: false 31 | Style/FrozenStringLiteralComment: 32 | EnforcedStyle: never 33 | 34 | Layout/HashAlignment: 35 | EnforcedColonStyle: key 36 | Layout/ExtraSpacing: 37 | AllowForAlignment: false 38 | Layout/MultilineMethodCallIndentation: 39 | Enabled: true 40 | EnforcedStyle: indented 41 | Lint/RaiseException: 42 | Enabled: false 43 | Lint/StructNewOverride: 44 | Enabled: false 45 | Style/HashEachMethods: 46 | Enabled: false 47 | Style/HashTransformKeys: 48 | Enabled: false 49 | Style/HashTransformValues: 50 | Enabled: false 51 | Lint/DuplicateMethods: 52 | Enabled: false 53 | -------------------------------------------------------------------------------- /spec/gr_scraper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'nokogiri' 2 | require 'open-uri' 3 | require 'selenium-webdriver' 4 | require_relative '../lib/book_data.rb' 5 | require_relative '../lib/gr_scraper.rb' 6 | require_relative '../lib/ratings.rb' 7 | 8 | describe '.gr_scraper' do 9 | let(:test_gr_scraper) { GReadsScraper.new(driver, 0, [['CAMINO WINDS']]) } 10 | let(:driver) { Selenium::WebDriver.for :chrome, options: options } 11 | let(:options) { Selenium::WebDriver::Chrome::Options.new } 12 | 13 | context '#scrap_goodreads method testing' do 14 | it '#scrap_goodreads must return nil after loading the page' do 15 | options.add_argument('--headless') 16 | driver.get('https://goodreads.com') 17 | expect(driver.find_element(id: 'sitesearch_field').send_keys('Camino Winds')).to be nil 18 | end 19 | end 20 | 21 | context '#reset_browser testing' do 22 | it '#reset_browser returns nil if action is complete' do 23 | options.add_argument('--headless') 24 | driver.get('https://goodreads.com') 25 | driver.get('https://www.goodreads.com/search?utf8=%E2%9C%93&query=CAMINO+WINDS') 26 | expect(test_gr_scraper.reset_browser).to be_nil 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /bin/main.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'nokogiri' 3 | require 'byebug' 4 | require 'csv' 5 | require 'ruby-progressbar' 6 | require 'open-uri' 7 | require 'selenium-webdriver' 8 | require_relative '../lib/book_data.rb' 9 | require_relative '../lib/gr_scraper.rb' 10 | require_relative '../lib/ratings.rb' 11 | 12 | scrap_nytimes = Nokogiri::HTML(URI.open('https://www.nytimes.com/books/best-sellers/')) 13 | NYTPAGE_CONST = scrap_nytimes.css('section.e8j42380') 14 | 15 | new_csv = CSV.open('csv_output/Book_Recommendations.csv', 'a+') 16 | new_csv << %w[Section Title Author Time(Weeks) Avg_Rating Num_of_Ratings Description Link] 17 | 18 | books_csv = new_csv 19 | all_books = [] 20 | i = 0 21 | j = 0 22 | 23 | progressbar = ProgressBar.create(title: 'Books', total: 55, length: 80, format: '%a <%B> %p%% %t') 24 | begin 25 | driver = Selenium::WebDriver.for :chrome 26 | wait = Selenium::WebDriver::Wait.new(timeout: 10) 27 | driver.get('https://goodreads.com') 28 | 29 | NYTPAGE_CONST.each do |section| 30 | 5.times do 31 | book = BookData.new(i, section).book_data_array 32 | i += 1 33 | all_books << book 34 | until j == all_books.count 35 | GReadsScraper.new(driver, j, all_books).scrap_goodreads 36 | rating = Ratings.new(driver) 37 | avg_rating = wait.until { rating.avg_rating } 38 | num_of_ratings = wait.until { rating.num_of_ratings } 39 | book.insert(4, avg_rating) 40 | book.insert(5, num_of_ratings) 41 | GReadsScraper.new(driver).reset_browser 42 | j += 1 43 | progressbar.increment 44 | end 45 | books_csv << book 46 | end 47 | end 48 | ensure 49 | driver.quit 50 | end 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # New York Times Best Sellers Curated with GoodReads.com User Ratings (Capstone Project) 2 | 3 | > This project scrapes the New York Times best selling books and finds the corresponding user ratings, combining them into a CSV file. Built for the Ruby Capstone project in the Microverse curriculum. 4 | 5 | This project includes the following features: 6 | 7 | 1. Scrapes [New York Times Best Sellers Static Website](https://www.nytimes.com/books/best-sellers/) 8 | 2. Book info is used from step 1 and finds the user ratings and number of ratings from [goodreads](https://www.goodreads.com/) 9 | 3. Finally, everything gets displayed categorically in a CSV file 10 | 11 | #### [I present it all to you!](https://www.loom.com/share/e26883bca4ef4caf8a4b4c6c7fef17ae) 12 | 13 | ## Built With 14 | 15 | - Ruby 16 | - Tested with RSpec 17 | - Gems used: nokogiri, ruby-progressbar and selenium-webdriver 18 | 19 | ## Getting Started 20 | 21 | 1. Clone the project to your local machine; 22 | 2. cd into the project directory; 23 | 3. Run bundle install to install the necessary gems; 24 | 4. Installing the ChromeDriver: 25 | 1. Navigate to the [ChromeDriver - WebDriver for Chrome](https://chromedriver.chromium.org/getting-started) webpage; 26 | 2. Read the instruction to setup ChromeDriver on your computer; 27 | 3. Visit the [download page](https://chromedriver.chromium.org/downloads); 28 | 4. Please ensure that you download the version that is compatible with the chrome web browser you have installed on your computer; 29 | 5. Move the chromedriver along your path, e.g Ubuntu Linux you will move it to `usr/local/bin` 30 | 5. Run bin/main.rb in your terminal; 31 | 6. A new chrome window will open and the magic will begin, just let it do it's job; 32 | 7. A progress bar will appear in the terminal, wait for it to reach 100%; 33 | 8. Navigate to the project directory and inside the `csv_output` folder; 34 | 9. Inside the `csv_output` folder, there will be a file called `Book_Recommendations.csv` 35 | 10. Enjoy your curated data! 36 | 37 | ### NOTE 1: If you get a TCP error when running the program, just wait a few seconds and then run it again 38 | 39 | ### NOTE 2: If you get a "Unable to find chromedriver" error then you need to revert to step 4 above 40 | 41 | ## Authors 42 | 43 | 👤 **Azeem Ahmed** 44 | 45 | - Github: [@Azeem838](https://github.com/Azeem838) 46 | - LinkedIn: [Azeem Ahmed](www.linkedin.com/in/azeemmahmed) 47 | 48 | ## 🤝 Contributing 49 | 50 | Contributions, issues and feature requests are welcome! 51 | 52 | Feel free to check the [issues page](https://github.com/Azeem838/web-scraper-ruby-capstone/issues). 53 | 54 | ## Show your support 55 | 56 | Give a ⭐️ if you like this project! 57 | 58 | ## 📝 License 59 | 60 | This project is [MIT](lic.url) licensed. 61 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file was generated by the `rspec --init` command. Conventionally, all 2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 3 | # The generated `.rspec` file contains `--require spec_helper` which will cause 4 | # this file to always be loaded, without a need to explicitly require it in any 5 | # files. 6 | # 7 | # Given that it is always loaded, you are encouraged to keep this file as 8 | # light-weight as possible. Requiring heavyweight dependencies from this file 9 | # will add to the boot time of your test suite on EVERY test run, even for an 10 | # individual file that may not need all of that loaded. Instead, consider making 11 | # a separate helper file that requires the additional dependencies and performs 12 | # the additional setup, and require it from the spec files that actually need 13 | # it. 14 | # 15 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 16 | RSpec.configure do |config| 17 | # rspec-expectations config goes here. You can use an alternate 18 | # assertion/expectation library such as wrong or the stdlib/minitest 19 | # assertions if you prefer. 20 | config.expect_with :rspec do |expectations| 21 | # This option will default to `true` in RSpec 4. It makes the `description` 22 | # and `failure_message` of custom matchers include text for helper methods 23 | # defined using `chain`, e.g.: 24 | # be_bigger_than(2).and_smaller_than(4).description 25 | # # => "be bigger than 2 and smaller than 4" 26 | # ...rather than: 27 | # # => "be bigger than 2" 28 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 29 | end 30 | 31 | # rspec-mocks config goes here. You can use an alternate test double 32 | # library (such as bogus or mocha) by changing the `mock_with` option here. 33 | config.mock_with :rspec do |mocks| 34 | # Prevents you from mocking or stubbing a method that does not exist on 35 | # a real object. This is generally recommended, and will default to 36 | # `true` in RSpec 4. 37 | mocks.verify_partial_doubles = true 38 | end 39 | 40 | # This option will default to `:apply_to_host_groups` in RSpec 4 (and will 41 | # have no way to turn it off -- the option exists only for backwards 42 | # compatibility in RSpec 3). It causes shared context metadata to be 43 | # inherited by the metadata hash of host groups and examples, rather than 44 | # triggering implicit auto-inclusion in groups with matching metadata. 45 | config.shared_context_metadata_behavior = :apply_to_host_groups 46 | 47 | # The settings below are suggested to provide a good initial experience 48 | # with RSpec, but feel free to customize to your heart's content. 49 | # # =begin 50 | # # This allows you to limit a spec run to individual examples or groups 51 | # # you care about by tagging them with `:focus` metadata. When nothing 52 | # # is tagged with `:focus`, all examples get run. RSpec also provides 53 | # # aliases for `it`, `describe`, and `context` that include `:focus` 54 | # # metadata: `fit`, `fdescribe` and `fcontext`, respectively. 55 | # config.filter_run_when_matching :focus 56 | 57 | # # Allows RSpec to persist some state between runs in order to support 58 | # # the `--only-failures` and `--next-failure` CLI options. We recommend 59 | # # you configure your source control system to ignore this file. 60 | # config.example_status_persistence_file_path = "spec/examples.txt" 61 | 62 | # # Limits the available syntax to the non-monkey patched syntax that is 63 | # # recommended. For more details, see: 64 | # # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ 65 | # # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 66 | # # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode 67 | # config.disable_monkey_patching! 68 | 69 | # # This setting enables warnings. It's recommended, but in some cases may 70 | # # be too noisy due to issues in dependencies. 71 | # config.warnings = true 72 | 73 | # # Many RSpec users commonly either run the entire suite or an individual 74 | # # file, and it's useful to allow more verbose output when running an 75 | # # individual spec file. 76 | # if config.files_to_run.one? 77 | # # Use the documentation formatter for detailed output, 78 | # # unless a formatter has already been configured 79 | # # (e.g. via a command-line flag). 80 | # config.default_formatter = "doc" 81 | # end 82 | 83 | # # Print the 10 slowest examples and example groups at the 84 | # # end of the spec run, to help surface which specs are running 85 | # # particularly slow. 86 | # config.profile_examples = 10 87 | 88 | # # Run specs in random order to surface order dependencies. If you find an 89 | # # order dependency and want to debug it, you can fix the order by providing 90 | # # the seed, which is printed after each run. 91 | # # --seed 1234 92 | # config.order = :random 93 | 94 | # # Seed global randomization in this process using the `--seed` CLI option. 95 | # # Setting this allows you to use `--seed` to deterministically reproduce 96 | # # test failures related to randomization by passing the same `--seed` value 97 | # # as the one that triggered the failure. 98 | # Kernel.srand config.seed 99 | # =end 100 | end 101 | -------------------------------------------------------------------------------- /csv_output/sample_output.csv: -------------------------------------------------------------------------------- 1 | Section,Title,Author,Time(Weeks),Avg_Rating,Num_of_Ratings,Description,Link 2 | Combined Print & E-Book Fiction,CAMINO WINDS,by John Grisham,3,4.01,6345,The line between fact and fiction becomes blurred when an author of thrillers is found dead after a hurricane hits Camino Island.,https://www.amazon.com/dp/0385545932?tag=NYTBSREV-20&tag=NYTBS-20 3 | Combined Print & E-Book Fiction,WHERE THE CRAWDADS SING,by Delia Owens,88,4.49,735578,"In a quiet town on the North Carolina coast in 1969, a young woman who survived alone in the marsh becomes a murder suspect.",https://www.amazon.com/Where-Crawdads-Sing-Delia-Owens/dp/0735219095?tag=NYTBS-20 4 | Combined Print & E-Book Fiction,LITTLE FIRES EVERYWHERE,by Celeste Ng,66,4.1,582818,An artist upends a quiet town outside Cleveland.,https://www.amazon.com/Little-Fires-Everywhere-Celeste-Ng-ebook/dp/B01N4VW75U?tag=NYTBS-20 5 | Combined Print & E-Book Fiction,IF IT BLEEDS,by Stephen King,4,4.23,8622,"Four novellas: “Mr. Harrigan’s Phone,” “The Life of Chuck,” “Rat” and “If It Bleeds.”",https://www.amazon.com/dp/1982137975?tag=NYTBSREV-20&tag=NYTBS-20 6 | Combined Print & E-Book Fiction,WALK THE WIRE,by David Baldacci,4,4.28,7667,The sixth book in the Memory Man series. Decker and Jamison investigate a murder in a North Dakota town in a fracking boom.,https://www.amazon.com/dp/1538761467?tag=NYTBSREV-20&tag=NYTBS-20 7 | Combined Print & E-Book Nonfiction,UNTAMED,by Glennon Doyle,10,3.95,209698,The activist and public speaker describes her journey of listening to her inner voice.,https://www.amazon.com/dp/1984801252?tag=NYTBSREV-20&tag=NYTBS-20 8 | Combined Print & E-Book Nonfiction,BECOMING,by Michelle Obama,76,4.55,452648,"The former first lady describes her journey from the South Side of Chicago to the White House, and how she balanced work, family and her husband’s political ascent.",https://www.amazon.com/Becoming-Michelle-Obama/dp/1524763136?tag=NYTBS-20 9 | Combined Print & E-Book Nonfiction,THE SPLENDID AND THE VILE,by Erik Larson,12,4.32,15307,An examination of the leadership of the prime minister Winston Churchill.,https://www.amazon.com/Splendid-Vile-Churchill-Family-Defiance/dp/0385348711?tag=NYTBS-20 10 | Combined Print & E-Book Nonfiction,EDUCATED,by Tara Westover,117,4.47,655303,"The daughter of survivalists, who is kept out of school, educates herself enough to leave home for university.",https://www.amazon.com/Educated-Memoir-Tara-Westover/dp/0399590501?tag=NYTBS-20 11 | Combined Print & E-Book Nonfiction,THE GREAT INFLUENZA,by John M. Barry,10,3.98,21450,An overview of the 1918 flu epidemic and cautionary tale for similar kinds of large-scale outbreaks.,http://www.amazon.com/The-Great-Influenza-Deadliest-Pandemic/dp/0143036491?tag=NYTBS-20 12 | Hardcover Fiction,WHERE THE CRAWDADS SING,by Delia Owens,89,4.49,735578,"In a quiet town on the North Carolina coast in 1969, a young woman who survived alone in the marsh becomes a murder suspect.",https://www.amazon.com/Where-Crawdads-Sing-Delia-Owens/dp/0735219095?tag=NYTBS-20 13 | Hardcover Fiction,CAMINO WINDS,by John Grisham,3,4.01,6345,The line between fact and fiction becomes blurred when an author of thrillers is found dead after a hurricane hits Camino Island.,https://www.amazon.com/dp/0385545932?tag=NYTBSREV-20&tag=NYTBS-20 14 | Hardcover Fiction,IF IT BLEEDS,by Stephen King,4,4.23,8622,"Four novellas: “Mr. Harrigan’s Phone,” “The Life of Chuck,” “Rat” and “If It Bleeds.”",https://www.amazon.com/dp/1982137975?tag=NYTBSREV-20&tag=NYTBS-20 15 | Hardcover Fiction,WALK THE WIRE,by David Baldacci,4,4.28,7667,The sixth book in the Memory Man series. Decker and Jamison investigate a murder in a North Dakota town in a fracking boom.,https://www.amazon.com/dp/1538761467?tag=NYTBSREV-20&tag=NYTBS-20 16 | Hardcover Fiction,THE 20TH VICTIM,by James Patterson and Maxine Paetro,2,4.24,2448,The 20th book in the Women’s Murder Club series. Lindsay Boxer looks into the murders of disreputable persons in three separate cities.,https://www.amazon.com/dp/031642028X?tag=NYTBSREV-20&tag=NYTBS-20 17 | Hardcover Nonfiction,UNTAMED,by Glennon Doyle,10,3.95,209698,The activist and public speaker describes her journey of listening to her inner voice.,https://www.amazon.com/dp/1984801252?tag=NYTBSREV-20&tag=NYTBS-20 18 | Hardcover Nonfiction,BECOMING,by Michelle Obama,75,4.55,452648,"The former first lady describes how she balanced work, family and her husband’s political ascent.",https://www.amazon.com/Becoming-Michelle-Obama/dp/1524763136?tag=NYTBS-20 19 | Hardcover Nonfiction,THE SPLENDID AND THE VILE,by Erik Larson,12,4.32,15307,An examination of the leadership of the prime minister Winston Churchill.,https://www.amazon.com/Splendid-Vile-Churchill-Family-Defiance/dp/0385348711?tag=NYTBS-20 20 | Hardcover Nonfiction,EDUCATED,by Tara Westover,117,4.47,655303,"The daughter of survivalists, who is kept out of school, educates herself enough to leave home for university.",https://www.amazon.com/Educated-Memoir-Tara-Westover/dp/0399590501?tag=NYTBS-20 21 | Hardcover Nonfiction,PLAGUE OF CORRUPTION,by Judy Mikovits and Kent Heckenlively,2,4.23,186,The controversial virologist gives her account of her work over nearly four decades.,https://www.amazon.com/dp/1510752242?tag=NYTBSREV-20&tag=NYTBS-20 22 | Paperback Trade Fiction,NORMAL PEOPLE,by Sally Rooney,13,3.87,261590,The connection between a high school star athlete and a loner ebbs and flows when they go to Trinity College in Dublin.,https://www.amazon.com/Normal-People-Novel-Sally-Rooney/dp/1984822179?tag=NYTBS-20 23 | Paperback Trade Fiction,LITTLE FIRES EVERYWHERE,by Celeste Ng,54,4.1,582818,An artist with a mysterious past and a disregard for the status quo upends a quiet town outside Cleveland.,https://www.amazon.com/Little-Fires-Everywhere-Celeste-Ng-ebook/dp/B01N4VW75U?tag=NYTBS-20 24 | Paperback Trade Fiction,THE WOMAN IN THE WINDOW,by A.J. Finn,56,3.94,389277,A recluse who drinks heavily and takes prescription drugs may have witnessed a crime across from her Harlem townhouse.,https://www.amazon.com/Woman-Window-Novel-J-Finn/dp/0062678418?tag=NYTBS-20 25 | Paperback Trade Fiction,THEN SHE WAS GONE,by Lisa Jewell,44,4.03,163585,"Ten years after her daughter disappears, a woman tries to get her life in order but remains haunted by unanswered questions.",https://www.amazon.com/Then-She-Was-Gone-Novel-ebook/dp/B074MDD3H6?tag=NYTBS-20 26 | Paperback Trade Fiction,THE TATTOOIST OF AUSCHWITZ,by Heather Morris,89,4.26,381707,A concentration camp detainee tasked with permanently marking fellow prisoners falls in love with one of them.,https://www.amazon.com/Tattooist-Auschwitz-Novel-Heather-Morris/dp/0062797158?tag=NYTBS-20 27 | Paperback Nonfiction,THE BODY KEEPS THE SCORE,by Bessel van der Kolk,82,4.54,27795,"How trauma affects the body and mind, and innovative treatments for recovery.",http://www.amazon.com/The-Body-Keeps-Score-Healing/dp/0670785938?tag=NYTBS-20 28 | Paperback Nonfiction,THE GREAT INFLUENZA,by John M. Barry,10,3.98,21450,An overview of the 1918 flu epidemic and cautionary tale for similar kinds of large-scale outbreaks.,http://www.amazon.com/The-Great-Influenza-Deadliest-Pandemic/dp/0143036491?tag=NYTBS-20 29 | Paperback Nonfiction,WHITE FRAGILITY,by Robin DiAngelo,89,4.49,23094,Historical and cultural analyses on what causes defensive moves by white people and how this inhibits cross-racial dialogue.,https://www.amazon.com/White-Fragility-People-About-Racism/dp/0807047414?tag=NYTBS-20 30 | Paperback Nonfiction,A WOMAN OF NO IMPORTANCE,by Sonia Purnell,7,4.21,9121,The true story of a Baltimore socialite who joined a spy organization during World War II and became essential to the French Resistance.,https://www.amazon.com/Woman-No-Importance-Untold-American/dp/073522529X?tag=NYTBS-20 31 | Paperback Nonfiction,SAPIENS,by Yuval Noah Harari,105,4.43,417478,How Homo sapiens became Earth’s dominant species.,http://www.amazon.com/Sapiens-A-Brief-History-Humankind-ebook/dp/B00ICN066A?tag=NYTBS-20 32 | "Advice, How-To & Miscellaneous","MAGNOLIA TABLE, VOL. 2",by Joanna Gaines,6,4.41,400,"",https://www.amazon.com/dp/0062820184?tag=NYTBSREV-20&tag=NYTBS-20 33 | "Advice, How-To & Miscellaneous",RELATIONSHIP GOALS,by Michael Todd,3,4.53,303,"",https://www.amazon.com/dp/0593192575?tag=NYTBSREV-20&tag=NYTBS-20 34 | "Advice, How-To & Miscellaneous","THE BOY, THE MOLE, THE FOX AND THE HORSE",by Charlie Mackesy,28,4.64,19188,"",https://www.amazon.com/Boy-Mole-Fox-Horse/dp/0062976583?tag=NYTBS-20 35 | "Advice, How-To & Miscellaneous",MAGNOLIA TABLE,by Joanna Gaines with Marah Stets,43,4.25,4788,"",https://www.amazon.com/Magnolia-Table-Collection-Recipes-Gathering/dp/006282015X?tag=NYTBS-20 36 | "Advice, How-To & Miscellaneous",ATOMIC HABITS,by James Clear,27,4.35,80413,"",https://www.amazon.com/Atomic-Habits-Proven-Build-Break/dp/0735211299?tag=NYTBS-20 37 | Children’s Middle Grade Hardcover,THE ONE AND ONLY BOB,by Katherine Applegate. Illustrated by Patricia Castelao,2,4.5,1020,"In this sequel to ""The One and Only Ivan,"" Bob sets out on a dangerous journey in search of his long-lost sister.",https://www.amazon.com/dp/0062991310?tag=NYTBSREV-20&tag=NYTBS-20 38 | Children’s Middle Grade Hardcover,THE COMPLETE COOKBOOK FOR YOUNG CHEFS,by America's Test Kitchen Kids,57,4.34,179,Over 100 kid-tested recipes from America's Test Kitchen.,https://www.amazon.com/Complete-Cookbook-Young-Chefs/dp/1492670022?tag=NYTBS-20 39 | Children’s Middle Grade Hardcover,THE COMPLETE BAKING BOOK FOR YOUNG CHEFS,by America's Test Kitchen Kids,15,4.43,49,One hundred plus kid-tested baking recipes.,https://www.amazon.com/Complete-Baking-Book-Young-Chefs/dp/1492677698?tag=NYTBS-20 40 | Children’s Middle Grade Hardcover,WONDER,by R.J. Palacio,248,4.45,678233,A boy with a facial deformity starts school.,http://www.amazon.com/Wonder-R-J-Palacio-ebook/dp/B0051ANPZQ?tag=NYTBS-20 41 | Children’s Middle Grade Hardcover,DIARY OF AN AWESOME FRIENDLY KID,by Jeff Kinney,58,4.06,16713,Greg’s best friend Rowley Jefferson writes his own diary.,https://www.amazon.com/Diary-Awesome-Friendly-Jeff-Kinney/dp/141974027X?tag=NYTBS-20 42 | Children’s Picture Books,I WISH YOU MORE,by Amy Krouse Rosenthal. Illustrated by Tom Lichtenheld,56,4.38,3483,A bounty of good wishes.,http://www.amazon.com/Wish-You-More-Krouse-Rosenthal/dp/1452126992?tag=NYTBS-20 43 | Children’s Picture Books,WAITING IS NOT EASY!,by Mo Willems,73,4.45,4198,Impatient Gerald has to wait for Piggie’s promised surprise.,http://www.amazon.com/Waiting-Easy-Elephant-Piggie-Book/dp/142319957X?tag=NYTBS-20 44 | Children’s Picture Books,GRUMPY MONKEY,by Suzanne Lang. Illustrated by Max Lang,33,4.26,1935,Jim Panzee is having a bad day.,https://www.amazon.com/Grumpy-Monkey-Suzanne-Lang/dp/0553537865?tag=NYTBS-20 45 | Children’s Picture Books,THE WONDERFUL THINGS YOU WILL BE,by Emily Winfield Martin,234,4.39,3148,A celebration of future possibilities.,http://www.amazon.com/The-Wonderful-Things-You-Will/dp/0385376715?tag=NYTBS-20 46 | Children’s Picture Books,DRAGONS LOVE TACOS,by Adam Rubin. Illustrated by Daniel Salmieri,306,4.17,11117,What to serve your dragon-guests.,http://www.amazon.com/Dragons-Love-Tacos-Adam-Rubin/dp/0803736800?tag=NYTBS-20 47 | Children’s Series,HARRY POTTER,by J.K. Rowling,582,4.47,6689953,A wizard hones his conjuring skills in the service of fighting evil.,http://www.amazon.com/Harry-Potter-And-Order-Phoenix/dp/0439358078?tag=NYTBS-20 48 | Children’s Series,DOG MAN,by Dav Pilkey,142,4.21,20078,A dog’s head is combined with a policeman’s body to create this hybrid supercop hound.,https://www.amazon.com/Dog-Man-Kitties-Creator-Underpants/dp/0545935210?tag=NYTBS-20 49 | Children’s Series,DIARY OF A WIMPY KID,written and illustrated by Jeff Kinney,583,3.99,406105,The travails and challenges of adolescence.,http://www.amazon.com/Diary-Wimpy-Kid-Hard-Luck/dp/1419711326?tag=NYTBS-20 50 | Children’s Series,THE BAD GUYS,by Aaron Blabey,78,4.21,4681,Tough animals in suits take on some real villains.,https://www.amazon.com/Bad-Guys-Intergalactic-Gas/dp/1338189573?tag=NYTBS-20 51 | Children’s Series,THE LAST KIDS ON EARTH,by Max Brallier. Illustrated by Douglas Holgate,36,4.21,6181,Jack and his friends fight for their lives through the zombie apocalypse.,https://www.amazon.com/Last-Kids-Earth-Nightmare-King/dp/0425288714?tag=NYTBS-20 52 | Young Adult Hardcover,THE BETROTHED,by Kiera Cass,2,3.07,3263,"Lady Hollis Brite and King Jameson are set to be married, but will a commoner steal Hollis’s heart?",https://www.amazon.com/dp/0062291637?tag=NYTBSREV-20&tag=NYTBS-20 53 | Young Adult Hardcover,ONE OF US IS LYING,by Karen M. McManus,121,4.05,175018,"For five students, a detour into detention ends in murder.",https://www.amazon.com/One-Us-Lying-Karen-McManus/dp/1524714682?tag=NYTBS-20 54 | Young Adult Hardcover,CHAIN OF GOLD,by Cassandra Clare,11,4.52,22041,Cordelia battles demons in a quarantined London that are nothing like she’s encountered before.,https://www.amazon.com/dp/1481431870?tag=NYTBSREV-20&tag=NYTBS-20 55 | Young Adult Hardcover,CLAP WHEN YOU LAND,by Elizabeth Acevedo,2,4.48,2279,"Unbeknownst to each other, two sisters meet when their father dies in a plane crash.",https://www.amazon.com/dp/0062882767?tag=NYTBSREV-20&tag=NYTBS-20 56 | Young Adult Hardcover,CHILDREN OF VIRTUE AND VENGEANCE,by Tomi Adeyemi,24,3.91,21052,Zélie must stop the threat of civil war in Orïsha.,https://www.amazon.com/Children-Virtue-Vengeance-Legacy-Orisha/dp/1250170990?tag=NYTBS-20 57 | --------------------------------------------------------------------------------