python의 기본 기능으로 간단하게 짜본 6개의 랜덤숫자 코드

뭔가 될거라고 생각해서 만든건 아니지만... 잘맞는다면 행운의 코드라고 생각하자.

 

import random

if __name__ == '__main__':
    result = []

    for i in range(5):
        tmp_res = []
        while True:
            if len(tmp_res) == 6:
                break
            key = random.randint(1, 45)
            if key not in tmp_res:
                tmp_res.append(key)
        tmp_res.sort()
        result.append(tmp_res)

    print(result)

총 6개의 랜덤의 숫자를 5번 수행하도록 만든 코드

5번 수행시킨이유는 그래야 한장으로(?) 나오니까....

 

너무 짧은 코드이기에 설명은 생략!

 

반응형

JSON 파일에 있는 데이터를 Mapping 하여 가지고 오는 방법 중 하나로 ObjectMapper를 이용할 수 있다.

JSON 파일에 있는 Key 값을 java 클래스 파일로 미리 만들어 두고 ObjectMapper의 readValue 함수를 이용하면 쉽게 가지고 올 수 있다.

 

예를 들어 json 형식과 java class 형태를 보자.

{   
    "title": "test news",
    "reporter": "Austin",
     "since": "2017"
}

 

 

 

 

그런데, 여기서 JSON 파일이 다음과 같다면?

{   
    "title": "test news",
    "reporter": "Austin",
    "since": "2017",
    "keyword": "prob"
}

 

아마 이와 같은 에러가 날 것이다.

org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "keyword"......

 

이를 해결하기 위해서 java 클래스에 해당 어노테이션을 해주면 된다.

 

@JsonIgnoreProperties(ignoreUnknown = true)
@Data
public class NewsInfoVo {
    private String title;
    private String reporter;
    private String since;
}

 

import는 최신 jackson의 버전에 따라 다르다.

 

최신 버전이면

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

이전 버전이면

import org.codehaus.jackson.annotate.JsonIgnoreProperties;

 

이러면 문제 해결!

간단하지만, 생각이 안나는 경우가 있다 보니.. 체크!

반응형

'IT관련 > Java' 카테고리의 다른 글

Java 관련 공부 링크  (0) 2020.01.15

간만에 들어와서 명령어 하나 끄적거리고 가야지!


현재 디렉토리 경로 기준으로 하위 디렉토리 포함 경로 내 모든 텍스트 파일을 출력하는 명령어

find . -type f -name '*.txt' -exec cat {} +


하나의 파일로 합치는건 이것만 추가하면 됨


find . -type f -name '*.txt' -exec cat {} + > output.txt


이러면 output.txt 에 출력한 모든 텍스트가 저장되어 하나로 합쳐지게 된다.


그럼...

반응형

오랜만에 C++ 코드를 보다가 좋은 글인것 같아서 남겨둬야지

거의 새로운 언어를 공부하는 느낌이다.... 그래봤자 금방 익숙해지겠지만 ㅋㅋㅋ


=====================================================================================================


‘기차모델’로 갈아탄 ‘C++17’이 가져온 변화는?

잠깐, C++17? 벌써 C++ 17이 나왔다고?



C++17이 정식으로 승인됐습니다.

지난 9월6일, C++ 표준화 기구의 의장인 허브 서터가 자신의 블로그에 위 제목으로 C++17 표준안이 ISO의 최종 심의 절차에서 만장일치를 통해 승인됐다는 글을 올렸습니다.

이 소식을 듣고 아직 C++11에서 가져온 변화에도 적응하지 못했는데 벌써 C++17 표준이 나왔다는 사실에 놀라는 분들도 계실 것입니다. 한편 C++ 표준에 계속해서 관심을 갖고 계신 분들이라면 이제서야 C++17 표준이 마무리된 것인가 하고 의아해하는 반응을 보일 분도 계시리라 생각됩니다. 이러한 반응들은 모두 예상할 수 있습니다. 왜냐하면 놀랍게도 C++17 표준은 갑자기 완성된 것이 아니라, 오히려 그 반대인 지난 3년간 꾸준히 발전해 왔기 때문입니다. 2011년에 C++11 표준이 발표된 후, 2014년에 C++14 표준이 발표됐고, 계속해서 정기적인 C++ 커미티 미팅을 거치며 기능들이 제안되고, 토론을 통해 C++ 표준으로 수정·통합돼 왔습니다. C++17의 주요 기능 역시 이미 지난해 대부분 기술적으로 마무리된 것들이기도 합니다. C++ 표준화 기구에서는 도대체 지난 몇 년 간 어떤 일들이 벌어지고 있는 것일까요?

C++ 표준의 발전과 역사

C++은 1983년에 태어난 언어이므로, 30살이 훨씬 넘은 언어입니다. 그러나 첫 C++ 표준화는 1998년에 처음 이뤄졌습니다. 그래서 우리가 첫 번째 C++ 표준을 이야기할 때 그 기준은 1998년이라고 하고, C++98이라고 표현하기도 합니다. 98년도에 발표된 첫번째 C++ 표준은 7년간 21번의 미팅을 통해 완성됐습니다. 그리고 그 이후 5년간 C++ 표준화 위원회는 새로운 기능을 추가하기보다는 약간의 휴지기를 거치며 C++98 표준의 버그들을 수정한 03 표준(ISO/IEC 14882:2003)을 2003년도에 발표했습니다. 그리고 이어서 8년 간 21번의 미팅을 거쳐 첫 번째 표준에 크게 기능을 추가한 두 번째 메이저 표준인 C++11 표준을 2011년도에 발표했습니다. 특히 이 C++11은 C++98(03)과 비교해 다른 언어로 보일 만큼 많은 기능이 업데이트돼 C++ 개발자들에게 C++ 언어는 오래되고 정체된 언어가 아니라 계속해서 발전하는 언어라는 깊은 인상을 남겼습니다. 뒤이어 3년, 6번의 미팅을 거쳐 C++11의 마이너 업데이트인 C++14 표준을 만들어냈습니다.

C++ 표준화, ‘기차 모델’로 갈아타다

허브 서터 커미티 의장은 C++11 표준이 발표된 후 그 시점부터 C++ 표준화 위원회의 운영을 조금 다른 방식으로 가져가기로 합니다. C++11 표준이 완성될 때 까지 그 과정을 비교적 조용히 드러내지 않고 활동해 왔던 것과는 달리 그 시점부터는 다음 설명하는 방식들을 통해 활발하게 표준화 과정을 진행하기로 합니다. C++11 표준화를 진행할 때에는 ‘언제까지’라는 마감기한을 못박아두지 않은 채 최종 표준안을 발표할 수 있을 정도로 다 완성되면 내놓겠다고 하면서 말이죠. 또한 C++11에 포함될 ‘기능(Features)들’에만 집중했기 때문에 표준 출시가 계속 지연되는 문제(커미티가 막판에 디자인을 계속해서 바꿈)가 있었습니다. 그래서 C++03 표준안을 낸 후, 차기 표준안의 이름이었던 C++0x는 결국 200x년도가 아닌 2011년에야 C++11 이름을 달고 발표됐습니다. 그 과정에서 C++0x에서 x는 실은 10진수가 아닌 16진수 값이었다는 농담도 나왔습니다. 또한 이러한 진행 방식으로 인해 이 시기에서는 2004년에 가승인(draft)된 표준안도 공식 C++11에 포함돼 출시되기만을 마냥 기다려야만 했습니다.

C++11 표준화를 진행하며 얻은 경험으로 C++ 표준화 위원회는 ‘무엇’을 내놓을지도 중요하지만, ‘언제’ 내놓는 것 역시 매우 중요하다는 걸 깨달았습니다. 그래서 C++11 표준이 출시된 뒤부터는 방법을 달리 가져가기로 결정합니다. 3년마다 표준을 출시하기로 결정하고, ‘언제’를 더 중요하게 다루기 시작한 것입니다. 이것을 ‘기차 모델‘이라고 불렀습니다. 또한 우리가 개발을 진행하며 별도로 구분되는 새로운 기능들을 ‘브랜치’를 따서 작업하는 것처럼, 표준화 스펙에서도 ‘모듈, 파일 시스템, 네트워킹 표준 라이브러리’와 같이 개별 기술 묶음들을 TS(Technical Specification)라는 이름의 실험 기술(베타) 브랜치로 동시에 진행했습니다. 해당 TS 스펙이 충분히 표준화되기에 무르익으면 트렁크(C++ 표준)에 머지(merge)하는 전략을 가져갑니다. 그 과정에서 만약 표준화 기차가 도착했지만(3년마다 돌아오는 C++표준 발표 시기까지) 스펙이 충분하게 완성되지 않았다면 그 TS는 다음 표준 기차를 타게 되는 것입니다.

