1 minute read

본격적인 사이드 프로젝트 시작에 앞서 몸풀기로 만들었다. 재미있는 주제가 안 떠올랐다. 조금 시간을 들여 뭘 만들지 고민하다가 만만한 검색 랭킹을 골랐다. 동작은 간단하다. 텔레그램 봇에 /start 명령을 입력하면 1분마다 네이버 검색 랭킹 알림이 온다. 그만 받아보려면 /end 명령을 입력하면 된다.

elixir 언어로 만들었다. 현업에서 쓰고 있고 계속 공부하는 언어라 다른 언어를 사용할 생각조차 안 했다. chrome driver를 사용해 chrome 브라우저가 렌더링하는 html에서 정보를 추출한다. 네이버 검색 랭킹 추출이 필요하다면 그냥 index.html 파일을 다운로드해서 추출하면 된다. 이후에 다이나믹 컨텐츠에서 추출할 걸 대비해 chrome driver를 사용했다. 배포는 heroku로 했다. 편해서 좋아라하는 서비스다.

elixir

defmodule SearchRankingTelegram.Telegram do
  defp process_message(state, message) do
    # ..
    case String.trim(text) do
      "/start" ->
        put_in(
          state.chat_id_list,
          Enum.uniq(state.chat_id_list ++ [chat_id])
        )

      "/end" ->
        put_in(
          state.chat_id_list,
          List.delete(state.chat_id_list, chat_id)
        )

      _ ->
        state
    end

telegram 모듈은 한번 만들어봐서 쉽게 짰다. /start, /end 명령으로 chat_id 관리를 한다.

defmodule SearchRankingTelegram.Telegram do
  def handle_cast({:broadcast, msg}, state) do
    state.chat_id_list
    |> Enum.each(fn chat_id ->
      Nadia.send_message(chat_id, msg)
    end)

    {:noreply, state}
  end

chat_id 값은 네이버 검색 랭킹을 가져와서 브로드캐스팅을 할 때, 사용한다.

defmodule SearchRankingTelegram.Scraper do
  defp scraping() do
    Logger.info("scraping")

    navigate_to("https://www.naver.com/")

    find_all_elements(:class, "ah_item")
    |> Enum.filter(fn e -> attribute_value(e, "data-order") end)
    |> Enum.map(fn e ->
      {find_within_element(e, :class, "ah_r") |> inner_text(),
       find_within_element(e, :class, "ah_k") |> inner_text()}
    end)

스크레이핑(scraping)hound 라이브러리를 사용했다. hound 세션은 elixir 프로세스당 하나만 만들 수 있다. 괜찮은 디자인 결정. 프로세스를 잘게 만들어서 디자인하는 elixir랑 잘 어울린다. 이런 디자인을 한 덕에 세션에 관련된 함수를 session id 없이 그냥 호출할 수 있어 편하다.

config :hound, driver: "chrome_driver", browser: "chrome_headless"

hound는 다양한 driver를 지원한다. 웹브라우저로 chrome을 주로 사용해서 chrome driver를 선택했다.

heroku

nil

heroku에서 앱을 만들고 cli를 설치한다.

$ heroku buildpacks:add "https://github.com/HashNuke/heroku-buildpack-elixir.git"
$ heroku buildpacks:add "https://github.com/heroku/heroku-buildpack-google-chrome.git"
$ heroku buildpacks:add "https://github.com/heroku/heroku-buildpack-chromedriver.git"

그 후 elixir, chrome, chrome driver 빌드팩을 설치한다.

$ cat elixir_buildpack.config
elixir_version=1.7.3

디폴트 elixir 버전이 낮아서 최신 버전으로 설정한다.

$ cat Procfile
worker: mix run --no-halt

$ heroku ps:scale web=0
$ heroku ps:scale worker=1

worker dyno로 실행한다. 디폴트 세팅은 web dyno 하나가 켜져 있기 때문에 web dyno를 끄고 worker dyno를 켜는 작업을 해야한다.

$ heroku config:set TELEGRAM_TOKEN=xxxxxxxxx:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
$ heroku config:set CHROME_DRIVER_PATH=/app/.chromedriver/bin/chromedriver

실행에 필요한 환경변수를 세팅한다.

$ git push heroku master

발사!

프로젝트 홈페이지 - ohyecloudy/search-ranking-telegram - github.com