New to Nutbox?

[개발이야기#042] 내가 해보고 싶은 것 - 한국 스팀잇 M2E 사용자 모임 글 수집하기 - 게시글 수집하기

3 comments

talkit
68
yesterday11 min read

안녕하세요 가야태자 @talkit 입니다.

관련글

[개발이야기#028] 내가 해보고 싶은 것 - 자동 보팅 프로그램 SQLite vs DuckDB [postingcuration]

[개발이야기#029] 내가 해보고 싶은 것 - 자동 보팅 프로그램 사용자 및 포스트 테이블 생성하기 [postingcuration]

[개발이야기#031] 내가 해보고 싶은 것 - 자동 보팅 프로그램 사용자 등록 프로그램 작성하기 [postingcuration]

[개발이야기#032] 내가 해보고 싶은 것 - 자동 보팅 프로그램 사용자 게시글 수집기 작성하기 [postingcuration]

[개발이야기#034] 내가 해보고 싶은 것 - 포스팅 큐레이션 글을 자동으로 정리해보자 [postingcuration]

[개발이야기#034] 내가 해보고 싶은 것 - 포스팅 큐레이션 글 목록을 자동 포스팅 하기 [postingcuration]

[개발이야기#035] 내가 해보고 싶은 것 - 자동 보팅 프로그램 하루에 한번 보팅하는 프로그램[postingcuration]\

[개발이야기#036] 내가 해보고 싶은 것 - 자동 보팅 프로그램 스케쥴러 프로그램 [postingcuration]

[개발이야기#037] 내가 해보고 싶은 것 - 자동 보팅 프로그램 정리 [postingcuration]

[개발이야기#040] 내가 해보고 싶은 것 - 한국 스팀잇 M2E 사용자 모임 글 수집하기

[개발이야기#041] 내가 해보고 싶은 것 - 한국 스팀잇 M2E 사용자 모임 글 수집하기 - 사용자 등록

머릿말

지난 번 글에 이어서 오늘은 사용자들의 포스트들, 즉 글을 수집해 보겠습니다.

위 글들 중에 #032와 동일한 역할을 하고, 대상 데이터베이스를 MariaDB로 넣습니다.

수집기 소스

import mariadb

from steem import Steem

import pandas as pd

from datetime import datetime

import traceback

import json  # 추가: JSON 파싱을 위해 필요

  

# Steemit에 접속 (필요 시 API 노드 설정)

steem = Steem(nodes=['https://api.steemit.com'])  # 노드 설정을 통해 안정적인 연결

  

# MariaDB에 연결 (파일 기반 데이터베이스가 아닌 MariaDB를 사용)

def connect_db():

    try:

        conn = mariadb.connect(

            user="steemit",

            password="비밀번호",

            host="서버주소",

            database="steemit_postings"

        )

        return conn

    except mariadb.Error as e:

        print(f"Error connecting to MariaDB: {e}")

        return None

  

# 로그 함수

def log(message):

    print(f"{datetime.now()} - {message}")

  

# 상세 오류 로그 출력 함수

def log_error(e):

    error_message = ''.join(traceback.format_exception(None, e, e.__traceback__))

    print(f"{datetime.now()} - ERROR: {error_message}")

  

# 특정 URL이 postings 테이블에 존재하는지 확인하는 함수

def url_exists(conn, url):

    try:

        cursor = conn.cursor()

        cursor.execute("SELECT COUNT(1) FROM postings WHERE post_id = ?", (url,))

        result = cursor.fetchone()[0]

        cursor.close()

        return result > 0

    except Exception as e:

        log_error(e)

        return False

  

# 모든 사용자의 최근 게시물 수집 함수

def fetch_recent_posts(conn, username, limit=100):

    try:

        log(f"Starting to fetch posts for user '{username}' with limit {limit}")

        # 각 사용자의 최근 게시물 가져오기

        posts = steem.get_discussions_by_blog({'tag': username, 'limit': limit})

        post_list = []

  

        for post in posts:

            try:

                if post['author'] != username:

                    continue

                json_metadata = post.get('json_metadata', {})

  

                if isinstance(json_metadata, str):

                    json_metadata = json.loads(json_metadata)

  

                tags_list = json_metadata.get('tags', [])

                tags_str = ','.join(tags_list)

  

                # 대표 태그 설정 로직 수정

                # 특정 태그가 포함된 경우 main_tag를 'kr-m2e'로 설정

                special_tags = {'kr-m2e', 'm2e-kr', 'stepn-kr', 'm2e'}

                if any(tag in special_tags for tag in tags_list):

                    main_tag = 'kr-m2e'

                else:

                    main_tag = 'postingcuration' if 'postingcuration' in tags_list else (tags_list[0] if tags_list else '')

  

                url = f"https://steemit.com/{post['category']}/@{post['author']}/{post['permlink']}"

  

                if url_exists(conn, url):

                    continue

  

                post_details = {

                    'post_id': url,

                    'user_id': post.get('author'),

                    'title': post.get('title'),

                    'body': post.get('body'),

                    'tags': tags_str,

                    'main_tag': main_tag,

                    'voting_status': False,

                    'posting_date': post.get('created'),

                    'created_at': post.get('created'),

                    'modified_at': post.get('last_update')

                }

  

                post_list.append(post_details)

            except json.JSONDecodeError:

                log(f"Failed to parse json_metadata for post '{post.get('id')}'")

            except Exception as e:

                log_error(e)

  

        return post_list

    except Exception as e:

        log_error(e)

        return []

  

# 사용자 목록 가져오기 함수

def get_users(conn):

    try:

        log("Fetching active users from database")

        cursor = conn.cursor()

        cursor.execute("SELECT user_id FROM users WHERE is_active = 'Y' ORDER BY user_id ASC")

        users = [row[0] for row in cursor.fetchall()]

        cursor.close()

        log(f"Found {len(users)} active users")

        return users

    except Exception as e:

        log_error(e)

        return []

  

def insert_posts_to_db(conn, df_posts):

    try:

        cursor = conn.cursor()

        for _, row in df_posts.iterrows():

            cursor.execute("""

                INSERT INTO postings (post_id, user_id, title, body, tags, main_tag, voting_status, posting_date, created_at, modified_at)

                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

            """, (row['post_id'], row['user_id'], row['title'], row['body'], row['tags'], row['main_tag'],

                  row['voting_status'], row['posting_date'], row['created_at'], row['modified_at']))

        conn.commit()

        cursor.close()

    except Exception as e:

        log_error(e)

  

def main():

    conn = connect_db()

    if conn is None:

        log("Failed to connect to the database. Exiting...")

        return

  

    usernames = get_users(conn)

  

    summary = {}

  

    for username in usernames:

        try:

            posts = fetch_recent_posts(conn, username, limit=100)

  

            if posts:

                df_posts = pd.DataFrame(posts)

                log(f"Post data for user '{username}':\n{df_posts}")

  

                insert_posts_to_db(conn, df_posts)

                log(f"Inserted {len(df_posts)} posts for user '{username}' into the database.")

                summary[username] = len(df_posts)

            else:

                summary[username] = 0

        except Exception as e:

            log_error(e)

            summary[username] = 0

  

    log("Summary of collected posts:")

    for user, count in summary.items():

        if count > 0:

            log(f"User '{user}': {count} posts collected.")

  

    conn.close()

  

if __name__ == "__main__":

    main()

위 소스에서 steemit으로 아이디를 만들지 않으셨다면 아이디도 바꾸셔야 합니다.

데이터베이스도 바꾸셔야 하구요. 그외 한글로 되어 있는 부분은 설정하신 내용으로 변경해주십시오.

코드의 흐름은 다음과 같습니다.

  1. Steemit API 연결: Steem 객체를 생성하여 Steemit 블록체인에 연결하고, API 노드를 설정합니다.

  2. MariaDB 연결: connect_db() 함수를 통해 MariaDB 데이터베이스에 연결합니다. 데이터베이스 연결에 실패하면, 오류 메시지를 출력하고 프로그램을 종료합니다.

  3. 로그 출력 함수: log() 함수는 프로그램의 진행 상황을 출력하는 역할을 하며, log_error() 함수는 예외가 발생한 경우 상세한 오류 메시지를 출력합니다.

  4. URL 중복 확인 함수: url_exists() 함수는 특정 게시물 URL이 postings 테이블에 이미 존재하는지 확인합니다. 이 함수는 SQL 쿼리를 사용하여 해당 URL의 존재 여부를 검사하고, 결과에 따라 True 또는 False를 반환합니다.

  5. 게시물 수집 함수: fetch_recent_posts() 함수는 특정 사용자의 최근 게시물을 Steemit API를 통해 가져옵니다. 게시물의 메타데이터를 파싱하여 태그 목록을 추출하고, 특정 태그가 포함된 경우 main_tagkr-m2e로 설정하는 로직이 포함되어 있습니다. 중복된 URL이 존재하면 이를 건너뛰고, 게시물의 정보를 post_list에 저장합니다.

  6. 사용자 목록 가져오기 함수: get_users() 함수는 데이터베이스에서 활성화된 사용자 목록을 가져옵니다. 활성화된 사용자(is_active = 'Y')의 ID를 SQL 쿼리를 통해 조회하고, 결과를 반환합니다.

  7. 게시물 데이터베이스 삽입 함수: insert_posts_to_db() 함수는 DataFrame 형식의 게시물 데이터를 데이터베이스의 postings 테이블에 삽입합니다. 각 게시물의 필드를 SQL 쿼리를 사용해 삽입합니다.

  8. 메인 로직: main() 함수는 데이터베이스에 연결한 뒤, 활성화된 사용자의 목록을 가져옵니다. 각 사용자에 대해 게시물을 수집하고, 수집된 게시물이 있으면 이를 DataFrame으로 변환하여 데이터베이스에 저장합니다. 모든 사용자의 게시물 수집이 완료되면, 수집된 게시물의 요약을 출력합니다.

이 코드는 Steemit 블록체인의 특정 사용자들의 최근 게시물을 수집하고 이를 데이터베이스에 저장하기 위한 스크립트입니다. 데이터베이스 연결, API 호출, 데이터 파싱, 데이터베이스 삽입, 중복 확인 등 여러 단계를 포함하고 있습니다.

코드 실행하기

파이썬 가상환경 활용

여기서는 지난 번에 만든 steemitm2e 가상 환경을 사용합니다.

conda activate steemitm2e

그런데 T.T 위 가상환경을 만들때 저희는 python을 3.12버전을 활용했었는데 steemit 패키지가 3.12에서는 잘안되서 3.8로 내려서 진행 했습니다.

저는 개발자여서 Visual Studio가 설치 되어 있는데 없으면 설치 하셔야할 수도 있습니다.

Windows 이야기이고 리눅스는 좀 더 편안하게 작업할 수 있습니다.

이 부분은 이번 글에서 생략하고 따로 한번 글을 적어 보겠습니다.

conda create -n steemitm2e python=3.8.19
conda activate steemitm2e

위와 같이 해서 steemitm2e 가상환경을 설정 합니다.

pip install mariadb steem pandas

위 프로그램을 설치할때 윈도우즈시라면 몇몇 오류가 발생할 껍니다.

그 오류에 관련된 이야기는 우선 개발기 끝나고 다시 적어 보겠습니다.

pip install pycryptodome

설치가 끝나도 하나 더 설치해야 합니다. T.T

여튼 우여 곡절 끝에 steemitm2e 가상환경이 다 만들어졌습니다.

파일 저장

 collect_steem_postings_mysql.py

소스 코드를 위와 같이 저장 하십시오.

그러면 실행은 간단 합니다.

python collect_steem_postings_mysql.py

위 프로그램만 실행하면 ^^

실행 될 때마다 사용자 테이블에 있는 사용자들의 최근글을 수집 합니다.

저위에 보이겠지만,

#kr-m2e #m2e-kr #stepn-kr 태그를 다시면 메인 태그를 kr-m2e로 제가 저장하는 테이블에서 변경하고 있습니다.

추후에는 복합 테이블이 되도록 해서 태그를 관리하는 테이블을 만들어 볼생각입니다.

우선 빨리 포스팅 프로그램을 짜야 해서 ^^

저렇게 진행 했습니다.

결론

잘 수집됩니다.

2024-10-27 10:42:17.957327 - Summary of collected posts:
2024-10-27 10:42:17.957327 - User 'areumlabs': 18 posts collected.
2024-10-27 10:42:17.959811 - User 'banbagi8011': 100 posts collected.
2024-10-27 10:42:17.960800 - User 'cancerdoctor': 100 posts collected.
2024-10-27 10:42:17.961765 - User 'dorian-lee': 99 posts collected.
2024-10-27 10:42:17.962743 - User 'epitt925': 63 posts collected.
2024-10-27 10:42:17.962743 - User 'ezen': 100 posts collected.
2024-10-27 10:42:17.963718 - User 'forealife': 99 posts collected.
2024-10-27 10:42:17.963718 - User 'happyday5433': 76 posts collected.
2024-10-27 10:42:17.964694 - User 'happypray': 89 posts collected.
2024-10-27 10:42:17.965670 - User 'hdc': 99 posts collected.
2024-10-27 10:42:17.966647 - User 'hellogomc2': 100 posts collected.
2024-10-27 10:42:17.967624 - User 'hodolbak': 100 posts collected.
2024-10-27 10:42:17.967624 - User 'illluck': 100 posts collected.
2024-10-27 10:42:17.969195 - User 'jakie77': 91 posts collected.
2024-10-27 10:42:17.969195 - User 'jeongpd': 96 posts collected.
2024-10-27 10:42:17.970110 - User 'jimae': 99 posts collected.
2024-10-27 10:42:17.971087 - User 'jsquare': 99 posts collected.
2024-10-27 10:42:17.971087 - User 'jungjunghoon': 53 posts collected.
2024-10-27 10:42:17.971087 - User 'kaine': 100 posts collected.
2024-10-27 10:42:17.972062 - User 'kangbyeongdo': 11 posts collected.
2024-10-27 10:42:17.973039 - User 'luminaryhmo': 95 posts collected.
2024-10-27 10:42:17.974015 - User 'manacoco': 83 posts collected.
2024-10-27 10:42:17.974992 - User 'mantonge': 98 posts collected.
2024-10-27 10:42:17.974992 - User 'newbijohn': 100 posts collected.
2024-10-27 10:42:17.975968 - User 'newiz': 97 posts collected.
2024-10-27 10:42:17.976945 - User 'parkname': 96 posts collected.
2024-10-27 10:42:17.977921 - User 'powerego': 80 posts collected.
2024-10-27 10:42:17.977921 - User 'shrah011': 48 posts collected.
2024-10-27 10:42:17.977921 - User 'siegfried.jeong': 44 posts collected.
2024-10-27 10:42:17.979477 - User 'sjsr': 100 posts collected.
2024-10-27 10:42:17.979477 - User 'sog332': 100 posts collected.
2024-10-27 10:42:17.980405 - User 'talkit': 97 posts collected.
2024-10-27 10:42:17.980405 - User 'tomchoi': 78 posts collected.
2024-10-27 10:42:17.981381 - User 'venomine': 100 posts collected.
2024-10-27 10:42:17.982357 - User 'yhj467': 30 posts collected.
2024-10-27 10:42:17.982357 - User 'yoghurty': 82 posts collected.
2024-10-27 10:42:17.983334 - User 'yonggyu01': 100 posts collected.
2024-10-27 10:42:17.983334 - User 'youngdeuk': 97 posts collected.

위와 같이 수집이 되었네요 ^^

이제 다음글에서 kr-m2e를 계정별로 정리하는 글을 작성해 보겠습니다.

이걸 저는 기존 스케쥴 프로그램에 넣어 두겠습니다. 45분에 한번씩 수집을 진행 하겠네요 ^^
감사합니다.



Posted through the ECblog app (https://blog.etain.club)

Comments

Sort byBest