간만에 VirtualBox 관련 포스팅을 해본다 ^^

 

Host OS: Windows 11

Guest OS: Ubuntu 20.04

VirtualBox: 버전 7.1.4 r165100 (Qt6.5.3)

 

부팅할 때의 화면이다.

사용에는 별다른 이슈는 없는 상태이다.

 

 

GuestOS 켤 때마다 나오는 마음에 들지 않는 메시지를 전부 없애고 싶다!!!

 

RDRAND gives funky smelling output, might consider not using it by booting with "nordrand"

 

AMD CPU 사용에 따른 문제인 것으로 보이는데, 아직 명확한 해결법을 찾지 못해서 지금은 Skip!!!

어짜피 사용에 있어서는 문제가 없는 것이기에...

 

 

그럼 다음 문제를 살펴보자.

 

VirtualBox Error 'drm:vmw_host_printf [vmwgfx]] *ERROR* Failed to send host log message'

 

 

이건 확실히 에러 메시지다!

디스플레이 설정을 바꿔주면 해결이 된단다.

 

 

"VBoxVGA"로 설정을 바꿔주면 된단다.

경고 메시지가 보이기는 하는데, 필요에 따라 사용할 수 있는 옵션이기에 그냥 선택하면 된다.

 

 

확실히 사라졌다!!!

 

반응형

개인적으로 뭘 해보고자 하는게 있어서 요즘 잠깐 잠깐 만들고 있는 프로젝트가 있다.

- NAVER API를 이용해서 블로그 검색하기 (with Python)

 

그런데, 검색된 블로그 포스팅들을 화면에 출력만 할 것이 아니라 저장을 해놓고 싶은데,

그냥 JSON 형태로 저장하거나 CSV 형태로 저장하기엔 싫어서 SQLite를 이용해보려고 한다.

 

SQLite에 대한 기본적인 사항은 다음 포스팅을 참고하기 바란다.

- SQLite에 대해서 알아보자

 

 

0. 개발 환경

- OS: Ubuntu 20.04

- Lang: Python 3.10.9

 

 

1. 기본 코드

블로그 포스팅을 검색하는 코드는 이미 아래와 같이 살펴봤다.

- NAVER API를 이용해서 블로그 검색하기 (with Python)

 

 

2. SQLite 활용

기본 작성한 코드에서 이것 저것 좀 많이 업그레이드 시켰다.

 

에러메시지는 log로 남기도록 했고,

태그를 지우는 것은 BeautifulSoup 패키지를 활용하도록 했다.

 

import requests
import os
import sqlite3
from bs4 import BeautifulSoup
import logging

# 로깅 설정
logging.basicConfig(level=logging.ERROR)

class BlogPost:
    def __init__(self, title, description, link, postdate):
        self.title = self.clean_html(title)
        self.description = self.clean_html(description)
        self.link = link
        self.postdate = postdate

    def clean_html(self, text):
        if text:
            return BeautifulSoup(text, "html.parser").get_text()
        return ""

    def __str__(self):
        return f"Title: {self.title}\nDescription: {self.description}\nURL: {self.link}\nPost Date: {self.postdate}\n"

class NaverBlogSearcher:
    def __init__(self):
        self.client_id = os.getenv("NAVER_CLIENT_ID")  # 환경변수에서 Client ID 읽어오기
        self.client_secret = os.getenv("NAVER_CLIENT_SECRET")  # 환경변수에서 Client Secret 읽어오기

        # 환경변수 값이 없을 때 예외 처리
        if not self.client_id or not self.client_secret:
            logging.error("NAVER_CLIENT_ID and NAVER_CLIENT_SECRET must be set as environment variables.")
            raise ValueError("NAVER_CLIENT_ID and NAVER_CLIENT_SECRET must be set as environment variables.")

    def get_blog_posts(self, query, display=10):
        # 요청 URL 및 헤더 구성
        url = f"https://openapi.naver.com/v1/search/blog.json?query={query}&display={display}&sort=date"
        headers = {
            "X-Naver-Client-Id": self.client_id,
            "X-Naver-Client-Secret": self.client_secret,
        }

        # 네이버 API에 요청 보내기
        try:
            response = requests.get(url, headers=headers)
            response.raise_for_status()
        except requests.exceptions.RequestException as e:
            logging.error(f"Network error occurred: {e}")
            return []
        
        if response.status_code == 200:
            return self.parse_response(response.json())
        else:
            print(f"Error: {response.status_code}, {response.text}")
            return []

    def parse_response(self, data):
        # 응답 데이터 파싱
        blogs = data.get("items", [])
        blog_posts = [BlogPost(blog.get("title"), blog.get("description"), blog.get("link"), blog.get("postdate")) for blog in blogs]
        return blog_posts

