현재 구조로는 Custom Repository Implementation을 사용 중

많은 필터가 쿼리문에 들어가기 때문에 기본적으로 제공하는 JPA를 사용할 수 없게 되어 사용하게 되었습니다.

 

문제점

 

전체를 select해서 조회하는 쿼리를 작성

전체 데이터와 전체 개수를 count하는 2개의 쿼리를 생성

 

아래코드는 where절을 뺀 코드 입니다. 상황에 맞게 추가하시면 될 것 같습니다.

필터값에 맞는 데이터를 List로 출력, 전체 데이터의 개수 2가지를 출력하는 메서드 입니다.

 

CustomRepositoryImpl.java

List<AllDataEntity> allDataEntities = jpaQueryFactory
        .selectFrom(qAllDataEntity)
        .offset(pageable.getOffset())
        .limit(pageable.getPageSize())
        .fetch();

long contentCount = jpaQueryFactory
        .from(qAllDataEntity)
        .fetch().stream().count();

 

 

위와 같이 데이터를 조회하니 2초가량이 나옵니다.

코드를 보고 문제점을 파악하신 분들도 있을 수 있습니다.

 

하나하나 짚어보겠습니다.

 

1. fetch().stream().count()

 

  • fetch()는 조건에 맞는 데이터를 전부 DB에서 가져와 메모리로 로드하며,
  • stream().count()는 이 메모리상에 로드된 데이터에서 조건에 맞는 항목을 계산하여 개수를 반환합니다.

즉, 정리하면 데이터 전체를 DB에서 가져와 메모리 로드 후 계산을 진행하는 방식

메모리 사용량이 증가, 성능저하 발생 우려

 

 

long contentCount = jpaQueryFactory
        .select(qAllDataEntity.count())
        .from(qAllDataEntity)
        .fetchOne();

if (contentCount == null) {
    contentCount = 0L;
}
// null처리는 센스 ㅎㅎ

 

 

2. count()

 

  • DB에서 직접 데이터를 세는 방법이며, 1번과 다른점은 DB에서 직접 가져오는 방식이기 때문에 메모리 사용량을 감소할 수 있습니다!

 


 

추가로 fetch는 리스트로 값이 반환되기 때문에 2번씩 같은 함수에서 사용하는 건 메모리 낭비와 DB과부하의 우려가 있습니다.

 

필터값과 상관없이 저는 DB에 저장된 모든 개수를 구해야 하기 때문에 fetchOne을 사용하여 count값만 나타내주었지만

fetch에서 출력된 값을 .size()함수를 사용하여 그대로 뽑아내는 것도 방법입니다.

long contentCount = allDataEntities.size();

 

 

이번에 자동화 프로그램에 흥미를 느껴 사무자동화 프로그램을 몇 개 개발해보게 되었습니다.

간단한 자동화 업무나 필요한 부분들 말씀해주시면 요청사항에 맞게 제작해드려요.

 

  • 반복되는 업무를 자동화 하고 싶으신 분!
  • 손으로 하나하나 파일을 바꾸는게 힘드신 분!
  • 웹에서 매일 정보를 탐색하기 귀찮으신 분!

 

정말 간단한 프로그램 디자인에 기능만 입힌거니 디자인은 부족해도 양해바랍니다

 

텍스트 위치나 버튼위치는 그림으로 섹션 따서 주시면 최대한 반영해 드리도록 하겠습니다!

 

데이터 수집 엑셀 추출 등등

 

파일은 exe파일로 드려요! 안타깝지만 제가 windows사용자라 macOS는 어려울 것 같습니다 ㅠㅠ

 

예시로 만들어본 파일 입니다. 이건 정말 간단하게 만든거라 더 좋은 퀄리티도 가능합니다~

 

요구사항 예시

ex1) 머신러닝 데이터셋 압축파일을 관리하다 보니 압축해제 할 일이 많습니다.

폴더 내 압축파일들을 일괄해제하여 zip파일명으로 폴더를 생성하는 프로그램 부탁드립니다.

 

ex2) 업무 파일들이 폴더별로 나누어져 있는데 폴더를 하나하나 들어가 삭제 하는게 번거롭습니다.

폴더는 유지하면서 폴더 안 파일만 삭제하도록 하는 프로그램 부탁드립니다.

말로는 한계가 있을 수 있으니 전 후 비교사진이나 파일을 주시면 좋을 것 같습니다!

 

완성본 받으시면

(기능), (후기)로 댓글에 남겨주시면 될 것 같아요!

ex) 폴더 내 일괄 압축해제 프로그램, 너무 작동 잘됩니다 감사해용!

 

편하게 업무하고자 하는 마음이니 다 같이 힘내도록 해요 감사합니다 ㅎㅎ

