안녕하세요 가야태자 @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으로 아이디를 만들지 않으셨다면 아이디도 바꾸셔야 합니다.
데이터베이스도 바꾸셔야 하구요. 그외 한글로 되어 있는 부분은 설정하신 내용으로 변경해주십시오.
코드의 흐름은 다음과 같습니다.
Steemit API 연결: Steem
객체를 생성하여 Steemit 블록체인에 연결하고, API 노드를 설정합니다.
MariaDB 연결: connect_db()
함수를 통해 MariaDB 데이터베이스에 연결합니다. 데이터베이스 연결에 실패하면, 오류 메시지를 출력하고 프로그램을 종료합니다.
로그 출력 함수: log()
함수는 프로그램의 진행 상황을 출력하는 역할을 하며, log_error()
함수는 예외가 발생한 경우 상세한 오류 메시지를 출력합니다.
URL 중복 확인 함수: url_exists()
함수는 특정 게시물 URL이 postings
테이블에 이미 존재하는지 확인합니다. 이 함수는 SQL 쿼리를 사용하여 해당 URL의 존재 여부를 검사하고, 결과에 따라 True
또는 False
를 반환합니다.
게시물 수집 함수: fetch_recent_posts()
함수는 특정 사용자의 최근 게시물을 Steemit API를 통해 가져옵니다. 게시물의 메타데이터를 파싱하여 태그 목록을 추출하고, 특정 태그가 포함된 경우 main_tag
를 kr-m2e
로 설정하는 로직이 포함되어 있습니다. 중복된 URL이 존재하면 이를 건너뛰고, 게시물의 정보를 post_list
에 저장합니다.
사용자 목록 가져오기 함수: get_users()
함수는 데이터베이스에서 활성화된 사용자 목록을 가져옵니다. 활성화된 사용자(is_active = 'Y'
)의 ID를 SQL 쿼리를 통해 조회하고, 결과를 반환합니다.
게시물 데이터베이스 삽입 함수: insert_posts_to_db()
함수는 DataFrame 형식의 게시물 데이터를 데이터베이스의 postings
테이블에 삽입합니다. 각 게시물의 필드를 SQL 쿼리를 사용해 삽입합니다.
메인 로직: 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)