class BlogPostDBHandler:
    def __init__(self, db_name="blog_posts.db"):
        self.db_name = db_name
        self.create_table()

    def create_table(self):
        # SQLite 데이터베이스 연결
        with sqlite3.connect(self.db_name) as conn:
            cursor = conn.cursor()

            # 테이블 생성
            cursor.execute('''
                CREATE TABLE IF NOT EXISTS BlogPosts (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    title TEXT,
                    description TEXT,
                    link TEXT UNIQUE,
                    postdate TEXT
                )
            ''')

    def save_to_db(self, blog_posts):
        # SQLite 데이터베이스 연결
        with sqlite3.connect(self.db_name) as conn:
            cursor = conn.cursor()

            # 데이터 삽입 (중복 확인)
            for post in blog_posts:
                cursor.execute('''
                    SELECT COUNT(*) FROM BlogPosts WHERE link = ?
                ''', (post.link,))
                if cursor.fetchone()[0] == 0:
                    cursor.execute('''
                        INSERT INTO BlogPosts (title, description, link, postdate) 
                        VALUES (?, ?, ?, ?)
                    ''', (post.title, post.description, post.link, post.postdate))

    def get_all_posts(self):
        # SQLite 데이터베이스 연결
        with sqlite3.connect(self.db_name) as conn:
            cursor = conn.cursor()

            # 저장된 블로그 포스팅의 모든 필드 가져오기
            cursor.execute('''
                SELECT title, description, link, postdate FROM BlogPosts
            ''')
            rows = cursor.fetchall()

        # 필드명을 갖는 dict 형태로 변환
        posts = [
            {"title": row[0], "description": row[1], "link": row[2], "postdate": row[3]} 
            for row in rows
        ]
        return posts

if __name__ == "__main__":
    # "동탄" 검색어로 최신 블로그 정보 가져오기
    try:
        searcher = NaverBlogSearcher()
        blog_posts = searcher.get_blog_posts("동탄")
        
        # 검색 결과 출력
        # for post in blog_posts:
        #     print(post)
        
        # 검색 결과를 SQLite 데이터베이스에 저장
        db_handler = BlogPostDBHandler()
        db_handler.save_to_db(blog_posts)

        # 저장된 블로그 포스팅의 제목 출력
        posts = db_handler.get_all_posts()
        for post in posts:
            print(f"Title: {post['title']}")
    except ValueError as e:
        print(e)

 

TABLE이 존재하지 않으면 create 하도록 했으며,

블로그 포스팅을 insert 하기 전에 기존에 있는지 미리 확인한 후에 없을 때에만 insert 하도록 했다.

 

아직은 PoC 성격의 스크립트 이기에,

SQLite에 저장한 이후에 전체 포스팅 목록을 SQLite로부터 읽어와서 제목들을 출력하도록 했다.

 

그리고,

추가 설치해야할 패키지들이 있기에 requirements.txt 파일도 업데이트 되었다.

 

beautifulsoup4==4.12.3
requests==2.32.3

 

 

3. Execute

실행 결과도 확인 해보고, database 내용도 살펴보자.

 

> dotenvx run -f .env -- python main.py

 

 

이렇게 저장을 하면 database 파일이 하나 생성되는 것을 확인할 수 있다.

- blog_posts.db

 

 

 

4. Check

db 파일을 확인해보기 위해서 "DB Browser for SQLite"를 사용해보겠다.

 

 

설치는 그냥 패키지 설치하면 된다.

 

> sudo apt-get install sqlitebrowser

 