댓글이나 오픈채팅으로 문의주세용~~

 

오픈채팅 링크

코드: 0429

https://open.kakao.com/o/sVJvYZjh

 

무료 자동화 프로그램 개발해드려용

 

open.kakao.com

 

완성본은 여기 티스토리에 하나하나 남겨질 것 같습니다! 원치 않으시다면 올리지 않도록 하겠습니다

많은 문의 주세요! 감사합니다! 

 

웹 서버 운영중 갑자기 MySQL에서 쿼리가 멈추는 문제가 발생 

SHOW FULL PROCESSLIST;

 

로 상태를 점검해봤을 때 state에 Waiting for table metadata lock 이슈 발생

 

processlist에서 출력 된 lock걸린 id를 찾아

KILL {id};

 

를 진행해보았지만 다시 lock 반복 됨.

 

인터넷에 있는 방법을 다 사용해봤지만 해결이 안됨


해결방안

원인 파악을 위해 putty로 DB서버에 접속해보았다.

df -h

 

로 디스크 용량 부터 파악하였다.

 

사용중인 디스크가 용량이 98%를 차지하고 있는게 눈에 들어와 좀 더 딥하게 찾아보니 로그파일이 쌓여 DB자체에서 LOCK을 걸어둔걸로 예상되었다.

 

sudo du -ah /var/log/mysql | sort -rh

 

mysql 로그쪽 본 결과 로그파일만 엄청 쌓여 있는걸 확인되었다 이 부분을 삭제하도록한다.

 

sudo rm -f /var/log/mysql/mysql-bin.*

 

바이너리 로그파일을 전부 삭제하는 명령어다 삭제하기 전 항상 조심!!

 

나의 경우에는 로그 파일이지만 파일이 사람마다 다를 수 있으니 로그 파일 정리는 수시로 하는게 권장되는 듯 하다!

 

이렇게 삭제해주고 나니 해결되었다 너무 돌고 돌다가 간단한 에러에 시간을 많이 잡아먹었다..

 

관리가 귀찮다면 기본으로 설정되어있는 logrotate설정을 건드려서 짧은 주기로 자주 삭제되도록 수정할 수 있다

이건 자신의 환경에 맞게 설정하면 될 듯 하다.

 

 

logrotate 설정 법은 구글 검색에 많이 나오니 아무 블로그만 봐도 자세히 나와있다.

https://velog.io/@yjj4899/ubuntu-mysql-db-logrotate-%EC%84%A4%EC%A0%95

 

ubuntu mysql db logrotate 설정

DB로그를 기간단위로 남겼을때 경험을 기록한다mysql의 설정 확인general_log_file : 로그파일 저장위치★위치에 따라 하단에 logrotate 설정에 참고★general_log 가 OFF라면 ON으로 바꿔준다.로그 저장방식

velog.io

 

크롤링과 스크래핑?

크롤링 (Crawling) 뉴스 기사 수집

크롤링은 웹사이트의 모든 페이지를 탐색하며 정보를 수집 구조화하는 과정을 의미합니다.

크롤러가 자동으로 웹사이트의 페이지 링크를 따라가며 데이터를 수집합니다.

스크래핑 (Scraping) 유튜브 구독자 좋아요 등 수집

스크래핑은 웹 페이지의 특정 데이터만 추출하는 작업을 의미합니다.

주로 특정 웹사이트에서 필요한 데이터만을 추출하여 가공하는 데 사용됩니다.

 

  • 크롤링: 웹사이트 전체 또는 다수의 페이지를 대상으로 데이터 수집
  • 스크래핑: 특정 페이지에서 필요한 데이터만 추출

 

스크래핑은 별점과 같은 특정 정보를 가져오는 것을 의미 

 

크롤링은 url을 이동하며 웹페이지를 조작해 데이터를 수집하는 것을 의미

 

url, 작성자, 날짜 등의 정보를 수집하기 위해선 Locator가 필요

Locator DOM 트리에서 특정 요소를 선택하는 데 사용

DOM 문서 객체 모델(Document Object Model) HTML, XML 문서를 표현하고 조작하는 데 사용되는 프로그래밍 인터페이스

 

Locator란 즉, CSS selector, Xpath를 가르킴

 

DOMtree란 html코드의 구조를 계층적으로 나타낸 것을 의미

*****HTML*****

<!DOCTYPE html>
<html>
<head>
    <title>Let's crawling</title>
</head>
<body>
    <h1>crawling Nice</h1>
    <p>crawling</p>
</body>
</html>

*****DOMTREE*****

html
├── head
│   └── title
│       └── "Let's crawling"
└── body
    ├── h1
    │   └── "crawling Nice"
    └── p
        └── "crawling"

 

