paint-brush
How to Create a Stock Market Price Watcher Using Rubyby@Kazumaki

How to Create a Stock Market Price Watcher Using Ruby

by Vinicius Campos CarvalhoMay 22nd, 2020
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

For my first ever wrote article, I've decided to write about web scraping and how I built a Stock Market Watcher using Ruby and Nokogiri Gem.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - How to Create a Stock Market Price Watcher Using Ruby
Vinicius Campos Carvalho HackerNoon profile picture

For my first ever wrote article, I've decided to write about web scraping and how I built a Stock Market Watcher using Ruby and Nokogiri Gem.

First of all some requirements to follow this guide:

  1. Ruby
  2. HTML/CSS basics

The purpose of this guide is to show what you can do using Ruby, probably out there you'll find better/professional ways to do this.

The repository containing the complete code used on this guide will be linked at the end of the article.

Let's begin

First of all, set up your environment, create a ruby file that will contain our scrapper code.

In this case, I'll create a file called stock_watcher.rb and I'll add the following code to it.

#stock_watcher.rb
require 'nokogiri'
require 'open-uri'

This code requires a Ruby Gem called Nokogiri (Help us to handle extracted data from the website) and the open-uri module to extract data from the web.

After this, we need to define from where we're going to get the data for our scrapper, here you can use any website you wish, for this guide I'm going to use TradingView data.

With the source defined now we need to get the data from the page, Nokogiri got some useful parsing methods to help us, we're going to use the Nokogiri::HTML parser since we're scraping an HTML page.

Add the following code to stock_watcher.rb

#stock_watcher.rb
page = Nokogiri::HTML(URI.open('https://www.tradingview.com/markets/stocks-usa/market-movers-large-cap'))

This simple get the data from the web using the URI module and parse it using Nokogiri, it returns a Nokogiri::HTML::Document.

With our page object, we have in our hands a bunch of useful methods.

Now we have to use our HTML/CSS acknowledgment to define from where on the page we want to get the data. Chrome dev tools is a good friend here.

The page in question is https://www.tradingview.com/markets/stocks-usa/market-movers-large-cap/, this page shows us the US STOCK MARKET companies ordered by large capitalization. This page stores data on the main table as you can see, what we have to find is a way to identify this table.

Nokogiri offers us a method called "css" that lets us search for specific CSS rules on our Nokogiri::HTML::Document, we can search for specific CSS classes, ids, etc...

Let's try to find out what's the class or ID of the table using the Chrome inspect tool, just right click on any table element then inspect, you'll find a window similar to that one:

Scroll a bit till you find the <table> tag like this:


As you can see our table uses the "tv-data-table" class, that one is going to be our target class.

Now we got our table class, add the following code to stock_watcher.rb:

#stock_watcher.rb
table = page&.css('.tv-data-table');

This code get our page object and find an HTML element with "tv-data-table" class assigned to it and returns a Nokogiri::XML::NodeSet.

The only thing we need from the table is the table data, the table data (td) is stored inside the table rows (tr). Nokogiri::XML::NodeSet offers more useful methods that allow us to search for specific keys inside it.

Add the following code to stock_watcher.rb:

#stock_watcher.rb
rows = table.search('tr');

This returns another Nokogiri::XML:NodeSet with a subset of keys and values from the table that contains 'tr'.

We're getting close to our goal.

Now add the following code to stock_wacher.rb:

rows.each do |row|
  stock_content = row.search('td').map { |td| td.text.strip }
  stock_content[0]&.gsub!("\n\t\t\t\t\t\t\t\t\t", " ")
end

Now we iterate over our XML subset, in this case, it's our rows containing the data from stocks. Then we search in every row for the table data (td) using the same method search, it returns an array with all the data of the iterated row. We need to strip our text because Nokogiri returns a lot of useless pieces of text inside our strings.

Now you can play a bit with that code, try to print the stock content you got using "puts stock_content" before the end keyword of the last code and see what you got, it should return something similar to this: 

"["MSFT Microsoft Corp.", "182.51", "-2.27%", "-4.23", "Buy", "31.952M", "1384.054B", "31.09", "6.06", "144000.00", "Technology Services"]"

With this data in hand who define what should be done next is you, there are thousands of possibilities of what could be done with this, I'll give a few examples: Store data on a file for later checking, store data on a database to handle it later even showing the data on another website (Check for permissions first), create triggers if a stock value is on a price that you want. As I said, there are a lot of possibilities.

Now to finish this guide I've created a class to handle the data we got from our last piece of code.

First, create another file on the same folder called stock.rb and add the following code:

#stock.rb
class Stock
  attr_reader :name, :last, :change_percentage, :change_value, :rating, :vol, :market_cap
  def initialize(stock_data)
    @name = stock_data[0]
    @last = stock_data[1]
    @change_percentage = stock_data[2]
    @change_value = stock_data[3]
    @rating = stock_data[4]
    @vol = stock_data[5]
    @market_cap = stock_data[6]
  end
end

I'm not going to explain how classes work on Ruby since that is not the purpose of this guide. But basically, that class receive the data we got on the "stack_content" variable and store it elegantly.

Now replace your entire code on stock_watcher.rb file to look like this:

#stock_watcher.rb
require 'nokogiri'
require 'open-uri'
require_relative 'stock'
page = Nokogiri::HTML(URI.open('https://www.tradingview.com/markets/stocks-usa/market-movers-large-cap'))
table = page&.css('.tv-data-table');
rows = table.search('tr');
stock_arr = []
rows.each do |row|
  stock_content = row.search('td').map { |td| td.text.strip }
  stock_content[0]&.gsub!("\n\t\t\t\t\t\t\t\t\t", " ")
  stock_arr << Stock.new(stock_content) if stock_content.length.positive?
end

It requires our new class file, creates a new array called stock_arr, and store every new stock object on it.

If you print the first object on stock_arr you'll get something like this: "#<Stock:0x00007fffd443e2f8 @name="MSFT Microsoft Corp.", @last="182.51", @change_percentage="-2.27%", @change_value="-4.23", @rating="Buy", @vol="31.977M", @market_cap="1384.054B">"

As you can see, it's a better way to store our stocks, and you can access its properties using dot notation, like stock.name, stock.last, stock.change_value, etc...

Thank you for reading this guide, I hope it helps you in some way.

Feel free to contact me on any social media listed on HackerNoon.

Useful links:

Complete code repository: https://github.com/kazumaki/stock-market-watcher

TradingView website: https://www.tradingview.com/

Nokogiri documentation: https://nokogiri.org/rdoc/index.html