잘 설치되어 있는 것을 볼 수 있다.

 

 

실행하고, "데이터베이스 열기" 하고 db 파일을 선택해주면 된다.

 

 

"데이터 보기" 탭을 선택하면 저장된 내역을 볼 수 있다.

 

 

 

오늘은 여기까지~

반응형

내 블로그를 오랫동안 보셨던 분들이라면

내가 git을 상당히 오랫동안 다뤄왔다는 것을 아실 수 있을 것이다.

 

git 자체에 대한 것은 물론이고

호스팅을 위한 gitolite 부터 시작해서, Gerrit 이라던지 지금은 GitHub 까지 ...

 

그런데, 요즘은 워낙에 GitHub가 시장을 평정해버려서

git 관련한 대부분의 것들의 표준이 GitHub에서 어떻게 하는지...가 되어버렸다.

 

그리고 많은 분들이 GUI 기반의 git 도우미들을 사용하곤 한다.

 

하지만, git 초심자를 좀 벗어나게 되면

많은 분들이 CLI 방식으로 git을 사용하게 된다. (사실 이게 더 편한 것 같다!)

 

CLI 방식으로 git을 사용할 때 가장 아쉬운 것이 코드를 살펴볼 때인데...

이걸 해결해주는(?!) 재미있는 도구가 있어서 한 번 설치해봤다.

 

 

1. Delta

의외로 문서화가 잘되어 있어서 깜짝 놀랐다.

- https://dandavison.github.io/delta/

 

 

 

감사하게도 MIT 라이선스이다.

Star 갯수도 ... 우와 !!!

- https://github.com/dandavison/delta

 

 

 

 

2. Installation

지원해주는 설치 플랫폼을 보고선 또 한 번 깜짝 !!!!!

- https://dandavison.github.io/delta/installation.html

 

 

 

Debian 계열은 release 페이지에 가서 다운로드 받으라네...

- https://github.com/dandavison/delta/releases

 

 

 

잉?! 버전은 아직도 v0.18.2 ... 내 생에 v1.0을 보지는 못할 것 같네 ^^ ㅋㅋ

다운로드 받아서 설치하자.

> wget https://github.com/dandavison/delta/releases/download/0.18.2/git-delta_0.18.2_amd64.deb

> sudo dpkg --install ./git-delta_0.18.2_amd64.deb

 

금방 설치된다.

 

 

3. Environment

'.gitconfig' 파일에 설정을 해줘야 한다.

 

 > nano ~/.gitconfig

 

[core]
    pager = delta

[interactive]
    diffFilter = delta --color-only

[delta]
    navigate = true  # use n and N to move between diff sections
    dark = true      # or light = true, or omit for auto-detection
    line-numbers = true
    side-by-side = true
    
[merge]
    conflictstyle = zdiff3

 

 

 

4. Just Do It

간단히 살펴보기 위해서 commit 확인 후 'git show'를 해봤다.

 

❯ git log --oneline --graph

❯ git show 2a9249a

 

 

line number 뿐만 아니라 전체적으로 GUI 못지 않은 깔끔함 !!!

 

심지어 Syntax highlighting도 잘 해준다.

 

 

우왕~

내 기본 환경에 포함시켜놔야 할 유틸리티이다 !!!

 

자세한 사용법은 공식 사이트를 참고하세요~

 

반응형

여기에서 해보고 싶은 것은 "동탄" 키워드가 들어간 최근 블로그를 검색해서

그 제목과 본문 내용, 링크 값을 얻어오는 코드를 작성해보고자 한다.

 

0. 개발 환경

- OS: Ubuntu 20.04

- Lang: Python 3.10.9

 

 

1. 준비

① NAVER API를 사용하기 위해 API 키를 생성하자.

    - NAVER 서비스 API 사용 준비

 

② 환경변수를 다루기 위해서 dotenvx를 사용해보자.

    - 아는 사람만 쓴다는 Config 관리자 - dotenv, dotenvx

 

 

2. 코드 작성

일단 API 호출을 위해 requests 패키지를 사용하기로 했다.

 

[ requirements.txt ]

