RubyとRailsでクローラー開発をしてみた
はじめに
Railsで開発したアプリケーションにタスクとしてクローラーを開発する必要があったので、ほぼ初めてのRubyでクローラーを開発してみました。
今回は海外メディアサイトのRecodeの記事をクローリングしてきてGCPのCloud Translation APIで翻訳後、データベースに保存します。
クローラーはrails_project/lib/tasks/
にrecode_crawler.rb
というファイル名で作成しました。
プログラム
require 'open-uri'
require 'nokogiri'
require 'net/http'
require 'uri'
require 'json'
class Tasks::RecodeCrawler
# recodeのurlを定数として定義
TOP_URL = 'https://www.recode.net'.freeze
@@archives_url = 'https://www.recode.net/archives'
# 過去の記事(archives)のクローラー
def self.archives_crawling()
while true
doc = Nokogiri.HTML(open(@@archives_url))
article_url_list = []
doc.xpath('//h2[@class="c-entry-box--compact__title"]').each do |element|
element.css('a').each do |a|
if a[:href] != nil && a[:href].include?('recode')
article_url_list.push(a[:href])
end
end
end
article_url_list.each do |article_url|
articles = Nokogiri.HTML(open(article_url))
url_split = article_url.split('/')
date = url_split[3]+ '-' + url_split[4] + '-' + url_split[5]
title = articles.xpath('//h1[@class="c-page-title"]').inner_text
body = articles.xpath('//div[@class="c-entry-content"]').inner_text.strip
puts title + "\n"
img_url = !articles.xpath('//picture[@class="c-picture"]//img').empty? ?
articles.xpath('//picture[@class="c-picture"]//img').first[:srcset].split(',').first.gsub(' 320w', '') : nil
# クローラー実行して、すでにurl一致するレコードがない場合以下の処理を実行
unless RecodeArticle.exists?(url: article_url)
recode_article = RecodeArticle::create(
url: article_url,
date: date,
title: title,
body: body,
img_url: img_url,
)
parse_body = body.split("\n")
translate_body = ''
# 翻訳APIへpostするデータが大きすぎるとエラーになるので細かくパースしてpost
parse_body.each do |sentences|
parse_sentences = sentences.split(". ")
parse_sentences.each do |sentence|
translate_body += translate(sentence).gsub('"', '')
end
end
TranslateArticle::create(
recode_article_id: recode_article[:id],
url: article_url,
date: date,
title: translate(title),
body: translate_body,
img_url: img_url,
)
end
sleep(rand(2..4))
end
pagination_next = doc.css('.c-pagination__next')
if pagination_next != nil
pagination_next.each do |n|
@@archives_url = TOP_URL + n[:href]
end
else
break
end
end
end
# トップページの記事のクローラー
def self.crawling
doc = Nokogiri.HTML(open(TOP_URL))
article_url_list = []
doc.xpath('//h2[@class="c-entry-box__title"]').each do |element|
element.css('a').each do |a|
if a[:href] != nil && a[:href].include?('recode')
article_url_list.push(a[:href])
end
end
end
article_url_list.each do |article_url|
# 例外処理: ページが存在しなければ次のページ
begin
articles = Nokogiri.HTML(open(article_url))
rescue OpenURI::HTTPError
sleep(rand(2..4))
next
end
url_split = article_url.split('/')
date = url_split[3]+ '-' + url_split[4] + '-' + url_split[5]
title = articles.xpath('//h1[@class="c-page-title"]').inner_text
body = articles.xpath('//div[@class="c-entry-content"]//p').inner_text.strip
puts title + "\n"
img_url = !articles.xpath('//picture[@class="c-picture"]//img').empty? ?
articles.xpath('//picture[@class="c-picture"]//img').first[:srcset].split(',').first.gsub(' 320w', '') : nil
# クローラー実行して、すでにurl一致するレコードがない場合以下の処理を実行
unless RecodeArticle.exists?(url: article_url)
begin
ActiveRecord::Base.transaction do
recode_article = RecodeArticle::create(
url: article_url,
date: date,
title: title,
body: body,
img_url: img_url,
)
parse_body = body.split("\n")
translate_body = ''
# 翻訳APIへpostするデータが大きすぎるとエラーになるので細かくパースしてpost
parse_body.each do |sentences|
parse_sentences = sentences.split('. ')
parse_sentences.each do |sentence|
translate_body += translate(sentence).gsub('"', '')
end
end
TranslateArticle::create(
recode_article_id: recode_article[:id],
url: article_url,
date: date,
title: translate(title),
body: translate_body,
img_url: img_url,
)
end
rescue
next
end
end
sleep(rand(2..4))
end
end
# GCPのCloud Translation APIの翻訳用メソッド
def self.translate(q)
url = URI.parse('https://www.googleapis.com/language/translate/v2')
params = {
q: q,
target: 'ja',
source: 'en',
key: '****************************'
}
url.query = URI.encode_www_form(params)
res = Net::HTTP.get_response(url)
JSON.parse(res.body)['data']['translations'].first['translatedText']
end
end
実行方法
$ bin/rails runner Tasks::RecodeCrawler.crawling
$ bin/rails runner Tasks::RecodeCrawler.archives_crawling
でクローラーを実行します。
最後に
RubyもRailsも初めてなのでお作法だったりがよくわかってない状態でクローラーを開発しましたが、Ruby結構楽しい。
Thanks for reading!