Skip to content
On this page

Web scraping with Ruby means fetching a page with an HTTP gem like HTTParty and parsing the HTML with Nokogiri, which supports both CSS selectors and XPath. Nokogiri is the de-facto standard parser in the Ruby ecosystem. For JavaScript-rendered pages you drive a real browser with Selenium or Watir, and for full crawlers there are frameworks built on these gems.

Quick facts

HTML parsingNokogiri — CSS and XPath, the Ruby standard
HTTP clientHTTParty or Faraday (or built-in net/http)
JavaScript pagesSelenium WebDriver or Watir
Form/session helperMechanize — cookies, forms, link-following
Installgem install nokogiri httparty

Your first Ruby scraper with HTTParty + Nokogiri

The classic Ruby stack is HTTParty to fetch and Nokogiri to parse. Install both with gem install nokogiri httparty, then:

require 'httparty'
require 'nokogiri'

response = HTTParty.get('https://books.toscrape.com/',
  headers: { 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)' })

doc = Nokogiri::HTML(response.body)

doc.css('article.product_pod').each do |book|
  title = book.at_css('h3 a')['title']
  price = book.at_css('.price_color').text
  puts "#{title} | #{price}"
end

doc.css(selector) returns all matching nodes; at_css returns the first. Access attributes with node['attr'] and text with .text. Nokogiri also supports XPath via doc.xpath('//...') when you need it. Always pass a realistic User-Agent — the default identifies your script as a bot.

Following pagination

To crawl multiple pages, read the "next" link and resolve it against the current URL with URI.join:

require 'uri'

url = 'https://books.toscrape.com/'
while url
  doc = Nokogiri::HTML(HTTParty.get(url).body)

  doc.css('article.product_pod h3 a').each { |a| puts a['title'] }

  nxt = doc.at_css('li.next a')
  url = nxt ? URI.join(url, nxt['href']).to_s : nil   # nil ends the loop
end

For sites that need cookies, logins, or form submissions, the Mechanize gem wraps Nokogiri with session and form handling, so you do not manage cookies by hand.

Scraping JavaScript-rendered pages

Nokogiri only parses the HTML you give it — it does not run JavaScript. For client-side-rendered pages, drive a browser with Selenium (gem install selenium-webdriver):

require 'selenium-webdriver'

options = Selenium::WebDriver::Chrome::Options.new
options.add_argument('--headless=new')
driver = Selenium::WebDriver.for(:chrome, options: options)

driver.get('https://quotes.toscrape.com/js/')

driver.find_elements(css: '.quote').each do |q|
  text = q.find_element(css: '.text').text
  author = q.find_element(css: '.author').text
  puts "#{author}: #{text}"
end

driver.quit

Watir is a friendlier wrapper around Selenium that reads more like natural Ruby. Either way, a browser is heavier and easier to detect than a plain HTTP request.

Which Ruby gem should you use?

GemTypeRuns JS?Best for
NokogiriHTML/XML parserNoParsing — CSS and XPath, the standard
HTTPartyHTTP clientNoSimple, readable requests
FaradayHTTP clientNoMiddleware, advanced configuration
MechanizeHTTP + parse + sessionNoLogins, forms, cookies
Selenium / WatirBrowser automationYesJavaScript-rendered pages

For most jobs, HTTParty + Nokogiri is all you need; add Mechanize for sessions and Selenium only for JavaScript.

The hard part: handling anti-bot blocking

The gem you choose rarely decides success — anti-bot defenses do. HTTParty sends a TLS fingerprint and headers that Cloudflare, DataDome, and Akamai recognise as non-browser, and headless Selenium leaks automation signals. Nokogiri cannot parse a 403 or CAPTCHA page.

Handling modern anti-bot stacks means residential proxies and a real browser fingerprint. A scraping API handles all of that server-side, so your Ruby code posts the URL and parses the returned HTML with Nokogiri as usual:

Code example

ruby
require 'httparty'
require 'json'
require 'nokogiri'

resp = HTTParty.post(
  'https://api.your-scraping-provider.com/v1?key=YOUR_API_KEY',
  headers: { 'Content-Type' => 'application/json' },
  body: { cmd: 'request.get', url: 'https://example.com/protected' }.to_json
)

# Fully rendered, unblocked HTML -- parse it with Nokogiri as usual.
html = resp.parsed_response['solution']['response']
doc = Nokogiri::HTML(html)
puts doc.at_css('title')&.text

Next in Scraping in every language · 7 of 8

And with no language at all, just a command line.

Web Scraping With curl: A Complete 2026 Guide

Related terms

Concept map

Concept map

How Web Scraping With Ruby: A Complete 2026 Guide connects

The terms most directly tied to this one. Hover a node to see its neighbours, click to preview, drag to rearrange.

0 terms · 0 connections
You are here · Web Scraping by Language
Building map…

Frequently asked questions

What is the best gem for web scraping with Ruby?

Nokogiri is the standard HTML/XML parser — it supports both CSS selectors and XPath and is what almost every Ruby scraper uses. Pair it with HTTParty or Faraday to fetch pages, and use Mechanize when you need cookies, logins, or form submissions. For JavaScript-rendered pages, add Selenium or Watir.

Can Ruby scrape JavaScript-rendered pages?

Yes, but not with Nokogiri alone, which only parses static HTML. Drive a real browser with selenium-webdriver or Watir (a friendlier Selenium wrapper) for client-side-rendered content. Alternatively, find the JSON API the page calls in your browser’s Network tab and request it directly with HTTParty.

Does Nokogiri support XPath?

Yes. Nokogiri supports both CSS selectors (doc.css) and XPath (doc.xpath), so you can use whichever fits. CSS is shorter for most selections; XPath is more powerful when you need to select by text content or navigate to parent and sibling nodes.

Why does my Ruby scraper get blocked, and how do I fix it?

Set a realistic User-Agent, throttle your requests, and rotate residential proxies. Against serious anti-bot vendors you also need a browser-grade TLS fingerprint , which is hard to maintain from HTTParty. Many teams route hard targets through a scraping API that handles proxies, fingerprinting, and challenges server-side.

Last updated: 2026-06-08