requests==2.32.3

 

가급적 클래스 구조로 작성해봤다.

[ main.py ]

import requests
import os

class BlogPost:
    def __init__(self, title, description, link):
        self.title = title.replace("<b>", "").replace("</b>", "")
        self.description = description.replace("<b>", "").replace("</b>", "")
        self.link = link

    def __str__(self):
        return f"Title: {self.title}\nDescription: {self.description}\nURL: {self.link}\n"

class NaverBlogSearcher:
    def __init__(self):
        self.client_id = os.getenv("NAVER_CLIENT_ID")  # 환경변수에서 Client ID 읽어오기
        self.client_secret = os.getenv("NAVER_CLIENT_SECRET")  # 환경변수에서 Client Secret 읽어오기

        # 환경변수 값이 없을 때 예외 처리
        if not self.client_id or not self.client_secret:
            raise ValueError("NAVER_CLIENT_ID and NAVER_CLIENT_SECRET must be set as environment variables.")

    def get_blog_posts(self, query, display=10):
        # 요청 URL 및 헤더 구성
        url = f"https://openapi.naver.com/v1/search/blog.json?query={query}&display={display}&sort=date"
        headers = {
            "X-Naver-Client-Id": self.client_id,
            "X-Naver-Client-Secret": self.client_secret,
        }

        # 네이버 API에 요청 보내기
        response = requests.get(url, headers=headers)
        
        if response.status_code == 200:
            return self.parse_response(response.json())
        else:
            print(f"Error: {response.status_code}, {response.text}")
            return []

    def parse_response(self, data):
        # 응답 데이터 파싱
        blogs = data.get("items", [])
        blog_posts = [BlogPost(blog.get("title"), blog.get("description"), blog.get("link")) for blog in blogs]
        return blog_posts

if __name__ == "__main__":
    # "동탄" 검색어로 최신 블로그 정보 가져오기
    try:
        searcher = NaverBlogSearcher()
        blog_posts = searcher.get_blog_posts("동탄", 3)
        for post in blog_posts:
            print(post)
    except ValueError as e:
        print(e)

 

환경 변수도 작성했다.

[ .env ]

NAVER_CLIENT_ID=""
NAVER_CLIENT_SECRET=""

 

 

3. 실행

dotenvx를 사용해서 실행했다.

> dotenvx run -f .env -- python main.py

 

나름 잘 진행되었다 !!! (스스로 뿌듯~)

반응형

옛날에 한 번 포스팅을 했었는데, 업데이트가 필요해서 다시 작성해본다.

- https://www.whatwant.com/entry/Naver-API-사용-준비하기

 

1. NAVER Developers

네이버 개발자 사이트에 방문해서 로그인까지 진행하자.

- https://developers.naver.com/

 

NAVER Developers

 

 

2. 서비스 API

일단 여기에서는 "검색"을 위한 API 사용 신청하는 과정을 기준으로 설명해보겠다.

 

서비스 API

 

상단 메뉴에서 "서비스 API - 검색" 메뉴를 선택해보자.

 

검색

 

3. 오픈 API 이용 신청

처음 사용자는 이용약관 동의나 계정 설정 등을 요구할 수도 있다.

나는 예전에 신청했던 이력이 있어서인지 바로 애플리케이션 등록 창으로 이동되었다.

 

애플리케이션 등록

 

"애플리케이션 이름" 적절하게 창작하면 되고,

"사용 API"는 검색을 선택했다. 각자 필요한 것 추가해도 된다.

 

"비로그인 오픈 API 서비스 환경"은 사용할 상황에 따라 잡아주면 되는데,

나는 일단 "WEB 설정"으로 하고 적당한 도메인 정보를 넣어주었다.

 

4. 애플리케이션 정보

사용할 때 필요한 "Client ID"와 "Client Secret"이 발행되었다.

 

애플리케이션 정보

 

하루에 2만5천번 호출할 수 있네!!!!

 

반응형

PDF, DOCX, PPTX 같은 문서 파일이나 이미지, HTML 등을

여러 용도로 사용하기 좋게 Markdown, Json 형식으로 변환해주는 도구를 찾았다.

 