DOMtree의 요소는 노드로 표시

  1. 루트 노드 (Root Node): 트리의 최상위 노드로, HTML 문서 전체를 나타냅니다.
  2. 요소 노드 (Element Node): HTML 요소를 나타내며, 트리의 가지(branch)에 해당합니다.
  3. 텍스트 노드 (Text Node): HTML 요소의 텍스트 내용을 나타냅니다.

DOMTREE형으로 구조화 하여 css xpath 원하는 요소를 찾는 것이 중요

 

css selector

 

css selector는 컴포넌트화된 환경에서 유일한 선택자를 찾기 어려움.

<div class="container">
    <div class="item">
        <h2>Title</h2>
        <p>Content</p>
    </div>
    <div class="item">
        <h2>text</h2>
        <p>item</p>
    </div>
</div>

 

div.item p -> 텍스트 기반으로 검색할 수 없어 2개의 p태그가 선택됨

이걸 xpath화 시키면

//div[@class="container"]/div[@class="item"]/h2[text()="text"]/following-sibling::p

2번째 p태그를 찾을 수 있음

<xpath>
<css selector>

 

부모 및 자식 선택

  1. ancestor: 현재 노드의 모든 조상 노드를 선택합니다.
  2. descendant: 현재 노드의 모든 후손 노드를 선택합니다.
  • XPath: XPath에서는 // 연산자를 사용하여 문서의 모든 위치에서 요소를 선택할 수 있습니다. 또한 ancestor 및 descendant 축을 사용하여 특정 요소의 조상 또는 후손 요소를 선택할 수 있습니다.
  • CSS 선택자: CSS 선택자는 > 및 공백을 사용하여 특정 요소의 직계 자식 또는 하위 요소를 선택할 수 있습니다. 그러나 CSS 선택자에는 조상요소를 선택하는 방법이 없습니다.
  • 직계 자식 선택: .parent > .child
  • 하위 요소 선택: .container .descendant

형제 선택

  1. following-sibling: 현재 노드의 다음 형제 노드들을 선택합니다.
  2. preceding-sibling: 현재 노드의 이전 형제 노드들을 선택합니다.
  • XPath: XPath에서는 following-sibling 및 preceding-sibling 축을 사용하여 현재 요소의 다음 형제 또는 이전 형제 요소를 선택할 수 있습니다.
  • CSS 선택자: CSS 선택자에서는 + 및 ~ 연산자를 사용하여 현재 요소의 다음 형제 요소를 선택할 수 있습니다. 그러나 이전 형제 요소를 선택하는 방법은 없습니다. (+ 는 다음 요소중 첫번째,  ~ 형제요소중 p 요소)

css selector -> p + span (이전 선택 X)

  • 다음 형제 선택: //p/following-sibling::span[1]
  • 이전 형제 선택: //span/preceding-sibling::p[1]  -> ??? 뭐가 선택될지

자식선택

  • XPath: XPath에서는 child 축을 사용하여 현재 요소의 모든 자식 요소를 선택할 수 있습니다.
  • CSS 선택자: CSS 선택자에서는 공백을 사용하여 현재 요소의 모든 하위 요소를 선택할 수 있습니다.

 

.container .child span:nth-child(2)

 

//div[@class="parent"]/div[@class="child"]/span[2]

or

//div[@class="parent"]/div[@class="child"]/span[text()='두 번째 스팬']

 

xpath 장점

  • 양방향 제어 가능 (부모 자식 상위 하위 검색가능)
  • 축(Axes)/함수를 사용하여 특정 노드 선택가능
  • 최신 및 구식 브라우저도 호환가능

단점

  • shadow DOM에는 사용 불가능(iframe -> 개별화된 DOM tree)
  • 속도가 느리고 복잡함

 

css selector 장점

  • 속도가 빠르고 읽기 쉬우며 배우기 쉬움
  • 단일 제어이므로 안정적

단점

  • 단방향이므로 제한적임
  • 복잡하거나 컴포넌트화 되어있으면 로케이터를 구성하기 어려움
  • 텍스트를 기반으로 selector를 구성하지 못함

 

 

Xpath Vs CSS Selector: Key Differences

XPath vs CSS selector Key differences explained with examples, this blog helps to choose the best locators for the automation framework.

testsigma.com

 

puppyteer를 python에서 사용하기 위해 pyppeteer를 사용하는 중이며

사용하기 위해서 공식 문서를 보며 숙지하는게 좋다.

https://miyakogi.github.io/pyppeteer/

 

Pyppeteer’s documentation — Pyppeteer 0.0.25 documentation

Pyppeteer is to be as similar as puppeteer, but some differences between python and JavaScript make it difficult. These are differences between puppeteer and pyppeteer. Element selector method name ($ -> querySelector) In python, $ is not usable for method

