Criando um agregador de notícias (Com agendamento de eventos – Whenever)

whenever-01

Um agregador de notícias é um site que reúne notícias de vários outros sites, no nosso caso nós vamos “baixar” as noticias do Google News e exibí-las.
Baixar o Html de um site e parsear as informações é um processo um pouco demorado e fazer isso toda vez que um usuário quiser ver as notícias é um problema por aumentar o tempo de carregamento da página e por nos obrigar a fazer uma chamada ao Google News a cada requisição, então nós vamos usar uma Gem que agenda eventos de tempos em tempos para “Baixar” as noticias de N em N minutos ao invés de fazer isso a cada requisição.

Caso queira acessar o projeto completo no GitHub clique aqui.

O que vamos aprender
  1. Como baixar informações de outros sites
  2. Como e porque agendar eventos dentro do Rails
Objetivos
  1. Criar um sistema que exiba as notícias que estão no banco de dados para o usuário.
  2. Baixar com sucesso as notícias da primeira página do Google News e inserir no banco de dados.
  3. Agendar o “download” dessas notícias de N em N minutos para economizar requisições e acelerar a resposta da página.
Ingredientes
  1. Ruby 2.3.1
  2. Rails 5
  3. Sqlite3
  4. rest-client (Gem)
  5. nokogiri (Gem)
  6. whenever (Gem)
Mãos à Obra
Parte 1:  Criar um sistema que exiba as notícias que estão no banco de dados para o usuário.

1- Para criar o projeto rode:

1
rails new get_news

2- Coloque essas Gems no seu Gemfile

1
2
3
gem 'nokogiri'
gem 'rest-client'
gem 'whenever'

3- Instale as Gems:

1
bundle

4- Gere o model para guardar notícias, rodando:

1
rails g model news title:string description:text link:string time:string

5- Rode as migrations:

1
rake db:migrate

6- Gere um Controller com index:

1
rails g controller news index

7- Cole esse conteúdo no seu controller (NewsController) para que ele devolva as notícias:

1
2
3
def index
  @news = News.all
end

8- Cole esse conteúdo na view do index (/app/views/news/index.html.erb) para mostrar as notícias:

1
2
3
4
5
6
7
8
9
10
<% @news.each do |news| %>
<b><%= news.title %></b>
 
<%= news.description %>
 
<a href="<%= news.link %>" target="_blank">Ver Mais no Site</a>
 
Data de atualização: <%= news.created_at %>
 
<% end %>

9 – Para colocar o  método no root do projeto adicione ao routes:

1
root to: 'news#index'

10 – Vamos adicionar ao banco de dados um coluna de teste:

  • Primeiro rode no console:
    1
    
    rails c
  • Agora dentro do console, rode:
    1
    
    News.create title: 'Novo Curso', description: 'One Bit Code vai lançar um curso completo em breve :)', link: 'http://onebitcode.com', time: '28 minutos atrás'
  • Para finalizar, saia do console rodando:
    1
    
    exit

11- Vamos ver se funcionou até aqui, rode o projeto e vá até o browser em http://localhost:3000 para ver:

1
rails s

img-mershan-email-01

Parte 2:  Baixar com sucesso as notícias da primeira página do Google News e inserir no banco de dados.

Agora vamos baixar as noticias do site e inserir no banco de dados

12- Vamos criar um método no model news chamado refresh, no seu model, cole esse método:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def self.refresh
    url = 'https://news.google.com/'
    # Baixa o Html do Google News
    doc = Nokogiri::HTML(RestClient.get(url))
    # Parseia o Html e coloca em variáveis
    results = doc.search('.section-content', '.blended-wrapper').map do |link|
      result = {}
      # Pega o titulo
     result[:title] = link.search('div.esc-lead-article-title-wrapper > h2').text
      # Pega a descrição
      result[:description] = link.search('div.esc-lead-snippet-wrapper').text
      # Pega o link
      result[:link] = link.search('.esc-lead-article-title > a').attr('href').value
      # Data da notícia
      result[:time] = link.search('.al-attribution-timestamp').text
      result
    end
    # Limpa as notícias antigas
    News.all.delete_all
    # Cria as novas notícias
    results.each_with_index do |n, i|
      next if i == 0
      News.create n
    end
  end