이러한 새로운 전략에 따라 3년이라는 시간이 지나 2014년 개정된 C++14 표준안을 내놓을 수 있었으며, 다시 3년 만에 정확히 돌아온 기차를 타고 2017년 C++17 표준을 내놓을 수 있게 된 것입니다. 마찬가지로 지난 2017년 7월 토론토 미팅을 통해 방금 C++20을 향하는 기차가 막 출발했고, 우리는 앞으로 3년 뒤 C++20이라는 기차가 도착할 것을 예상할 수 있게 된 것입니다.

C++ 위원회 미팅

현재 C++ 표준화 위원회는 일년에 세 번씩 미팅을 진행하고 있으며, 그 정보들은 C++ 표준화 위원회의 공식 사이트에서에서 확인할 수 있습니다. 최근에 있었던 미팅들과 주요 사건들을 나열해보자면 다음과 같습니다.

연도장소트립 리포트 주요 사건
2015년 5월렉사나, 미국[링크]표준 스펙 버그 수정
Transactional Memory TS, Parallelism TS 표준 발표
2015년 10월코나, 미국[링크1]
[링크2]
Concurrency TS 표준 발표
2016년 2월잭슨빌, 미국[링크]다음 TS들이 C++ 표준으로 합쳐짐
– Parallelism TS(Parallel STL)
– Library Fundamental 1 TS: any, optional, string_view
– File System TS
2016년 6월올루, 핀란드[링크1]
[링크2]
1. 다음 기능들이 표준 draft로 포함됨
– structured binding, if (init;condition)
– variant<>2.C++17 feature가 마무리되고 리뷰 기간에 들어감
2016년 11월이사퀘어, 미국[링크]ISO 리뷰 커멘트를 해결함
2017년 2월코나, 미국[링크]1. C++17이 기술적으로 마무리 됨
2. 마지막 ISO 승인(balloting)을 위해 보내짐
3. 버그 수정
2017년 7월토론토, 캐나다[링크]C++20을 위한 첫번째 미팅
2017년 11월엘버커키, 미국[링크]

C++17 표준이 가져온 변화들

C++17 표준안은 어떤 새 기능들을 포함했고, 어떤 변화를 가져오게 할까요? 새로 발표된 C++17의 표준 가운데 개인적으로 주목할만하다고 생각되는 몇 개의 내용을 살펴보겠습니다. 아래의 예제 코드들은 표준 문서, 블로그 또는 컨퍼런스 등을 통해 발표된 자료들로부터 가져왔으며, 출처는 함께 표시했습니다.

1. 언어(C++ 문법)에서의 변화

a. If and switch with initializer

if (init; condition) 또는 switch (init; condition) 구문처럼 if/switch 안에 조건문과 선언문을 함께 사용할 수 있습니다. 과거에 RAII를 활용한 lock_guard를 사용해야 할 때, 그 lock에 대한 필요성이 전체 scope에 대해서는 필요없는 경우라면 좌측 테이블처럼 {} 괄호를 더 사용해 인스턴스의 lifetime을 제한하기도 하였습니다. 그러나 이젠 if 문 안에 선언문을 넣을 수 있으므로 우측과 같이 사용 가능합니다.

b. Structured bindings

배열, tuple, pair가 주어졌을 때 그 안에 담긴 개별 값들에 대해 로컬 변수 선언과 값 저장을 한 번에 할 수 있도록 합니다.

a와 b의 변경점을 모아서 함께 활용해 본다면, 다음과 같은 코드도 이제 C++17에서는 사용가능합니다. std::map의 insert는 삽입된 iterator와 성공여부를 가리키는 pair를 반환하므로, insert() 를 호출하고 그 성공 여부를 체크하는 동작을 if 구문 한 줄에서 아래처럼 작성이 가능합니다.

c. 기타 중요한 변경 사항들

– Template argument deduction for class templates [링크]
– constexpr if [링크]
– fold expression [링크]

2. 표준 라이브러리에서 변경된 점

a. std::string_view [링크]

std::string_view는 기존 std::string을 사용할 때 불필요하게 일어날 수 있었던 임시 객체 생성을 막을 수 있도록 고안된 클래스입니다. 내부적으로 문자열에 대한 pointer와 length만 가지므로 복사가 쉽고, 메모리를 할당하지 않습니다. std::string 인터페이스와 거의 동일하므로 기존 std::string을 사용하던 코드에서 std::string을 std::string_view로 변경하는 것만으로 쉽고 간단하게 성능상의 이득을 얻을 수 있을 것으로 예상됩니다. 다음 코드는 동일한 역할을 하는 코드지만, C++17 코드에서는 문자열의 복사가 일어나지 않습니다.

▲출처: https://youtu.be/LvwXJjRQfHk

b. std::variant [링크]

boost 라이브러리에 존재하던 variant 타입이 C++ 표준 라이브러리에 머지됐습니다.

▲출처: https://herbsutter.com/2016/06/30/trip-report-summer-iso-c-standards-meeting-oulu/


추가로 눈여겨 볼만한 C++17 새 라이브러리에는 std::optional<>, std::any<>가 있습니다.

C++의 다음 기차, C++20

다음 표준이 될 C++20에는 C++0x 시절부터 논의돼 온 Concept 기능이 드디어 포함될 예정입니다. 특히 코루틴, 네트워킹, 모듈 등이 모두 표준에 포함될 것으로 보여 C++20 역시 큰 변화를 맞이할 것으로 예상됩니다. 마지막으로 현재 각 컴파일러 벤더 별 C++17 지원사항은 다음 페이지에서 확인 가능합니다.

컴파일러 지원 상황

– Clang: https://clang.llvm.org/cxx_status.html
– GCC: https://gcc.gnu.org/projects/cxx-status.html
– MSVC: https://blogs.msdn.microsoft.com/vcblog/ (비주얼 스튜디오 업데이트를 통해 지원이 추가될 때마다 새 글을 통해 공유합니다)

=====================================================================================================

출처 : http://www.bloter.net/archives/291766

반응형

데이터 사이언스 관련 페이지에서 가지고 온 글이지만, 개발자 입장에서 RTB 에 대한 설명이 잘 되어 있어서 글을 남겨 본다. 이외에 대한 설명은 그냥... 그러려니 하면서 보고 넘어가면 될 듯하다.

=========================================================================

“Change is automatic. Progress is NOT!” -Tony Robbins-

필자도 광고 시장 짬밥이 많지 않은 관계로 “고대”에는 어떻게 온라인 광고가 운영되었는지 잘 모른다. 그저 듣기로는 광고 지면 판매를 담당하는 직원이 "저희 지면에 광고 좀 실어주십시오”라며 세일즈를 하러 다닌 적도 있었다고 하고, 또 잘 팔리는 광고 지면은 서로 자기네 광고 싣겠다고 비싼 가격을 주고 매월 초에 줄을 서서 구매했다고 한다. 웹을 돌아다니면 포털에, 블로그에, 동영상에 (덕지덕지) 붙은 광고들은 다들 그렇게 팔렸(댄)다.

아직도 우리나라에서는 이런 인간적인(Read “구식”) 방식으로 지면이 사고 팔리는 경우가 많지만, 해외에서는 많은 지면들이 자동화되어서 거래되고 있다. 그 자동화 거래 시스템을 Real-Time Bidding, RTB 시스템이라고 부른다.

광고를 팔려는 측(“매체”)에서 쿠키 ID (유저의 접속 기록)를 보내주면 광고를 사려는 측(“광고주”)에서 그 ID에 맞는 가격(Bidding CPM)을 제시하는데, 여러 광고주가 동시에 가격을 제시하고, 그 중에서 최고가를 써낸 광고주에게 광고 지면을 판매하는 “경매” 방식으로 지면이 거래가 된다. 광고주는 내가 원하는 ID에 맞춰서 광고가 나가서 좋고, 매체 측에서는 비싸게 팔아서 좋다. 기존에는 어느 지면에 특정 연령, 성별의 사람들이 많이 들어오고 이들이 구매력이 높은 유저들이므로 1,000번 노출(CPM)에 500원, 1,000원 같은 방식으로 지면을 팔았지만, RTB로 지면을 거래하면 각 유저별로 경매를 붙일 수 있기 때문에 구매자가 많으면 고가로 지면을 판매할 수 있다.

