메인 스크립트를 통해 크게 2가지 작업을 한다.
1. 각 플랫폼 데이터 수집 -> 데이터베이스 저장
2. 데이터베이스에서 데이터 취득 -> 구글 스프레드시트에 자동 입력
이번 포스트에선 1. 각 플랫폼 데이터 수집에 대해 설명해보고자 한다.
먼저, 각 플랫폼에서 데이터를 수집하는 방법은 크롤링 혹은 API를 통해서이다. 이렇게 수집한 데이터를 DB에 저장하며 1번 작업이 끝나게 된다. 아래와 같은 흐름으로 코드를 작성했다.
각 플랫폼 코드 객체화 및 초기화 -> 데이터 수집 -> 데이터 저장
1) 각 플랫폼 코드 객체화/초기화 및 데이터 수집
# main.rb
def retrieve_dtoc_data(date)
begin
puts "start retrieve #{date} d2c data"
sixpack = Sixpack.new(ENV['SIXPACK_ID'], ENV['SIXPACK_PW'])
project_root = File.dirname(File.absolute_path(__FILE__))
mcc_config_path = File.join(project_root, "googleads", "google_ads_config.rb")
gdn = Gdn.new(mcc_config_path)
ec = EcLifecreate.new(ENV['EC_ID'], ENV['EC_PW'])
(중략)
[
sixpack,
gdn,
ec,
(중략)
].each do |platform|
platform.request(date)
end
puts "end retrieve #{date} d2c data"
rescue => e
puts "Failed to update d2c report"
puts "#{e}: #{e.message}\n" + e.backtrace.join("\n")
end
end
위의 sixpack, gdn, ec 등이 바로 각 플랫폼 코드의 객체이다. 각 객체를 생성할 때 초기화를 하는데 이를 통해 ID와 Password를 인스턴스 변수에 담고, (필요하다면) Capybara 세팅까지 완료한다. 초기화된 플랫폼 객체는 인스턴스 메서드인 request를 갖고 있으며 이 메서드를 통해 데이터 수집 / DB 저장 등을 진행한다.
해당 플랫폼이 API를 제공하지 않는다면 크롤링을 통해 데이터를 수집해야 한다. 이때 필요한 것이 Capybara gem이다. Capybara는 테스트용 프레임워크이며 이를 통해 웹 크롤링이 가능하다. 초기화를 할 때 아래와 같이 Capybara의 환경설정도 진행한다. chrome headless로 드라이버 등록을 하고 필요한 옵션들을 적용 후 세션을 인스턴스 변수에 담는다.
# Sixpack.rb
class Sixpack
def initialize(id, password)
@id = id
@password = password
Capybara.register_driver :chrome_headless do |app|
options = ::Selenium::WebDriver::Chrome::Options.new
options.add_argument("--headless")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--remote-debugging-port=9111")
client = Selenium::WebDriver::Remote::Http::Default.new
client.read_timeout = 10000
Capybara::Selenium::Driver.new(app, browser: :chrome, options: options, http_client: client)
end
Capybara.run_server = false
Capybara.default_max_wait_time = 10
Capybara.javascript_driver = :chrome_headless
@session = Capybara::Session.new(:chrome_headless)
end
...
초기화를 통해 Capybara 세팅이 완료되면 크롤링을 진행한다. 각 플랫폼 사이트를 분석하여 효율적인 크롤링 코드를 짜는 게 중요하다. 각 플랫폼마다 코드가 다르므로 사이트에 맞게 크롤링 코드를 작성했다.
나의 경우 최대한 visit메서드를 통해 필요한 데이터가 있는 URL로 들어가 데이터를 추출하는 방법을 자주 사용했는데, 예를 들어보자면,
@session.visit "https://sixpack.work/manaver/partner_media?id=#{media_id}"
이렇게 원하는 페이지의 URL에 직접 방문하는 방식이었고, URL에 필요한 id나 date정보 등은 위와 같이 리터럴 템플릿을 사용해 삽입하였다.
위의 경우, media id를 취득하기 위해 a 태그의 href 부분을 사용했다. 내가 원하는 데이터가 한 페이지에 몰려있지 않을 가능성이 크기에 이렇게 a 태그를 뒤져보거나 하면서 페이지 이동을 최소화하려 노력했다.
REGEX = /\d+/
...
href_arr = tr.find(:xpath, "#{tr.path}/td[1]/a")['href'].split('/')
media_id = href_arr.select { |e| REGEX.match(e) }.first
a태그의 href는 "https://sixpack-c.work/.../media/11/dates" 이런 식이 었고 숫자 부분이 media id였다. 그렇기에 '/'로 split을 하여 숫자로만 이루어져 있는 데이터를 추출한다면 media id를 얻을 수 있었다.
일단 split을 진행하면 배열로 리턴이 된다. 이 배열, 여기선 href_arr의 데이터를 select 메서드를 통해 순회하며 조건에 맞는 값을 찾는다. regex를 통해 숫자로만 이루어진 데이터를 찾도록 했다.
REGEX.match(e) 로 조건에 맞는 값을 찾았을 때 boolean값을 리턴한다. true를 리턴하는 배열 요소를 media_id변수에 담았다.
2) 데이터 저장
이렇게 필요한 데이터 추출과정이 끝나면, 데이터베이스에 저장하는 작업을 진행한다.
# sixpack.rb
...
obj = {
creative_id: media_id,
creative_name: media_name,
imp: imp || 0,
click: click || 0,
conversion: cv || 0,
net: net || 0,
}
DailyReport.process(obj)
...
각 데이터를 hash의 형태로 만든 후 DailyReport모델의 process메서드에 매개변수로 넘긴다. process 메서드를 호출하기 위해 따로 DailyReport의 객체를 만드는 작업을 진행하지 않았는데, 이는 process 메서드가 인스턴스 메소드가 아닌 클래스 메서드라는 얘기다.
# daily_report.rb
class DailyReport < ApplicationRecord
scope :data_on_ids_date, ->(advertiser_id, order_id, schedule_id, creative_id, date) {
where(date: date, advertiser_id: advertiser_id, order_id: order_id, schedule_id: schedule_id, creative_id: creative_id)
}
...중략
def self.process(obj)
return unless obj[:date] && obj[:advertiser_id] && obj[:order_id] && obj[:schedule_id] && obj[:creative_id]
db_record = self.data_on_ids_date(obj[:advertiser_id], obj[:order_id], obj[:schedule_id], obj[:creative_id], obj[:date]).first
if db_record
db_record.update!(obj)
else
self.create!(obj)
end
end
end
이번 프로젝트에선 rails의 scope기능을 정말 많이 사용하였는데, 위의 경우도 마찬가지이다. scope란 자주 사용하는 query를 하나의 예약어로 묶어 사용할 수 있는 기능이다. 실제로, where(...) 이 긴 where 절을 매번 쓸 필요 없이 data_on_ids_date라는 scope 한 줄 만으로 쉽게 쿼리를 작성할 수 있다.
data_on_ids_date 스코프를 통해 데이터가 있는지 확인을 한다. 만약 존재한다면 update를, 존재하지 않는다면 새 값을 입력하도록 했다.
이렇게 데이터 수집부터 데이터베이스에 저장하는 과정까지 완료했다. 다음은 이 데이터를 가져와 스프레드시트에 입력하는 과정을 설명해보겠다.
'개발일지 > 광고데이터 연결 PJT - 차콜진센' 카테고리의 다른 글
20201228_시스템 구조 - 메인스크립트 구조 및 환경설정 (0) | 2021.01.09 |
---|---|
20201219_시스템 구조 - 데이터베이스 (0) | 2020.12.22 |
20201219_뒤죽박죽 엉망진창 -> 얼렁뚱땅 (1) | 2020.12.19 |
2020.12.10_프로젝트의 이해 (0) | 2020.12.12 |