13- Vamos chamar ele no nosso controller, substitua o conteúdo do seu NewsController por:

1
2
3
4
5
6
class NewsController < ApplicationController
  def index
    News.refresh
    @news = News.all
  end
end

14- Agora rode o servidor e acesse http://localhost:3000 no seu browser para ver uma página com notícias como esta:

rest client nokogiri

15- Nossa página apareceu, maravilha \o/

16- Você pode ter notado que a requisição demora um pouco, ainda mais se a sua internet for lenta. Agora vamos criar um evento para baixar as noticias de 1 em 1 minuto ao invés de carregar todas as vezes que alguém chamar. Dessa forma vamos economizar muitas chamadas e acelerar a resposta.

Parte 3:  Agendar o “download” dessas notícias de N em N minutos para economizar requisições e acelerar a resposta da página.

17- Para gerar o arquivo de eventos, rode no console:

1
wheneverize .

18- Agora no arquivo que foi gerado (/config/schedule.rb), cole o seguinte código:

1
2
3
4
5
6
7
set :output, 'log/whenever.log'
 
# Chamando de 1 em 1 minuto
every 1.minute do
 # Chamada do nosso método de refresh
 runner "News.refresh"
end

19- No NewsController comente a seguinte linha:

1
#News.refresh

20- Agora vamos limpar os dados já baixados, no console rode:

  • Primeiro, rode no console:
    1
    
    rails c
  • Depois  rode esse comando:
    1
    
    News.all.delete_all

21- Agora rode o projeto:

1
rails s

22- Você deve ver no browser que agora obtivemos uma resposta vazia porque o nosso controller parou de chamar o método refresh

23- Para ativar o seu arquivo que baixa as notícias de 1 em 1 minuto (ou n em n você que escolhe 🙂 ), rode no console:

1
whenever --update-crontab --set environment=development

24- Pronto, agora rode o projeto de novo:

1
rails s

25- Agora quando o Whenever rodar seu método ele vai automaticamente preencher o seu banco de dados com as novas notícias e você vai conseguir ver na tela.  Perceba que a resposta agora é quase instantânea e as noticias estão lá.

\o/ Parabéns você conseguiu criar um método que baixa notícias de tempos em tempos para devolver para seus clientes.

Extra: Mais opções do Whenever
O whenever possui três tipos de comandos:
  • runner: Esse é o método que usamos e ele roda comandos do rails e pode acessar os Models e etc
    Exemplo:

    1
    
      runner 'MyModel.some_process'
  • command: Usando o command você pode realizar qualquer comando do seu console:
    Exemplo:

    1
    
      command '/usr/bin/my_great_command'
  • rake: Este comando roda tarefas do rake, exatamente como você faz quando roda as migrations e etc:
    Exemplo:

    1
    
      rake 'my:rake:task'
O whenever permite vários tipos de agendamento, alguns exemplos são:
  • Roda uma vez por dia as 4:30 da manhã um comando expecifico do Rails:
    every 1.day, :at => '4:30 am' do
      runner "MyModel.task_to_run_at_four_thirty_in_the_morning"
    end
    
  • Roda no sábado a meia noite o comando do console:
    every :sunday, :at => '12pm' do
      command "echo hey"
    end
    
  • Ele também permite usar a notação de agendamento do CronTab:
    every '0 0 27-31 * *' do
      command "echo 'you can use raw cron syntax too'"
    end
    
Conclusão

Usando o Whenever você consegue realizar processamentos de tempos em tempos e evita reprocessar dados a cada nova requisição. Com ele você consegue fazer coisas bem interessantes como executar comandos no linux, comandos do Rails e tarefas do Rake.

Sempre que você for criar um novo aplicativo você deve imaginar a melhor maneira de organizá-lo para que os dados fluam de maneira fácil, sempre evitando realizar tarefas repetitivas que não agreguem valor como no nosso caso requisições ao Google News a cada nova requisição ao App.

Se você ficou com alguma dúvida, comenta aí em baixo ou me adiciona no Facebook clicando aqui.

Gostou do Post? Ajude o Blog compartilhando esse e outros Posts que já saíram por aqui com seus amigos, isso faz uma grande diferença. 🙂

Muito Obrigado,
Sua atenção é uma honra para mim.

Leonardo Scorza

Deixe seu Feedback!

Comentários