예제코드분석 - moabogey/docs GitHub Wiki

코딩의 시작은 다른 사람이 만든 코드를 보고 따라하는 것입니다. 여기서는 예제 봇의 소스 코드를 분석하여 자신만의 봇을 만드는데 도움을 드리고자 합니다.

봇 구조

모아보기 봇의 구조는 3개의 파일로 구성되어 있습니다.

1. moabogey_database.py

수집한 데이터를 저장하고, 저장된 데이터와 비교하는 기능을 가지고 있는 파일입니다. 이 파일은 내용을 변경하지 않고 그냥 가져다가 사용하면 됩니다.

2. moabogey_id.py

봇의 고유값을 저장하는 파일입니다. 우리가 인터넷 커뮤니티에서 다른 사람과 닉네임이 중복되지 않는 것처럼 봇은 고유의 ID를 가지고 있습니다. 고유값은 일반적으로 봇의 파일 이름을 그대로 사용합니다.

3. {subject} _on _{site}.py

사이트에 들어가서 데이터를 수집하는 핵심적인 기능을 수행하는 파일입니다. 파일 이름은 모아보기 앱에서 일반 사용자에게 보여지기 때문에 알기 쉽도록 만들어 져야 합니다. 영어로만 표기가 되어야 하고 특수 문자등을 사용할 수 없습니다. 예제 봇들의 이름은 앞부분은 주제를 표시하고 뒷부분은 사이트를 알려 주고 있습니다. 예를 들어서 best_on_ruliweb이라는 이름은 루리웹에서 베스트를 모아주는 봇이라는 뜻을 가지고 있습니다.

소스 코드 분석

분석에 사용되는 소스 코드는 bts_army_on_twitter.py를 사용합니다. bts_army_on_twitter는 BTS ARMY의 트윗을 모아주는 봇입니다.

봇의 소스 코드는 크게 세단계로 나눌 수 있습니다.

  1. 사이트의 HTML에서 데이터를 수집
  2. 포스트의 HTML에서 데이터를 수집
  3. 데이터 저장

사이트의 HTML에서 데이터를 수집

데이터를 수집할 사이트의 정보와 주소를 설정합니다. 예제에서는 https://twitter.com/BTS_ARMY 에서 데이터를 수집합니다.

# 사이트 이름
site_name = 'twitter'
# 사이트에서 가져올 주제
subject_name = 'BTS_ARMY'
# 사이트 주소
site_url = 'https://twitter.com/BTS_ARMY'

requests와 beautifulsoup4를 이용해서 사이트의 HTML을 가져오고 파일로 저장합니다.

# 사이트에 대한 정보를 요청하고 응답을 받는다.
response = requests.get(site_url)

# 응답에서 HTML을 변수로 빼낸다.
html_source = response.text

# HTML을 html해석기로 해석하고 결과를 변수로 빼낸다.
soup = BeautifulSoup(html_source, 'html.parser')

# 해석 결과를 보기 좋게 만들어서 파일로 저장한다.
with open(file_name, 'w', encoding='utf-8') as f:
    f.write(soup.prettify())

저장된 HTML파일 (예제에서는 twitter_source.html)을 열어 봅니다. 여기서 우리는 "포스트의 리스트"를 표현하는 구간을 찾을 것입니다. 포스트는 제목, 내용, 이미지, 작성자, 작성 날짜 및 페이지 위치(URL)를 가지고 있는 하나의 문서(여기서는 트윗)를 나타내는 용어로 사용합니다.

+-------------+  +------>   <div class="content">
|   Post 1    |
|  (Tweet 1)  |
+-------------+  +------>   </div>

+-------------+  +------>   <div class="content">
|   Post 2    |
|  (Tweet 2)  |
+-------------+  +------>   </div>

+-------------+  +------->  <div class="content">
|   Post3     |
|  (Tweet 3)  |
+-------------+  +------>   </div>

예제에서 각각의 포스트는 <div class="content"> 에서 시작 되고 </div>로 끝난다는 것을 알아내는 것이 중요합니다. 이것은 사이트마다 다르기 때문에 이것을 찾아내는 것은 약간의 경험이 필요합니다.

# 반복해서 포스트의 목록을 하나씩 검색하며 데이터를 수집한다.
for post in soup.find_all('div', class_='content'):
    # 포스트를 올린 작성자를 수집한다.

발견한 포스트에서 작성자, 올린 시간 및 포스트 위치(URL)을 찾습니다.

