아빠는 개발자

[Aqqle] Ya후 finance 긁어보자 2 본문

Aqqle/CRAWLER

[Aqqle] Ya후 finance 긁어보자 2

father6019 2024. 9. 8. 11:13
728x90
반응형

몇일 전까지 다운로드 링크를 제공했었는데..  막혔다. 

치사하다..

그래서 또 긁어야 겠다. 

 

주가정보를 훔쳐서 보관할 테이블 생성 

CREATE TABLE `stock_data` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `company` varchar(100) NOT NULL,
  `company_code` varchar(20) NOT NULL,
  `trading_date` date NOT NULL,
  `open` float DEFAULT NULL,
  `high` float DEFAULT NULL,
  `low` float DEFAULT NULL,
  `close` float DEFAULT NULL,
  `adj_close` float DEFAULT NULL,
  `volume` bigint DEFAULT NULL,
  `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`company_code`,`trading_date`),
  UNIQUE KEY `unique_id` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

 

재물로 사용할 페이지 주가정보의 히스토리를 가지고 있는..

 

원랜 다운로드를 제공해서 편하게 사용했는데 프리미엄 구독을 해야 다운로드를 제공하는걸로 바뀐듯..

 

date 를 사용할때는 Unix Epoch 로 계산해서 사용해야 한다. 

 

Unix Epoch이란?

  • Unix Epoch: 1970년 1월 1일 00:00:00 UTC를 기준으로 합니다. 이 시점은 Unix 시스템의 시작 시점으로, 시간 계산의 기준점 역할을 합니다.

 

일단 자바로 긁었다. 

거래일이 Sep 13, 2024 이런식이라 포맷을 맞춰주는 부분을 .. 귀찮아서 그냥  넣음.. 나중에 정리해야지..

일단 되게 만 해놓고

package com.doo.aqqle.service;


import com.doo.aqqle.element.Site;
import com.doo.aqqle.factory.SiteFactory;
import com.doo.aqqle.factory.YahooDataFactory;
import com.doo.aqqle.repository.Stock;
import com.doo.aqqle.repository.StockData;
import com.doo.aqqle.repository.StockDataRepository;
import com.doo.aqqle.repository.StockRepository;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service("YahooDataService")
public class YahooDataService extends AqqleService implements AqqleCrawler {

    public YahooDataService(
            StockRepository stockRepository,
            StockDataRepository stockDataRepository) {
        super(stockRepository, stockDataRepository);

    }

    private final int CRAWLING_COUNT = 25;

    @Override
    public void execute() {

        List<Stock> stockList = stockRepository.findAllByUseYn("Y");

        Site site = SiteFactory.getSite(new YahooDataFactory());

        stockList.stream().forEach(x -> {

            LocalDate localDate = LocalDate.parse(x.getStartDate());
            long period1 = localDate.atStartOfDay(ZoneId.of("UTC")).toEpochSecond();
            long period2 = Instant.now().getEpochSecond();

            Map<String, Long> periodMap = new HashMap<>();
            periodMap.put("period1", period1);
            periodMap.put("period2", period2);

            site.getUrl(x.getCompanyCode(), periodMap);

            String listUrl = site.getUrl(x.getCompanyCode(), periodMap);

            System.out.println(listUrl);
            try {
                Document listDocument = Jsoup.connect(listUrl)
                        .timeout(5000)
                        .get();
                Elements tableTr = listDocument.select(site.getListCssSelector());

                tableTr.stream()
                        .map(tr -> tr.select("td").stream()
                                .map(Element::text)  // 각 td 요소의 텍스트를 추출
                                .collect(Collectors.toList())  // 추출한 텍스트를 리스트로 수집
                        )
                        .filter(tds -> tds.size() > 6)
                        .forEach(tdTexts -> {

                            StockData stockData = new StockData();
                            DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("MMM d, yyyy", Locale.ENGLISH);
                            DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
                            LocalDate date = LocalDate.parse(tdTexts.get(0), inputFormatter);

                            // 날짜를 원하는 형식으로 포맷
                            String tradingDate = date.format(outputFormatter);

                            stockData.setCompany(x.getCompany());
                            stockData.setCompanyCode(x.getCompanyCode());
                            stockData.setTradingDate(tradingDate);  // 첫 번째 항목: 거래일
                            stockData.setOpen(Float.parseFloat(tdTexts.get(1).replace(",", "")));         // 두 번째 항목: 시가
                            stockData.setHigh(Float.parseFloat(tdTexts.get(2).replace(",", "")));         // 세 번째 항목: 최고가
                            stockData.setLow(Float.parseFloat(tdTexts.get(3).replace(",", "")));          // 네 번째 항목: 최저가
                            stockData.setClose(Float.parseFloat(tdTexts.get(4).replace(",", "")));        // 다섯 번째 항목: 종가
                            stockData.setAdjClose(Float.parseFloat(tdTexts.get(5).replace(",", "")));     // 여섯 번째 항목: 수정 종가


                            Long volume = 0L;
                            if(!tdTexts.get(6).replace(",", "").replace("-", "").isBlank()) {
                                volume = Long.parseLong(tdTexts.get(6).replace(",", "").replace("-", ""));
                            }
                            stockData.setVolume(volume);       // 일곱 번째 항목: 거래량

                            stockDataRepository.upsert(
                                    stockData.getCompany(),
                                    stockData.getCompanyCode(),
                                    stockData.getTradingDate(),
                                    stockData.getOpen(),
                                    stockData.getHigh(),
                                    stockData.getLow(),
                                    stockData.getClose(),
                                    stockData.getAdjClose(),
                                    stockData.getVolume()
                            );
                        });
            } catch (IOException e) {
                e.printStackTrace();
            }
            log.info("DATA 저장 완료.");
        });
    }

}

 

repository 를 upsert 로 만들

public interface StockDataRepository extends JpaRepository<StockData, Long> {

    @Modifying
    @Transactional
    @Query(value = "INSERT INTO stock_data (company, company_code, trading_date, open, high, low, close, adj_close, volume) " +
            "VALUES (:company, :companyCode, :tradingDate, :open, :high, :low, :close, :adjClose, :volume) " +
            "ON DUPLICATE KEY UPDATE company = VALUES(company), open = VALUES(open), high = VALUES(high), " +
            "low = VALUES(low), close = VALUES(close), adj_close = VALUES(adj_close), volume = VALUES(volume), " +
            "updated_time = CURRENT_TIMESTAMP", nativeQuery = true)
    void upsert(String company, String companyCode, String tradingDate, Float open, Float high,
                         Float low, Float close, Float adjClose, Long volume);

}

 

훔친물건

 

이제 깨끗하게 세탁해서 뒷탈없게 사용해야겠다. 

 

 

728x90
반응형

'Aqqle > CRAWLER' 카테고리의 다른 글

[Aqqle] 상품데이터 긁어보자  (2) 2024.10.08
[Aqqle] 야hoo finance 긁어보자  (4) 2024.09.01