프로덕트

아직 프로덕트가 없습니다.

아티클

전체 보기
Jinwoo Hwang

Jinwoo Hwang

간단한 논문 서치용 크롤러

개발로그를 어쩌다 보니 또 방치를 해버렸는데...고사기간만 끝나면 한 번 재개해 보겠습니다~ 문어발 식으로 사는 것의 폐해 중 하나 같네요ㅠㅠ

오늘은 졸업논문 때문에 잠시 시간 내서 짰던 간단한 Google Scholar 크롤러 코드 하나 던지고 가는 거로 퉁치려고 합니다 👀

사실 대단한 것도 아니고, 비슷한 거, 더 좋은 거 꽤 많지만 시간이 없는데 논문 리스트업 해두고 슥 볼 때 쓰고 싶은 분들이 또 있으실까 싶어서... (차주에 전 교수님 미팅이라 ㅎㅎ)

하나의 파일에 그냥 간단하게 짰고요, 크롬드라이버 위치 정도만 잘 확인해서 고쳐 써 주시면 될 것 같아요. 크롬드라이버 + 구글크롬 stable 버젼 설치 관해선 구글링하면 그래도 꽤 잘 나와 있으니 일단 생략할게요.

함수에 몇 페이지까지 크롤링하고 싶은지, 어떤 내용을 검색하고 싶은지 넣고 실행해 주시면 됩니다~ 셀레늄이 구글 크롬 창 띄우는 게 싫으시면 --headless 옵션 주석을 풀어주시면 안 보이게 돌아갑니다.

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
import json
import time

def crawl(search_term: str, num_pages: int):
    # Setting up Selenium WebDriver
    options = webdriver.ChromeOptions()
    options.add_argument("--disable-background-timer-throttling")
    options.add_argument("--disable-backgrounding-occluded-windows")
    options.add_argument("--disable-renderer-backgrounding")
    # options.add_argument('--headless')  # Uncomment to run headless

    service = Service('/opt/homebrew/bin/chromedriver')

    pagination_size = 10
    result_dict = {}

    for it in range(num_pages):
        driver = webdriver.Chrome(service=service, options=options)
        url = f'https://scholar.google.com/scholar?start={pagination_size * it}&q={search_term.replace(" ", "+")}&hl=ko&as_sdt=0,5'
        driver.get(url)

        WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, '//h3/a[@href]')))
        results = driver.find_elements(By.XPATH, '//h3/a[@href]')
        for result in results:
            print(result.text, result.get_attribute('href'))
            result_dict[result.text] = result.get_attribute('href')
    with open('results.json', 'w') as json_file:
        json.dump(result_dict, json_file, indent=4)

    driver.quit()
    print("Data has been successfully saved to 'results.json'.")


if __name__ == '__main__':
    crawl("parkinson's disease turn detection", 5)
5
0
Jinwoo Hwang

Jinwoo Hwang

개발일지 (엔티티)

엔티티는 무엇인가?

엔티티는 핵심 로직이나 모델 구조를 기술한다고 혼자 나름대로 정리해 봤다.

User, 유저 정보를 담고 User와 연결된 Profile, 플랫폼 상에서 판매되는 Product 등 모두 엔티티로 정의할 수 있다.

사실 Django를 하다 왔다 보니 그냥 속으로는 model이랑 똑같은 거! 정도로 정리하고 있다. 물론 Django는 별도의 ORM 없이 behind-the-scene에서 CharField, TextField, BooleanField 등으로 선언한 필드들을 table 상의 column으로 바꿔준다. Node.js는 높은 자유도를 대가로 번거로움은 수반되는 듯하다.

개인적인 엔티티 파일 네이밍 컨벤션

src/data/entities/{name}.entity.ts 형태로 지금 네이밍 컨벤션을 잡은 상태다.

ORM과의 연관성

필자는 TypeORM을 쓰고 있다. TypeORM은 엔티티를 정의한 파일들을 파싱해서 DB schema를 구성할 수 있게 도와준다.

How to ORM

사실 TypeORM에는 자동으로 마이그레이션을 짜주는 좋은 기능이 있다.

Django로 치면 python manage.py makemigrations 정도 되는 기능이다. 다만, 매우 별로다...

npm run typeorm -- migration:generate src/data/migrations/initial 지금 이런 형태의 커맨드를 사용하고 있는데, 뒤에 오는 argument는 마이그레이션을 어디에 생성하고 싶은지 기술하는 용도이다.

실제로 생성되는 팡리 이름은 initial이 아니라, 타임스탬프가 앞에 붙어서 나온다. 실제로 TypeORM 단에서 마이그레이션을 실행할 때 타임스탬프를 읽어서 순서를 정렬한다고 한다.

generate는 스키마를 읽어들여서 자동으로 마이그레이션을 생성해주는 고마운 기능이다.

아주 큰 맹점을 하나 가지고 있는데, ALTER를 못한다. DROP하고 나서 ADD 하는 식으로 무식하게 스키마를 수정하는데 기존에 존재하던 column의 정보가 날아간다.
(varchar 10에서 20으로 바꾼다고 해당 칼럼 내용이 다 날아가버리는 그런 상황)

그래서 이제는 번거롭더라도 create 커맨드를 써서 수동으로 마이그레이션을 생성하려고 한다. 이에 관해선 좀 더 알아보고 글을 적겠다. 사실 임시방편으로는 generate로 생성한 마이그레이션을 수작업으로 검수해서 쓰고 있다.

python manage.py migrate에 준하는 커맨드도 있다.

npm run typeorm -- migration:run를 실행하면 마이그레이션을 일괄로 다 돌려준다.

아직까진 Django처럼 마이그레이션 단위로 fine-grained control을 하게 해주는 방법은 못 찾았다...

롤백하려면 npm run typeorm -- migration:revert 커맨드를 사용하면 된다. 가장 최근 마이그레이션을 롤백한다.

2
0
Jinwoo Hwang

Jinwoo Hwang

개발로그 (시작)

왜 이걸 적는가?

필자는 개발 외주팀에서 백엔드 리드를 맡고 있다.

백엔드 경력이 특출나거나 시니어 엔지니어라서 그런 건 아니지만 어쨌든 꽤나 large-scale의 시스템을 디자인하게 되었다. 원래 기술 스택은 Django이나, Node.js + TypeScript로 바꾸게 되면서 거의 2년만에 TypeScript를 첨 만져보게 되었다.

당연히 엄청 헤매고 있고, 지금도 똑바로 못하는 게 태반이다. 그렇기에 어떻게 하는 게 좋은지 스스로 프로세스나 컨벤션을 정리하기 위해서 여기 상에서 내용을 정리해 보고자 한다. 혼잣말에 가까운 개발일지...

5
4

포스트

아직 포스트가 없습니다.