+-------------+
|   Post 1    |
+-------------+
|  createdBy  | +------->  <strong class="fullname...> </strong>
|             |
|  post URL   | +------->  <a class="tweet-timestamp... href=...>
|             |
|  createdAt  | +------->  <a class="tweet-timestamp... title=...>
|             |
+-------------+

# 포스트를 올린 작성자를 수집한다.
moa_createBy = post.find('strong', class_='fullname').text.strip()

# 포스트의 주소를 수집한다.
href = post.find('a', {"class": "tweet-timestamp", "href": True})

# 포스트를 올린 날짜를 수집한다.
post_time = post.find('a', class_="tweet-timestamp")['title'].replace('오전', 'AM').replace('오후', 'PM')

나머지 데이터를 수집하기 위해서 포스트 HTML로 이동합니다.

포스트의 HTML에서 데이터를 수집

데이터를 수집할 포스트의 주소를 설정합니다.

href_url = 'https://twitter.com' + href['href']

requests와 beautifulsoup4를 이용해서 사이트의 HTML을 가져오고 파일로 저장합니다.

# 사이트에 대한 정보를 요청하고 응답을 받는다.
response =  requests.get(href_url)

# 응답에서 HTML을 변수로 빼낸다.
subhtml_source = response.text

# HTML을 html해석기로 해석하고 결과를 변수로 빼낸다.
post = BeautifulSoup(subhtml_source, 'html.parser')

# 해석 결과를 보기 좋게 만들어서 파일로 저장한다.
with open(file_name, 'w', encoding='utf-8') as f:
    f.write(post.prettify())

저장된 HTML파일 (예제에서는 twitter_post_source.html)을 열어 봅니다. 여기서 우리는 포스트의 제목, 요약, 이미지, 사이트 이름, 포스트 주소(URL), 수집 날짜 및 시간 데이터를 수집할 것입니다.

Open Graph Protocol

대부분의 사이트들은 우리가 수집할 데이터를 사이트의 첫머리에 미리 모아 놓고 있습니다. 이 규약(Protocol)은 사이트를 모두 분석하지 않고도 사이트의 내용을 파악하는데 도움이 됩니다.

아래와 같은 매타 태그를 사용합니다.

<head>
...
    <meta content="..." property="og:url"/>
    <meta content="..." property="og:title"/>
    <meta content="..." property="og:image"/>
    <meta content="..." property="og:description"/>
    <meta content="..." property="og:site_name"/>
...
</head>

데이터를 수집합니다.

# 포스트 제목을 수집/가공 한다.
moa_title = post.find('meta', property="og:description")

# 포스트 요약을 수집/가공한다.
moa_desc = post.find('meta', property="og:description")

# 대표 이미지의 주소를 수집한다.
moa_image = post.find('meta', property="og:image")

# 사이트 이름을 수집한다.
moa_site_name = post.find('meta', property="og:site_name")

# 포스트의 주소를 가공한다.
moa_url = 'https://mobile.twitter.com' + href

# 현재 날짜와 시간을 수집한다.
moa_timeStamp = datetime.now()

데이터 저장

수집한 데이터를 선별해서 중복되는 것을 제외하고 데이터베이스에 저장합니다. 모아보기 봇은 하루에 24번 이상 동작 하도록 되어 있기 때문에 한번에 모든 데이터를 수집하지 않고 가장 최근의 데이터 1~2개를 수집하는 것이 원칙입니다. 여기서는 데이터를 수집한 날짜와 동일한 날에 등록된 포스트만 수집하도록 프로그래밍되어 있습니다.

# 오늘 발행된 포스트만 선택한다.
delta = moa_timeStamp - moa_createdAt + timedelta(hours=9)
    if delta.days <=0:
        # 데이터베이스에 있는 포스트와 중복되는지를 확인한다.
        if my_db.isNewItem('title', moa_title):
            # JSON형식으로 수집한 데이터를 변환한다.
            db_data = { 'title': moa_title, 
                'desc': moa_desc,
                'url': moa_url,
                'image': moa_image,
                'siteName': moa_site_name,
                'createdBy': moa_createBy,
                'createdAt': moa_createdAt,
                'timeStamp': moa_timeStamp
            }
            # 수집한 데이터를 데이터베이스에 전송한다.
            my_db.insertTable(db_data)
⚠️ **GitHub.com Fallback** ⚠️