웹페이지가 광고 지면을 팔 때 “우리 웹 사이트는 도시 지역 30대 여성이 많이 들어와서 XXX, YYY, ZZZ 계열의 상품 광고에 용이하고….”로 접근할 때는 그 웹 사이트에 20대 남성이 들어가도 같은 광고를 봐야했다. 그러나 쿠키 ID를 보고 광고주 입장에서 딱 필요한 사람에게만 높은 가격을 지불하고, 나머지 유저에게는 가격을 제시하지 않으면 광고주 입장에서는 “개이득” 아닌가? 매체 입장에서는 지면 팔리는 가격이 고만고만했는데, 가격 경쟁이 붙어서 높은 가격으로 지면을 파니 또한 “개이득”이다. 거기다 모든 거래를 컴퓨터가 알아서 진행하니 인건비를 아껴서 더 좋다.

이 글은 광고 시장 글이 아니라 Data Science에 관한 글이니까 RTB 광고에 더 궁금하신 분은 다른 글을 찾아보실 것을 추천하고, 필자는 RTB가 돌아가는 매커니즘과 기술을 소개해 드리고 싶다.

1. RTB 매커니즘

자동화라고하니 뭔가 대단해보이고 어려워보이겠지만, 사실 바탕에 있는 기술의 로직은 간단하다. 자동으로 AD ID를 송출하고, 거기에 맞춰서 비딩하고, 노출 여부를 결정하면 된다. 우선 오픈소스 RTB 플랫폼인 iab.com의 API 문서를 간단하게 살펴보자.

(Source: iab.com/OpenRTB)

일단 유저가 웹 (앱) 페이지에 들어오면 쿠키 ID (모바일에는 AD ID)를 확인할 수 있고, 이를 바탕으로 Ad request를 보낸다. 윗 그림 제일 왼쪽 상단에 보면 0. Ad Request가 있다. 그리고 1. Bid Request를 다수의 광고주에게 송출한다. 이때 쿠키 ID 뿐만 아니라 웹 사이트, 기기 종류, 장소, IP, 사용 언어, 브라우저, 쿠키 생성 시간, 노출 지면, 노출 지면의 사이즈 등등의 정보가 동시에 송출된다. 자세한 내용은 구글 RTB의 Ad Request 페이지에서 Example bid request를 참조하시면 좋을 것 같다.

지면을 구매하는 비더 (광고주, DSP 등)는 Bid Request에 있는 정보를 바탕으로 자신의 경매 입찰가를 제시한다. 이때 가격뿐만 아니라, 어떤 종류의 광고를 노출할지에 대한 정보도 함께 보낸다.

RTB 매체는 그 정보들을 바탕으로 낙찰여부와 낙찰가를 알려주면서 비딩이 종료된다. (비딩가와 낙찰가가 왜 다른지는 다음 섹션에 설명하겠다.)

지면을 낙찰받으면 자사 광고를 노출할 수 있는데, 이 모든 과정을 최대 100ms 안에 해결해야한다. (실제로는 더 빨리 진행된다. 그래서 한국에 있는 서버로 미국에 있는 RTB 지면을 구매하기는 ‘거의’ 불가능하다.)

아래 Bid Request object model을 보면 Bid Request에 어떤 정보가 들어오는지 Imp (Impression)에 어떤 정보가 들어가는지 좀 더 쉽게 파악할 수 있다. 먼저 DistributionChannel로 집적되는 정보를 보면 Publisher(매체)와 Content(광고내용), 그리고 Site, App (웹 or 앱) 정보가 들어간다. 각각 0 or 1의 2진법 정보로. 예를들어 매체 리스트가 총 100개 있으면 하나만 1이고 나머지는 0으로 들어갈 것이다. 이렇게 DistributionChannel에서 재정리된 정보가 BidRequest로 송출된다. 같은 방식으로 Device, User 등의 정보가 BidRequest에 들어가는 것을 확인할 수 있다.

(Source: iab.com/OpenRTB)

이렇게 RTB Exchange에서 정보를 송출하고 나면 Imp (Impression, 광고 노출) 탭에는 배너 노출인지, 음성 노출인지, 비디오 노출인지, Native Ad인지와 더불어 어떻게 구매가 이뤄졌는지 가격은 얼마고 화폐단위는 뭐였는지 등이 기록이 된다. Imp에서 BidRequest로 돌아가는 정보가 1 일 경우에만 체크되어 있는 것은 당연히 노출이 안 되면 Impression 정보가 없을 것이기 때문이다.

더 자세한 내용은 OpenRTB 의 API를 참조해주시면 되겠다.

Native Ad

요즘 한창 뜨고 있고 있고 저 위의 Bid Request object model에도 잠깐 언급된 Native Ad를 살펴보자. 예전에는 광고 지면이 어떤 내용인지 고려없이 유저의 특성만 보고 광고가 나갔다. 그래서 극단적인 경우에는 테러리스트들이 일으킨 참혹한 피해를 보여주는 동영상에 정작 테러리스트 모집 광고가 나가는 사건이 일어나기도 했다. (관련 기사)

덕분에 최근들어 광고 지면의 내용에 광고 내용을 맞추려는 시도들이 여럿 있다. URX 라는 실리콘 밸리 스타트업은 신문 기사에 있는 단어들을 읽어서 (Crawling 이라고 한다) 그 단어에 매칭되는 광고를 보여주는 기술을 제시했다. 기술 설명을 한 페이지를 보면 어느 도시에 대한 기사를 읽는 사람에게 그 도시의 숙박권 할인 쿠폰이나 여행권 광고가 나가도록 만들어놨는데, 광고 자동화가 어디까지 진화하는지 단적으로 보여주는 사례가 아닐까 싶다.

좀 더 단순한 시도로는 OpenRTB와 구글 RTB 등에서 시도하고 있는 광고지면 Feed 설정이다. 어느 지면이 어떤 카테고리인지 지정을 매우 세부적으로하고 나면, 그 페이지에 어떤 광고를 내보내야하는지 좀 더 세분화할 수 있다. 리타게팅과 RTB가 들어오기 전에 유저의 관심사를 세분화해서 유저별로 지면을 팔던 방법의 2017년 버젼이라고 보시면 얼추 맞을 듯 하다.

Feed 설정방식도 세분화되어서 Content feeds, social feeds, product feeds 등등 내용이 구체화되고 있는데 리타게팅 광고가 유저가 봤던 상품과 유사한 상품을 보여주는 것이라면, Native Ad는 유저가 지금 보고 있는 페이지 (예. 블로그, 신문 기사)에 나오는 상품과 매우 적합도가 유사한 상품을 보여주는 방식으로 이해하면 된다. 이런걸 보면 광고 자동화를 이용하는 인간의 아이디어는 참 끝이 없다는 생각이 든다.

좀 더 자세한 내용은 OpenRTB의 API 문서를 참조해주셨으면 한다.

2. 2차 가격 경매 (2nd price auction or Vickrey auction)

사실 R 코드 하나도 안 들어가는 이 글을 쓰고 싶었던 건 아니지만, “고대”부터 짬밥이 쌓이신 어느 광고회사 매체 세일즈 시니어 분이 2nd price auction을 이해 못 하시는 걸 보고 이 글을 쓰기로 맘을 고쳐 먹었다. 내용이 좀 어려울 줄은 알지만 간단하게 정리해보자.

우선 RTB는 우리가 알고 있는 경매 방식대로 돌아가지 않는다. 제일 비싼 가격을 제시한 사람이 자기가 제시한 가격대로 상품을 사가는 경매가 1st price auction이고, 2등이 제시했던 가격으로 상품을 사가는 경우가 2nd price auction이다. 2등 가격으로 팔면 파는 사람이 손해 아닌가? 먼저 결론만 말하면 “아니다”. 1등 가격으로 팔면 서로 눈치보기를 할 유인동기가 있지만, 2등 가격으로 팔면 남들 눈치 안 보고 자기가 생각하는 상품의 적정 가격을 써내게 된다. 처음 들으면 믿을 수 없을 것이다. (필자가 공부할 때 당황했던 기억이 아직도 생생하다ㅋ)

(Link)