사실 너무 유명해서 알만한 사람들은 이미 다 알고 있는 것 같지만 ^^

- https://ds4sd.github.io/docling/

 

Docling

 

MIT 라이선스인 오픈소스 프로젝트이다.

- https://github.com/DS4SD/docling

 

GitHub

 

파이썬 패키지이기 때문에 파이썬 개발 환경이 필요하다 ^^

 

다양한 버전의 파이썬 활용을 위한 pyenv 환경이 필요하다면 아래 포스팅을 참고하기 바란다.

- https://www.whatwant.com/entry/pyenv

 

1. Installation

가상환경 생성 후 docling 설치까지 진행해보자.

cuda, torch 및 의존성 있는 패키지들이 엄청 많이 설치되고 용량도 커서 시간도 좀 걸린다.

 

> python -m venv .venv

> source .venv/bin/activate

> pip install docling

 

2. Usage

공식적으로 알려주는 샘플 코드를 작성 후 실행해보자.

- https://ds4sd.github.io/docling/usage/

 

from docling.document_converter import DocumentConverter

source = "https://arxiv.org/pdf/2408.09869"  # PDF path or URL
converter = DocumentConverter()
result = converter.convert(source)
print(result.document.export_to_markdown())  # output: "### Docling Technical Report[...]"

 

우쒸... GPU 환경이 필수인가보다.

그런데, Colab 환경에서도 정상적인 결과가 나오진 않는다.

 

 

 

3분 정도가 소요되어 정상 완료한 것처럼 나오지만,

출력되는 내용이 없다.

 

잘못된 것인줄 알았는데, 출력 결과 전체 화면 보기를 하니 제대로 보였다.

 

 

엔터(줄바꿈) 없이 너무 길게 string이 나와서 출력 결과가 안보였던 것이다.

뭐 일단, Colab으로 테스트를 해볼 수는 있었다.

 

음... 재미있긴 하지만,

속도가 그다지 빠르지 않아서... (A100이나 H100 환경에서는 조금 빠르려나!? 함 해볼까...!?)

자주 사용할 아이는 아닐 것 같다.

반응형

리눅스를 좋아하는, 특히 커맨드-라인 인터페이스를 좋아하는 분들은

각자 나름의 커스터마이징된 환경 꾸미는 것을 좋아하는 경우가 많다.

 

우분투와 같은 리눅스를 설치하면 기본적으로 bash가 설치되지만

리눅스를 많이 사용하는 분들은 zsh으로 바꿔서 사용하는 것 처럼 ... ^^

 

이처럼 커맨드-라인을 사용하는 환경을 커스터마이징 할 때

자주 언급되는 유틸리티 중 하나가 바로 "fzf (command-line fuzzy finder)" 이다.

- https://junegunn.github.io/fzf/

 

fzf

 

버전이 아직도 v0.56.3 에 머물러 있음에도 (ㅋㅋㅋ 절대 v1.0 릴리즈를 하지 않겠다는 의지?!)

Star 갯수에서 유추할 수 있듯이 global에서 유명한 툴이다.

 

GitHub

 

우리나라에서 더더욱 유명한 이유는 !!!

개발자가 우리나라 분이시다 !!!

 

Junegunn Choi

 

푸릇푸릇한 잔디밭도 대단하고,

Sponsors 목록.... 오!!!!! 국내 개발자 중에 저 정도의 Sponsors 목록을 갖고 있는 분은 처음 봤다!!!

 

 

1. Installation

Homebrew를 이용해서 설치하는 것이 기본적으로 권장하는 방법이고,

운영 체제에 따라서 편하게 설치할 수 있는 다양한 방법을 제공해주고 있지만

(개인적인 취향으로) 지원만 해준다고 하면 git clone 방식으로 설치하는 것을 권장한다.

- https://github.com/junegunn/fzf?tab=readme-ov-file#using-git

 

git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install

 

버전 업그레이드가 필요하다고 하면 다음과 같이 할 수 있다.

- https://github.com/junegunn/fzf?tab=readme-ov-file#upgrading-fzf

 

cd ~/.fzf && git pull && ./install

 

 