miyakogi.github.io

 

비동기로 크롤링을 하기 때문에 pyppeteer를 사용하게 되었다.

 

주로

await page.goto(url, {"waitUntil": "domcontentloaded"})
  • 'load': 페이지의 'load' 이벤트가 발생할 때까지 기다립니다.
  • 'domcontentloaded': 페이지의 'DOMContentLoaded' 이벤트가 발생할 때까지 기다립니다.
  • 'networkidle0': 네트워크가 사용되지 않는 상태(네트워크 요청이 0개 이하)가 되었을 때까지 기다립니다.
  • 'networkidle2': 네트워크가 0.5초 이상 유휴 상태가 될 때까지 기다립니다.

아무 값 없이 url만 입력하게 되면 네트워크 활동이 없을 때 까지 기다리는 상태

그래서 크롤링을 하기 위해선 domcontentloaded를 주로 사용

 

크롤링하려는 웹사이트의 환경과 조건에 맞게 사용

 

먼저 크롤링을 하기 위해 html을 가져와야 하는데 여러 기능이나 패키지가 있겠지만
BeautifulSoup를 사용하여 전체의 html가져와 데이터 파싱 또는 클릭, 입력 기능을 사용할 수 있게 된다.

 

 

click 을 사용하기 위해서 추가 할 selector를 검색 (xpath는 적용 X)

await page.click(selector)

 

만약 클릭을 해야한다 evaluate를 사용하여 click을 시도하는 방법도 있다.

python안에서 javascript코드를 전달해주는 느낌이다.
예시)  2500만큼 스크롤

async def scroll_page(page):
    await page.evaluate("window.scrollBy(0, 2500);")
    await asyncio.sleep(2)

 

스크롤을 한 후 딜레이를 주어 로드 시간을 기다린다.

 

type은 값을 입력하는 기능

await page.type(selector, email)

위와 같이 텍스트박스나 입력란의 selector를 지정하여 오른쪽에는 저장된 값을 넣어주면 된다.

 

 

getenv로 mysql에 대한 정보를 전달하고 있는데
print할 때 정상적으로 출력되는걸 볼 수 있었습니다.

 

caching_sha2_password
분명 잘 찍히기도 하고 다른곳에서는 정상작동 되는걸 보니 

 

mysql-connector-python을 재설치 해보았습니다.

pip uninstall mysql-connector-python

 

 

최신버전으로 재 설치 후 동작해보면 오류가 사라지고 잘 해결됩니다.

 

pip install mysql-connector-python

 

(아마 버전문제라고 생각됩니다....ㅎㅎ)

원하는 동작

1,2,3은 각자 비동기를 자기 일을 하고 있으면서 일정 횟수가 되면 작업을 중지 한다. 그리고 나머지가 끝날때까지 대기

 

그렇게 while문으로 무한대기중이지만 

3개의 작업이 모두 종료되어 조건이 일치할 때 나오도록 설계하였습니다.

 

여기서 문제점은

1,2,3 중에 하나가 작업이 먼저 종료되어 대기중에 있을 때 나머지도 동작을 멈춘다는것입니다.

 

그 이유는 파이썬은 GIL(Global Interpreter Lock) 한 번에 하나의 스레드만 바이트코드를 실행할 수 있는 조건이라서

해당 스레드 즉, 먼저 작업이 종료된 스레드가 CPU를 점유하고 다른스레드의 자리를  마련할 공간이 없어 계속 멈춘상태로 있는 것입니다.

 

그러기위해선 sleep을 주어 스레드가 실행될 공간을 마련해 주는 것 입니다. 이렇게 공평하게 자리를 나눠갖게 하는 것이 해결방법이었습니다.

 

await asyncio.sleep(3)

'Python > crawling' 카테고리의 다른 글

크롤링 관점에서 바라본 Css selector와 Xpath  (0) 2024.05.13
pyppeteer (goto, click, type) 사용법  (0) 2024.03.22

http에서 https로 redirect하는 방법을 다 찾아봐서 적용을 하였습니다.

 

그런데 자꾸 상태 확인 세부 정보에 [302] 오류가 나와 찾아봤는데 정확한 정보가 별로 없었습니다.

 

그래도 원하던 https가 적용이 되고 웹페이지도 로드도 잘 되는데 fail로 나와서....
Healthy로 나오게 하려면

 

먼저,

EC2로 이동 후

왼쪽 사이드바에서 대상그룹 선택

그 후 상태검사에서 편집으로 성공 코드를 200에서 302로 수정하시면 됩니다 그럼 초록색 체크로 

이렇게 출력 ^^

 

음...거슬리는 오류 해결..?

+ Recent posts