자, 상품 1이 있고 구매자 A와 B가 있다고 생각해보자. 둘은 상품의 적정 가격을 100원, 99원으로 생각하고 있지만 서로가 얼마로 생각하는지는 모른다. (모른다는 가정이 제일 중요하다.) B가 자기가 생각하는 가격 99원으로 비딩에 참여한다고 해 보자. 이때 A가 어떤 전략을 취할 수 있을까?

100원 이상 가격으로 비딩: 그럴 필요가 없다. 어차피 100원 이상의 어떤 가격을 쓰건 경매에서 이기고 99원을 내고 상품을 사온다.

99원이상으로 비딩: 위와 같다. 비딩에서 이기고 99원을 낸다.

99원 밑으로 비딩: 경매에서 진다.

결론은? 99원 이상으로 비딩하면 되는데, 문제는 B가 99원으로 비딩할지 모른다. B가 생각하는 적정가격을 모르니까. 결국 100원 아래에 있는 “미지의 영역”을 찾아다니느니 100원으로 비딩하는게 제일 합리적이다. 잘못하다 경매에서 지면 국물도 없고, 이기더라도 자기가 비딩한 가격 아래로 지불하고 상품1을 구매하니까.

A가 100원으로 비딩한다면, B의 경우엔 어떨까?

99원 아래로 비딩: 경매에서 진다

100원 이상으로 비딩: 경매에서 이긴다. 그러나 100원을 내고 99원짜리 상품을 사야한다. 말을 바꾸면 1원을 손해본다.

B 입장에서는 A가 99원 아래로 비딩하지 않으면 절대로 이 상품을 구매할 수 없다. 그렇다고 100원 이상으로 비딩하면 이겨도 손해를 본다. 자기가 생각하는 가격보다 더 높은 가격을 제시하면, 이기면 손해를 보고, 지면 아무런 이득이 없다. 그래서 결국엔 자기가 생각하는 가격으로 비딩하는게 최적 전략이다.

위의 사례에서 사회적으로 최적 배분은 뭘까? 상품1의 가치를 100원으로 생각하는 사람이 이 상품을 사가고 판매자는 100원을 받는 것이다. 2nd price auction을 하면 구매자는 최적 배분이 되지만 판매자는 99원을 받으니 1원만큼 덜 이득을 본다.

이렇게 1원을 “손해"보는게 A와 B가 매일 눈치보고 (심지어는 담합하는) 1st price auction을 하는 것보다는 훨씬 낫지 않은가? (물론 B가 90원을 비딩하면 10원을 “손해”보는거지만, 여러 사람이 비딩에 참여하면 그 차이값이 줄어들 것이다.)

아무도 경매에 참여하지 않으면 0원에 팔 수는 없으니 최저 가격 (Floor price)를 정해놓기는 한다. (Myerson auction이라고 부른다.)

Bidding CPM optimizer

RTB에서 광고 지면을 구매할 때는 위에 보여드린 것처럼 남들 눈치 안 보고 재량껏 내가 생각하는 지면 가치를 써 내면 된다는 결론이 나왔다. 이제 문제는 내가 생각하는 지면 가치를 어떻게 합리적으로 계산하느냐이다.

위에서 언급했듯이, 구글을 비롯한 대부분의 RTB 판매자들은 유저를 식별할 수 있는 쿠키 ID (or 스마트폰 AD ID)와 IP, 지역, 시간대, 언어 설정, 쿠키 나이, 웹 브라우저, 웹사이트 주소, 노출 크기, 노출 위치 등등의 기본적인 정보를 구매자들에게 제공한다. (구글 RTB 예시 참조) 예를들면, 캘리포니아 지역의 스페인어 사용자를 대상으로만 하는 광고도 송출가능한 것이다. 이런 경우에는 특정 조건을 만족하는 유저에게 x원, 나머지 유저에게 0원 같은 단순한 방식으로 Bidding CPM을 정한다.