2. shell integration (zsh)

설치 과정에서 자동으로 셋업 해주기는 하는데,

혹시 잘 안되는 경우가 있을까 하여 추가적으로 확인하는 과정을 넣어보았다.

 

가이드 문서에는 다음과 같이 되어있다. (zsh이 아닌 경우 아래 링크 참조)

- https://github.com/junegunn/fzf?tab=readme-ov-file#setting-up-shell-integration

 

# Set up fzf key bindings and fuzzy completion
source <(fzf --zsh)

 

하지만, 설치 과정에서 자동으로 반영해준 내역은 조금 다르다.

 

.zshrc

 

어!? 뭔가 파일을 불러오네!?

그러면, 그 파일을 추가로 확인해보자.

 

.fzf.zsh

 

가이드 문서에 있는 내용과 결국은 같기는 한데, 경로 추가도 포함되어 있어서 더 좋은 것 같다.

 

뭐, 여하튼 이렇게 잘 반영되어 있으면 좋고,

혹시 반영이 안되어 있으신 분은 위 내용 참고해서 반영해주면 된다.

 

그런 후에, 터미널을 재시작 하던지 아니면 "source ~/.zshrc" 하던지 하고

fzf가 잘 설치되어 있는지 확인해보면 된다.

 

version

 

3. Simple Usage

nano 에디터를 이용해서 어떤 파일을 편집하고 싶다고 해보자.

그런데, 파일 이름은 알고 있지만 어느 경로에 있는지 까지는 모른다거나 타이핑하기 귀찮은 상황이라면...

 

> nano $(fzf)

 

제일 아래 부분에서 원하는 파일 이름을 하나씩 타이핑하면

위에 추천하는 파일들을 실시간으로 업데이트 하면서 보여준다.

 

원하는 파일이 있으면 화살표 키를 이용해서 고른 다음에 엔터를 치면 된다.

 

4. Short-Key

단축키도 지원을 해준다.

커맨드-라인 상태에서 "Ctrl + R" 키를 누르면 이전에 사용했던 이력을 보여준다.

 

 

현재 디렉토리 기준으로 하위 디렉토리를 포함한 파일들의 목록을 보여주는 것은 "Ctrl + T"이다.

 

 

이외에도 다양한 기능들을 제공해주고 있는데...

한 번에 다 공부하기에는 부담스러워서 이번에는 일단 여기까지~~~

 

더 알고 싶으신 분들은 공식 문서를 참고하기 바란다.

- https://github.com/junegunn/fzf?tab=readme-ov-file#usage

 

반응형

그동안 블로그 포스팅을 하면서

티스토리에서 코드 출력을 위해 기능을 제공해주고 있다는 것을 지금까지 몰랐다.

 

이런 바보!

 

이렇게 예쁘게 출력을 해준다.

from transformers import pipeline, AutoTokenizer, AutoModelForSequenceClassification

tokenizer = AutoTokenizer.from_pretrained("maywell/Synatra-42dot-1.3B")
model = AutoModelForSequenceClassification.from_pretrained("maywell/Synatra-42dot-1.3B")

classifier_ko = pipeline("sentiment-analysis", model=model, tokenizer=tokenizer)

classifier_ko("정말 니가 싫어")

 

그런데,

깔끔하긴 한데... 뭔가 좀 아쉽다.

이게 훨씬 더 예쁜 것 같다!!!! 

 

 

소스 코드를 이렇게 예쁜 이미지로 만들어 주는 사이트를 소개해보고자 한다 ^^

- https://ray.so/

 

사이트에 방문해서 소스 코드를 붙여 넣기 하고,

하단의 옵션을 적절하게 만져주면 된다.

 

예쁘게 만들었다면,

오른쪽 상단의 Export Image를 통해 그림파일로 내려 받으면 된다.

 

 

해당 사이트는 사실 더 많은 기능을 제공해 준다.

 

정말 가볍고, 깔끔하고, 유용한 기능을 많이 제공해주는 아름다운 사이트이다.

 

[추가] 로컬에서 띄울 수도 있다.

- https://github.com/raycast/ray-so

반응형

+ Recent posts