요즘 RTB를 활용해서 한창 유행인 리타게팅 광고 상품은 어떻게 작동될까? 일단 쿠키 ID를 받아 내부 DB에 있는 유저 정보와 매칭을 한다. 그리고 그 유저가 이전에 광고주 사이트에서 많은 활동을 했던 유저고, 특정 상품에 관심이 있을 것 같다는 계산이 나오면(글 3번. 최적화 참조) 이 유저의 기대 가치를 계산할 수 있다. (참고로 “고대"에 통신사 가입자들에게 같은 방식의 계산을 하는 모델을 Life-Time Value 모델이라고 했다.)

3. Hybrid RTBs

기존의 RTB는 모든 비딩 참여자에게 동등한 권리를 부여하고 높은 가격을 제시하면 누구나 그 지면을 구매할 수 있었다. 그러나 2차 가격 경매로 지면을 판매하는 매체들 입장에서는 당연히 지면을 더 비싸게 팔고 싶을 것이다. 그래서 지난 몇 년간 다양한 방식의 변형 모델들이 등장했다. 그 중 가장 유명한 2가지만 소개해본다.

Header bidding

비딩 참여자들의 등급을 나누면 어떨까? 우선권을 줄테니 얼마간의 프리미엄을 내던가, 아니면 프리미엄 클래스들이 구매하지 않은 지면에 대해서만 비딩에 참여하라고 하면 어떻게 될까? 경제학에서 말하는 2급 가격 차별(2nd Degree Price Discrimination)이다. 우리 일상생활에서는 비행기, 기차 티켓에서 흔히 볼 수 있다. 좋은 유저들이 많은 지면을 구매하기 위해서 줄까지 섰던 “고대”의 광고 시장을 생각하면 프리미엄 클래스가 되기 위해서 선뜻 비용을 지불하는 광고주들이 생기지 않을까? 실제로 구글, 페이스북, AppNexus 등의 광고시장 선두 주자들이 모두 이 방식을 빠르게 도입하고 있다.

Vickrey-Clarke-Groves (VCG) auction

페이스북에서 Generalized second price auction라고 부르는 지면 판매 방식이다. 관련 기사 관련 논문 쉽게 이야기하면 지면을 낱개로 파는 방식이 아니라, 여러 지면을 동시에 경매에 올려서 2차 가격으로 판매하는 방식이다. 문제는 지불 가격을 “사회에 주는 피해액 (Negative Externalities)”만큼으로 정하는 독특한 경매방식인데, 실제 디테일이 좀 복잡하니 아래에 간단한 예시를 들어보겠다.

광고 지면 3개에 광고를 조합해서 내는 방식을 총 4가지 방식 중 하나로 고를 수 있다고 해 보자. 이 때 사회적 효용이 가장 큰 조합은 그룹 4번이다. 이 때 가장 큰 이득을 보는 유저는 A와 B인데, 둘에게 각각 16, 12를 지불하라고하면 그 다음번 경매에는 얼마를 써 낼까? 아마 그룹 3이 선택되지 않도록 그룹3에 0을 쓰고 그룹4에도 낮은 숫자를 써 낼 것이다. 그래야 자기들이 가장 큰 이득을 보는 그룹 4를 저가에 구매할 수 있기 때문이다. 합리적인 경매 방식은 유저들이 자신의 진짜 가치를 써 넣도록 해야한다.

그러면 유저 A가 얼마를 내게할지를 정할 때, 유저 A의 존재를 지우고 다시 생각해보면 어떨까?

만약에 유저 A가 (6,14,2,16)을 써내지 않았다면 사회적 효용이 35인 그룹 3이 선택되었을 것이다. 유저 A를 제외하면, 그룹3이 선택되어야했을 걸 그룹4가 선택되면서 사회적으로는 35 - 26 = 9만큼의 손실을 본다. 유저 A에게 사회적 손실분인 9만큼 지불하라고 제시하면 다음번 경매에 거짓말을 굳이 해야할까? 어차피 남들이 생각하는 가치에 의해서 내가 내는 금액이 결정되고, 그룹4에 큰 숫자를 쓰지 않으면 그룹4가 선택 안 될수도 있으니 솔직하게 쓰는게 낫다. 같은 맥락에서 유저 B의 경우도 보자.

유저 A보다 차이값은 적지만 어쨌건 유저 B도 그룹 3대신 그룹 4가 선택되도록 만들어서 33 - 30 = 3만큼의 사회적 비용을 만들어냈다. 다른 유저들은 그룹4가 선택되면서 손해를 봤고, 유저 F는 금액이 너무 작아서 사회적 효용에 영향을 못 미친다. 이 경우 매체는 유저 A와 B에게만 9원, 3원을 지불하라고 할 수 있을 것이다.

물론 이렇게 적은 금액을 받으면서 VCG 경매를 계속할 수는 없다. 실제로 페이스북이 VCG를 이용하는 방식은 위의 예시보다 훨씬 더 복잡하다.

(위의 eCPM은 effective CPM의 약자로 CPM에 클릭 확률을 곱한 것이다.)

먼저 각 유저별로 광고 지면에 따른 자신의 Bid를 보낸다. 페이스북은 광고 5개로 만들어 낼 수 있는 모든 조합을 고려해서 상단의 예시처럼 그룹 1,2,3,4,…를 만들어낸다. 그 중 조합의 가치가 최대인 경우를 골라 위의 방식으로 지불 가격을 계산한다. 1등 지면을 얻은 광고주는 1등이 없었을 경우, 2등과 3등이 만들어냈을 사회적 가치만큼을 지불하는 것이다. 위에서 광고 A에 대한 지불 가격은 2등 가격 (10)과 3등 가격 (5)에 2등이 1등에 노출되었으면 얻었을 eCPM 차이값 (1.00 - 0.80)과 3등이 2등 지면에 노출되었으면 얻었을 eCPM 차이값(0.80 - 0.70)을 곱해서 계산되었다. 정리하면, 광고 A의 구매 비용은 광고 B와 광고 C의 비딩 가격에 의해서 결정된다. 광고B와 광고 C의 가격도 그 아래에 뜨는 광고의 가격에 의해서 결정되는 구조다.

금액을 다 합해보면 알겠지만, VCG 방식 RTB는 일반 RTB에 비해 단기적으로는 이득을 볼 수 없는 구조다. 그러나 "사회적인 피해액 (Negative externalities)”만큼을 지불하는 방식은 리타게팅 광고처럼 특정 광고 지면에 “몰아주기”를 통해서 수익을 창출하는 사업에 장애물이 될 수 있다. 그룹으로 묶인 모든 지면에 대해서 솔직한 가치를 써내는 편이 광고 지면을 살 수 있는 확률을 높이기 때문이다.

광고 자동화의 명암

광고주 쪽 영업을 열심히 하는 친구를 만나 들었던 이야기다. RTB와 리타게팅이 시장에 들어오면서 예전처럼 돈 2배 태우면 광고 물량 2배된다는 이야기를 하기가 어려워서 너무 힘들단다. 예전에는 CPM으로 계산하면 노출되는 지면 숫자가 정해지고, 클릭율이 어느정도 예상되면 CPM x CTR이 클릭숫자니까 그에 맞춰서 광고 예산을 설정하면 됐었는데, RTB가 들어오면서 CPM이 유저마다 다르게 적용되는 것도 당황스럽고, 리타게팅이 클릭율도 예상 안 되고 CPC도 일률적으로 적용되지 않는 것 같아서 광고주 쪽 영업가면 설명을 못하겠단다.

예의상 차마 못했던 말인데, 여기에만 조심스레 털어놓는다. 걸어다닐때는 1km에 15분정도 걸리니까 거리에 따라서 언제 도착할지 예상할 수 있는데, 자동차가 들어오고 난 다음부터 길이 막히고 신호에 걸려서 언제 도착할지 도무지 예측을 못하겠다고 불평하는 것 같더라. 아니면 필자가 광고 시장 경험이 일천하다보니 3.5인치 플로피 디스크가 20년전에는 널리 쓰이는 저장장치였음을 모르는 요즘의 “급식”같은 마인드여서 그런걸까?

이런 분들을 위해서 다음 글은 광고 데이터를 시뮬레이션하는 법을 소개해볼까 한다. 수학과 통계학이 좀 들어가지만, 요즘처럼 빅 데이터가 많고 컴퓨팅 파워가 발달한 시대에 모델만 한번 잘 짜놓으면 별다른 수고없이 쉽게 계산해낼 수 있을 것이다.

  1. 빅 데이터, 머신러닝, 데이터 사이언스에 대한 오해
  2. 데이터 시각화(Data visualization)
  3. 최적화 - CTR Optimizer
  4. 멀티 터치 어트리뷰션(Multi-Touch Attribution Model)
  5. 크로스 디바이스 매칭(Cross-Device Matching)
  6. RTB란?
  7. 시뮬레이션(Simulation)
  8. 머신러닝 1주차: Regression problems
  9. 머신러닝 2주차: Classification problems
  10. 사기 설치와 클릭(Fraud Installs & Clicks)


출처 : http://blog.ab180.co/data-science-with-r-6-rtb/

반응형

'광고관련' 카테고리의 다른 글

VAST 란??  (1) 2018.01.03
모바일 및 온라인 광고 관련 용어...  (0) 2017.12.20

VAST (Video Ad Serving Template)란?

VAST는 비디오 플레이어에 광고를 제공하는 광고 태그들을 구성하는데 필요한 비디오 광고 표준규격을 의미합니다. XML 스키마를 사용하는 VAST는 광고 서버로부터 광고에 대한 주요 메타데이터를 비디오 플레이어로 전달합니다. 더불어 IAB의 VPAID(Video Player-Ad Interface Definition)는 비디오 광고와 비디오 플레이어가 서로 통신할 수 있도록하는 표준이며, 이 VPAID를 통해 VAST는 프로그래매틱 광고 혹은 인터랙티브 광고를 전달합니다.

비디오 광고 표준 (VAST) 1.0, VPAID와 같이 잘 만들어진 기술 규정과 프로토콜은  비디오 산업 성장에 중요한 역할을 해왔습니다. 그간 온라인 광고 업계의 표준은 많은 성장을 거두었고 새로운 트렌드와 기술 지원에 있어서도 크게 발전했습니다. 특히 VAST 4.0은 비디오 광고를 개선하고 최종 소비자에게 좀 더 향상된 경험을 제공하기 위해 업계의 모든 구성원들이 합심해 이루어 낸 결실이라 말할 수 있습니다.

브라이트코브와 비디오 광고 플랫폼 스팟엑스(SpotX)가 함께 한 VAST 4.0 표준 테스트 결과 개선된 비디오 광고 기술들을 확인할 수 있었습니다. VAST 4.0의 새로운 기능들은 최종 소비자의 광고 시청 경험에 긍정적인 영향을 미칠 뿐 아니라 광고 렌더링 과정에서 불 필요한 기능들을 제거하고 페이지 로딩 시간 또한 줄여줍니다.

VAST 4.0의 새로운 기능들을 아래에서 확인해 보십시오.



  • 조건부 광고 (Conditional ad)
  • 메자닌(mezzanine) 파일 지원 (일명 서버 사이드 스티칭)
  • 인터랙티브 요소 파일과 비디오 파일의 분리
  • 광고 인증 및 가시성 제공

조건부 광고(Conditional Ads)

이용자의 검색경로, 검색어 등의 빅데이터를 분석해 이용자가 가장 필요로 하는 광고를 띄워주는 프로그래머틱 광고에서 VPAID는 때때로 광고를 어디에 배치할 것인지를 결정하는데 사용됩니다. 만약 이런 "Conditional ad"가 어떠한 광고도 시청자에게 보여주지 못한다면, 콘텐츠 게시자는 광고가 시청자에게 노출되지 않은 경우에도 수익을 가져와야할 수도 있습니다. Conditional Ad에 대한 VAST 발표는 콘텐츠 게시자들이 프로그래머틱 광고 전송에 있어 광고가 표시되지 않는 상황에서 수익을 가져가는 경우를 막고 이를 다시 되찾을 수 있도록 도와줍니다.
VAST 4.0에서는 VPAID 콘테이너가 광고를 표시하지 못할 때 (블랙 스크린으로 표시될 경우), 광고에 “conditional” 플래그를 추가해 콘텐츠 게시자에게 이를 알릴 수 있는 기능을 제공합니다.

서버 사이드 스티칭 및 메자닌 파일

VAST는 고화질의 서버사이드 광고 삽입(Server Side Ad Insertion) 및 스티칭광고를 위한 메자닌 화질의 비디오를 허용하지 않았습니다. 하지만 VAST 4.0에서 메자닌 파일 지원 기능을 추가하며 이 문제를 간단히 해결했습니다. VAST 4.0은 고화질 및 길이가 긴 비디오 콘텐츠를 포함한 모든 비디오 플랫폼에 광고를 전송하기 위해 원본 및 고화질의 메자닌 파일을 지원합니다. 메자닌 파일은 크기가 커 광고 디스플레이를 위해 사용될 수는 없지만, 광고 스티칭 서비스와 다른 광고 벤더는 메자닌 파일을 활용해 시청환경에 맞는 적절한 화질의 파일을 생성할 수 있습니다. 즉, 이 업데이트를 통해 콘텐츠 게시자들은 비디오 스트림을 고화질로 렌더링할 수 있게 되었을 뿐 아니라 콘텐츠와 광고 모두를 끊기지 않게 최종 소비자에게 전달할 수 있게 되었습니다.

인터랙티브 요소 파일과 비디오 파일의 분리

서버사이드 광고 삽입(SSAI)과 마찬가지로 VAST 4.0은 광고 하나하나에 각각의 “Interactive Creative File node”를 제공합니다. 이를 통해 광고주들이 스티치 된 광고 비디오 스트림에까지 인터랙티브 요소를 제공할 수 있도록 지원할 수 있게 되었습니다. 오늘날 VPAID는 그 자체만으로 인터랙티브 광고를 지원합니다. 이는 콘텐츠 게시자에게는 소비자에게 끊김없는 광고경험을 제공할 수 있는 기회를 제공하고, 광고주에게는 소비자와의 상호작용을 눈으로 볼 수 있게 지원하기 때문에 게시자 및 광고주 모두를 만족시킵니다.

광고 인증 및 가시성 제공

광고주들은 원하는 자리에서 광고할 수 있기를 지속적으로 기대하고 있습니다. 이런 광고주들의 니즈를 충족시키기위해 VAST 4.0은 데이터를 수집하고 광고 재생 상세정보를 인증할 수 있는 코드를 포함해 콘테이너 배치를 계산할 수 있는 Ad Verifications 요소를 추가 했습니다. 원칙적으로 콘텐츠 게시자들은 의도하는 대로 인증 논리가 광고 재생을 추적할 수 있도록 광고 재생 전에 이 논리를 검토합니다. 이 기능은 “신뢰하되 검증하라”는 말을 충실히 따르고 있고, 광고주에게는 각 광고 위치에 대한 가시성을 제공합니다.

출처:https://www.brightcove.com/ko/blog/2016/02/%EB%AF%B8%EA%B5%AD-%EC%9D%B8%ED%84%B0%EB%84%B7-%EA%B4%91%EA%B3%A0%ED%98%91%ED%9A%8Ciab-%EC%83%88%EB%A1%9C%EC%9A%B4-%EB%B9%84%EB%94%94%EC%98%A4-%EA%B4%91%EA%B3%A0-%ED%91%9C%EC%A4%80-vast-40-%EB%B0%9C%ED%91%9C

반응형

모바일  및 온라인 광고 관련 용어인데... 관련 개발을 하기위해선 일단 알아둬야 하는 지식이니까... 여기에 남겨둬야겠다. 이 또한 지식이니까 ㅎㅎ 


1. AD Network

다수의 광고미디어(퍼블리셔)를 네트워크로 묶어 광고주에게 RON(Run of Network)으로 판매하는 업체를 말합니다.  

AD network 서비스는 광고전송, 오디언스 타게팅, 리포팅 등의 기능을 갖춘 AD서버를 필요로 합니다. 

AD network를 가진 대표 사업자로는 메조미디어의 모바일 광고 네트워크 MAN과 구글 GDN, 다음 DDN, 모바일 분야에서는 다음 아담 등이 있습니다.

 

2. AD Exchange 

수많은 AD Network이 등장하다보니, 시장은 이 네트워크들을 모아줄 플랫폼을 필요로 하게 되었습니다.  

이 플랫폼이 AD network들의 거래 시장인 AD Exchange 입니다.  

개별 네트워크 안에서만 광고 거래가 가능했던 AD network와 달리,   AD Exchange는 네트워크 간의 거래를 가능하게 하여 전체 시장의 수요와 공급을 원활하게 합니다.  

그리고 거래의 효율성과 공정성을 높이기 위해 RTB(Real time bidding)가 도입되었습니다.

AD Exchange를 운영하는 곳은 구글 AD-X(double click), 야후 RightMedia, OpenX, appnexus 등이 있습니다.

 

3. DSP(Demand side Platform) 

AD Exchange의 등장으로 시장에서는 손쉽게 다양한 미디어와 오디언스들을 접할 수 있게 되었습니다.  

구매할 수 있는 광고미디어가 많아짐으로 인해 광고주는 그만큼 옥석을 가려내기가 어려워졌습니다.  

광고주가 효과적으로 광고 미디어를 구매할 수 있도록 하기 위해 등장한 플랫폼이 바로 DSP(Demand side Platform)입니다.  

DSP는 미디어와 오디언스 정보를 분석하여 광고주의 캠페인 목표를 달성할 수 있도록 자동화된 방식으로 구매를 진행합니다.

DSP 사업자로는 Turn, MediaMath, TheTradeDesk, DataXu 등이 있으며 이 중 TheTradeDesk는 메조미디어와 업무 제휴를 맺었습니다.

 

4. DMP(Data management platform)

DSP가 효과적인 구매를 하기 위해서는 미디어와 오디언스의 데이터에 대한 즉각적인 분석이 병행되어야 합니다.  

DMP는 이러한 데이터 분석을 위해 정보를 쌓고 관리하는 플랫폼입니다.  

DSP 자체적으로 DMP를 보유하여 정보를 분석하는 경우와 3rd party 데이터 업체를 통해 정보를 확보하는 방법이 있습니다.

 

5. 최적화(Optimization)

DSP에서 광고주의 캠페인 목표를 달성하도록 하기 위해 여러 조건들을 지속적으로 조정하게 됩니다.  

이러한 과정을 최적화(Optimization)라고 하며 입찰가 최적화, 미디어 최적화, 타게팅 최적화, 광고소재 최적화 등   DSP사 별로 각자의 노하우를 통해 다양한 방식으로 진행이 가능합니다.

 

6. SSP(Supply side platform) 

광고미디어가 가장 중요하게 생각하는 것은 수익 극대화 입니다.

이를 지원하기 위해 등장한 플랫폼이 SSP(Supply side platform) 입니다.  

AD Exchange로 인해 광고판매 경로가 다양해진 환경에서 SSP는 광고미디어의 수익을 극대화하기 위해   다양한 네트워크나 DSP로부터 가장 수익성 높은 경로에 광고를 판매를 합니다.

대표적인 SSP 사업자는 Rubicon, Pubmatic가 있습니다.


출처 : Mezzomedia    

출처: http://superkong85.tistory.com/8 [SuperKong]

반응형

'광고관련' 카테고리의 다른 글

[Data Science] RTB(Real Time Bidding) System이란?  (0) 2018.01.04
VAST 란??  (1) 2018.01.03

요번엔 Elasticsearch 초간단 실행방법에 대해 알아보자.


알아보기 전에, Elasticsearch가 뭔지에 설명을 하고 넘어가자. 

Elasticsearch 는 분산형 RESTful 검색 및 분석 엔진이다. (홈페이지에도 이렇게 설명을 하고 있다...)

처음엔 데이터 검색엔진이라고 생각했지만, 데이터 분석과 시각화 기능까지 제공하고 있다. 

시각화 기능은 Kibana를 설치하여 이용할 수 있는데, 이 부분은 추후에 진행해보자.


그럼 이제, Elasticsearch 설치부터 알아보자.

참고로, 설치 환경은 Linux 환경 (Ubuntu 16.04) 이다. 


우선 설치 파일은 https://www.elastic.co/kr/downloads/elasticsearch 에서 받을 수 있다.

글 작성시의 최신 버전은 5.5.1이다. 


다운로드 페이지에서 tar 파일을 받아서 설치할 경로에 저장을 한다.

명령어로 받으려면 아래와 같이 하면 된다.


wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.5.1.tar.gz


다운로드 받은 파일은 tar 파일로 압축을 해제 한다.


tar zxvf elasticsearch-5.5.1.tar.gz 


압축해제를 하고나면, 파일명과 같은 폴더가 생기고, 파일 목록은 아래와 같다.


$~/elasticsearch/elasticsearch-5.5.1$ ls

LICENSE.txt  NOTICE.txt  README.textile  bin  config  data  lib  logs  modules  plugins


bin 폴더에 가서 elasticsearch 를 실행하면 되는데, 

전에 config 폴더에 elasticsearch.yml 파일을 열어서 몇가지 설정만 해놓자.


먼저, 아래 글과 같이 path.logs에 경로를 지정한다. 이름과 같이 로그 파일 저장경로이다.


...

# ----------------------------------- Paths ------------------------------------

#

# Path to directory where to store the data (separate multiple locations by comma):

#

#path.data: /path/to/data

#

# Path to log files:

#

path.logs: /home/austinlee/elasticsearch/elasticsearch-5.5.1/logs

...


다음으로는, IP 설정이다. elasticsearch 서버의 IP 주소를 입력한다. 

일단, 내부적으로 테스트 하는 것이므로 Localhost인 127.0.0.1로 한다.


...

# ---------------------------------- Network -----------------------------------

#

# Set the bind address to a specific IP (IPv4 or IPv6):

#

network.host: 127.0.0.1

#

# Set a custom port for HTTP:

#

#http.port: 9200

#

# For more information, consult the network module documentation.

...


이렇게 설정하고 나서, bin 폴더에 있는 elasticsearch를 실행한다.


./bin/elasticsearch


데몬으로 실행하고 싶으면 다음과 같이 실행하면 된다.


./bin/elasticsearch -d


데몬으로 실행했을 경우, 종료는 해당 pid 를 찾아서  kill 을 하면 된다.


# pid 찾기 

ps -ef | grep elasticsearch 


# 찾은 pid 로 프로세스 죽이기

kill 'pid'


elasticsearch 서버를 실행하고 나서 정상적으로 동작을 하는지, 웹 브라우저를 통해 확인한다.

주소 창에 http://localhost:9200/를 입력하고 접속을 한다. (localhost는 위에서 설정한 IP 주소, 접속  Default 포트는 9200이다.)


{
  "name" : "OR5RLR1",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "Fe_tQmzOT8OVj-K1C7VQYA",
  "version" : {
    "number" : "5.5.1",
    "build_hash" : "19c13d0",
    "build_date" : "2017-07-18T20:44:24.823Z",
    "build_snapshot" : false,
    "lucene_version" : "6.6.0"
  },
  "tagline" : "You Know, for Search"
}

정상 동작이면 위와 같은 정보가 나온다.


이렇게 초간단 실행법은 끝!

이제 REST API 를 이용해서 데이터를 저장하고 검색하면 된다. 이에 대한 것은 다음에....






반응형

오랜만에 들어온 김에 요즘 해보고 있는 것에 대해 글을 써보자는 취지로 작성!


요즘 Crawler 를 만들어보고 싶어서 이것저것 알아 본 결과, 개발하기 쉬운 Web Crawler를 선정했다.

 

우선, Web Crawler에 대한 설명은...

아래 링크를 참조하고, 

https://ko.wikipedia.org/wiki/%EC%9B%B9_%ED%81%AC%EB%A1%A4%EB%9F%AC

개발에 대해 얘기를 해보자.


Youtube를 Crawling 대상으로 선정한 이유는 아무래도 데이터가 많고, 다양하기 때문에 선정하게 되었다. (나름 참고할 문서들도 많았다...)


그리고 개발 언어는 Python을 이용하였고, 버전은 3.5버전이다.

원래 2.7버전을 했었는데, 3.5 버전이 더 빠르다보니(더 안정적인거 같다...) 바꾸게 되었다.


개발하기 전에 먼저 필요한 것은 Python 설치와 BeautifulSoup 설치이다.

Python 설치는 구글에 'Python 설치' 라고 검색하면 잘나와있기에 넘어간다.


BeautifulSoup 은 html 과 xml 문서를 파싱해주는 라이브러리이다.


pip를 이용하여 간단하게 설치할 수 있다.


pip install lxml



Youtube Crawling 의 경우, xml 문서를 파싱하기 때문에 위와 같이 설치하지만, html 파싱을 하는 경우에는 

아래 명령어로 설치를 할 수가 있다.


pip install html5lib



BeautifulSoup 라이브러리를 설치하고 본격적으로 개발을 해보자.


Python에 대해 잘모르지만, BeautifulSoup 문서와 다른 자료들을 참고하여 아래와 같은 소스를 만들었다. 



# -*- coding: utf-8 -*-

from bs4 import BeautifulSoup 

import lxml 

import requests 

import json 

import datetime

 


video_info = { 'title':'', 'video_link':'', 'img_link':'', 'play_time':'', 'hits' : '', 'updated_time':'', 'description':'', 'reg_time':'' } 


def get_video_link(target_url): 

response = requests.get(target_url) 

soup = BeautifulSoup(response.text, "lxml") 

lis = soup.find_all('li', {'class' : 'channels-content-item yt-shelf-grid-item'}) 

for li in lis : 

title = li.find('a', {'title' : True})['title'] 

video_link = 'https://www.youtube.com' + li.find('a', {'href' : True})['href'] 

img_link = li.find('img', {'src' : True})['src'] 


#<span class="video-time" aria-hidden="true"><span aria-label="8분, 55초">8:55</span></span>

play_time = li.find('span', {'class' : 'video-time'}).text 


#<ul class="yt-lockup-meta-info"><li>조회수 2,902,617회</li><li>6개월 전</li></ul>

hits = li.find_all('li')[2].text 

updated_time = li.find_all('li')[3].text 

video_info = { 

'title' : title, 

'video_link' : video_link, 

'img_link' : img_link, 

'play_time' : play_time, 

'hits' : hits, 

'updated_time' : updated_time 

}

 

print(video_info) 


return video_info 


def get_hot_video_info(target_url): 

response = requests.get(target_url) 

soup = BeautifulSoup(response.text, "lxml") 

lis = soup.find_all('li', {'class' : 'expanded-shelf-content-item-wrapper'}) 

for li in lis : 

# exception

try : 

title = li.find('a', {'title' : True})['title'] 

video_link = 'https://www.youtube.com' + li.find('a', {'href' : True})['href'] 

img_info = li.find('img', {'data-thumb' : True})

if img_info != None :

img_link = img_info['data-thumb'] 

else : 

img_link = li.find('img', {'src' : True})['src'] 

#<span class="video-time" aria-hidden="true"><span aria-label="8분, 55초">8:55</span></span>

#play_time = li.find('span', {'class' : 'video-time'}).text 

play_time_info = li.find('span', {'class' : 'video-time'})

if play_time_info != None :

play_time = play_time_info.text

else :

play_time = None

#<ul class="yt-lockup-meta-info"><li>조회수 2,902,617회</li><li>6개월 전</li></ul>

hits = li.find_all('li')[3].text 

updated_time = li.find_all('li')[2].text 

description_info = li.find('div',{'class':True, 'class':'yt-lockup-description yt-ui-ellipsis yt-ui-ellipsis-2'})

if description_info != None :

description = description_info.text

else :

description = None


now = datetime.datetime.now()

video_info = { 

'title' : title, 

'video_link' : video_link, 

'img_link' : img_link, 

'play_time' : play_time, 

'hits' : hits, 

'updated_time' : updated_time,

'description' : description,

'reg_time' : now.strftime('%Y-%m-%d %H:%M:%S')

}

print(video_info) 


except BaseException as e : 

print(e)

 


return video_info 



target_url = 'https://www.youtube.com/user/CJENMMUSIC/videos' 

target_url2 = 'https://www.youtube.com/feed/trending'


# 특정 채널

#get_video_link(target_url)


# 인기 리스트

get_hot_video_info(target_url2)




위의 소스를 조금씩 파헤쳐보자. 

첫번째 줄인 # -*- coding: utf-8 -*- 이 부분은 한글 인코딩과 관련하여 넣어준 부분이다.


다음으로 import 부분은 필요한 라이브러리를 선언하는 부분이다.


다음 줄에는 video_info 를 선언하는 부분이다. 이것은 Dictionary 타입으로 Key-Value 형식으로 되어 있다. Youtube Crawling 한 데이터 중 필요한 부분을 여기에 저장한다.


이 프로그램에는 두개의 함수가 있다. 하나는 get_video_link 이고 다른 하나는 get_hot_video_info 이다. 


get_video_link 는 특정 채널에 대한 영상 정보를 가져오는 함수이고, get_hot_video_info 는 인기 채널에 대한 영상 정보를 가져오는 함수이다.

참고로 get_video_link 함수는 예외처리가 제대로 안되어 있으므로, 참고를 한다면 get_hot_video_info 함수를 참고하는 것이 좋다.


설명도 get_hot_video_info 함수에 대해서만 해보자. (차근차근 설명을 남기기 위해 쪼개서 설명하자...)


response = requests.get(target_url) 

soup = BeautifulSoup(response.text, "lxml") 

lis = soup.find_all('li', {'class' : 'expanded-shelf-content-item-wrapper'}) 


먼저 target_url 은 Crawling할 Youtube 주소를 의미 하고, 

첫번째 라인은 target_url 로부터 xml 을 요청하는 부분이고, 이에 대한 반환 값은 response 이다.

두번째 라인은 반환값인  response를 BeautifilSoup 라이브러리를 이용하여 파싱을 하는 부분이다. 파싱결과는 soup이다.

세번째 부분은 파싱된 soup에서 li 속성 중 class가 expanded-shelf-content-item-wrapper 인 부분을 찾아서 리스트로 넣는 부분이다.

리스트는 lis 이다.

이 부분을 이해하기 위해선 xml에 대한 지식이 필요하다...



간단하게 설명을 하자면, 

크롬 브라우저에서 Youtube 인기를 접속하여 개발자모드(F12를 누르면됨)를 들어가보면 다음과 같이 나온다. 익스플로러도 가능하다.




위의 그림과 같이 동영상에 파란색으로 나오게 하려면 xml 정보있는 부분 위에  과 같은 표시를 누르고 

동영상 부분에 마우스를 가지고 가면 해당 부분에 대한 xml 위치를 나타낸다. 여기서 보면 저 부분이 li 속성이고 class가 expanded-shelf-content-item-wrapper 라는 것을 알 수 있다. 이 부분에서 해당 동영상에 대한 정보를 가지고 오는 것이다.





다시 코드설명으로 돌아와서 설명을 해보자.


for li in lis : 

# exception

try : 

title = li.find('a', {'title' : True})['title'] 

video_link = 'https://www.youtube.com' + li.find('a', {'href' : True})['href'] 

img_info = li.find('img', {'data-thumb' : True})

if img_info != None :

img_link = img_info['data-thumb'] 

else : 

img_link = li.find('img', {'src' : True})['src'] 

#<span class="video-time" aria-hidden="true"><span aria-label="8분, 55초">8:55</span></span>

#play_time = li.find('span', {'class' : 'video-time'}).text 

play_time_info = li.find('span', {'class' : 'video-time'})

if play_time_info != None :

play_time = play_time_info.text

else :

play_time = None

#<ul class="yt-lockup-meta-info"><li>조회수 2,902,617회</li><li>6개월 전</li></ul>

hits = li.find_all('li')[3].text 

updated_time = li.find_all('li')[2].text 

description_info = li.find('div',{'class':True, 'class':'yt-lockup-description yt-ui-ellipsis yt-ui-ellipsis-2'})

if description_info != None :

description = description_info.text

else :

description = None


now = datetime.datetime.now()


이 부분은 반복문으로 lis 리스트를 li에 넣고 실질적인 분석을 하는 부분이다. (try 는 혹시 모를 exception을 위해 걸어 놓았다.)

분석은 위에서 간단하게 설명한 xml 분석을 통해서 필요한 정보를 찾고 그 위치를 코드에 입력하는 방식이다.

title 은 동영상의 제목으로, a 속성에 title 정보가 있는 부분에 존재를 한다. 

video_link 는 동영상 페이지로 접속하는 페이지링크로, a 속성에 href 정보가 있는 부분에 존재하며, 공통 도메인인 'https://www.youtube.com'이 빠져 있으므로 직접 추가해줬다.

img_link 는 동영상의 썸네일 이미지정보로, img 속성에 data-thumb 정보가 있다면, data-thumb 에 존재하고, 

data-thumb 정보가 없는 경우, src 정보에 존재한다.

play_time 은 동영상 길이에 대한 정보로, span 속성에 video-time 정보에 존재한다. 이 정보를 존재하지 않는 경우도 있다. (정확한 이유를 아직...)

hits 는 조회 수 정보로, li 속성 중 4번째 위치에 있는 정보이다. (해당 정보안에 li 속성이 여러개 존재하는데, 앞에 3개가 있고 4번째에 조회 수 정보가 존재.)

updated_time 는 업로드 시간 정보로, li 속성 중 3번째 위치에 있는 정보이다.

description 는 동영상 설명에 대한 정보로, div 속성 중 class 정보가 yt-lockup-description yt-ui-ellipsis yt-ui-ellipsis-2 인 곳에 있는 정보이다.

now 는 현재 Crawling 한 시간 정보이다.


video_info = { 

'title' : title, 

'video_link' : video_link, 

'img_link' : img_link, 

'play_time' : play_time, 

'hits' : hits, 

'updated_time' : updated_time,

'description' : description,

'reg_time' : now.strftime('%Y-%m-%d %H:%M:%S')

}

print(video_info) 


except BaseException as e : 

print(e)

 


return video_info 


추출한 정보들을 video_info 에 위와 같이 저장을 하고, 출력을 한다.

추후에 수집한 데이터를 저장하기 위해서는 이 부분에는 데이터를 전송하는 부분이 추가되어야 한다. 

예외가 발생하는 부분이 있으면 except BaseException as e :  으로 빠지고 계속해서 반복문을 수행한다.

반복문 수행이 끝나면 리턴한다.



target_url = 'https://www.youtube.com/user/CJENMMUSIC/videos' 

target_url2 = 'https://www.youtube.com/feed/trending'


# 특정 채널

#get_video_link(target_url)


# 인기 리스트

get_hot_video_info(target_url2)


target_url2 은 Youtube 인기 채널 주소이고, get_hot_video_info(target_url2) 은 함수를 호출하는 부분이다.


코드에 대한 설명은 여기까지 한다.

혹시 몰라서 특정 채널에 대한 함수를 뺀 설명한 부분만 있는 소스코드는 아래와 같다.


# -*- coding: utf-8 -*-

from bs4 import BeautifulSoup 

import lxml 

import requests 

import json 

import datetime

 


video_info = { 'title':'', 'video_link':'', 'img_link':'', 'play_time':'', 'hits' : '', 'updated_time':'', 'description':'', 'reg_time':'' } 


def get_hot_video_info(target_url): 

response = requests.get(target_url) 

soup = BeautifulSoup(response.text, "lxml") 

lis = soup.find_all('li', {'class' : 'expanded-shelf-content-item-wrapper'}) 

for li in lis : 

# exception

try : 

title = li.find('a', {'title' : True})['title'] 

video_link = 'https://www.youtube.com' + li.find('a', {'href' : True})['href'] 

img_info = li.find('img', {'data-thumb' : True})

if img_info != None :

img_link = img_info['data-thumb'] 

else : 

img_link = li.find('img', {'src' : True})['src'] 

#<span class="video-time" aria-hidden="true"><span aria-label="8분, 55초">8:55</span></span>

#play_time = li.find('span', {'class' : 'video-time'}).text 

play_time_info = li.find('span', {'class' : 'video-time'})

if play_time_info != None :

play_time = play_time_info.text

else :

play_time = None

#<ul class="yt-lockup-meta-info"><li>조회수 2,902,617회</li><li>6개월 전</li></ul>

hits = li.find_all('li')[3].text 

updated_time = li.find_all('li')[2].text 

description_info = li.find('div',{'class':True, 'class':'yt-lockup-description yt-ui-ellipsis yt-ui-ellipsis-2'})

if description_info != None :

description = description_info.text

else :

description = None


now = datetime.datetime.now()

video_info = { 

'title' : title, 

'video_link' : video_link, 

'img_link' : img_link, 

'play_time' : play_time, 

'hits' : hits, 

'updated_time' : updated_time,

'description' : description,

'reg_time' : now.strftime('%Y-%m-%d %H:%M:%S')

}

print(video_info) 


except BaseException as e : 

print(e)

 


return video_info 



target_url2 = 'https://www.youtube.com/feed/trending'


# 인기 리스트

get_hot_video_info(target_url2)



Web Crawler는 xml 파싱을 해서 필요한 정보가 어느 위치인지를 분석하고 나면, 금방 만들 수 있을 것 같다.

결국 분석 능력이다....


다음엔 Youtube가 아닌 다른 사이트에 대한 Web Crawling을 해봐야겠다. 


반응형

+ Recent posts