본문 바로가기
GAS/[Js_손코딩]

[Console] 에서 Innertext 뽑아내기

by 일등미노왕국 2025. 5. 14.

 

와이즈토토라는 스포츠분석 사이트에서 일정 테이블의 내용을 console 창으로 뽑아오는 코드를 실습하면서 실무 자바스크립트를 연습해보자.

 

물론 테이블을 가져오는 VBA 코드도 있지만 그건 너무 복잡하고 힘들어서 생략하려고 한다.

 

굳이 이거 보다는 크롬의 개발자 도구에서 손쉽게 사이트의 특정 테이블을 가져와서 Console창에 출력하려고 한다.

 

더보기

// 모든 경기 정보 목록을 찾습니다.
const gameLists = document.querySelectorAll('.gameinfo ul');

// 각 열의 너비 설정 (문자 단위)
const columnWidths = {
  번호: 5,
  시간: 16,
  리그: 8,
  핸디캡: 10,
  홈팀: 12,
  VS: 2,
  원정팀: 12,
  배당1: 8,
  배당2: 8,
  배당3: 8,
  상태: 8
};

// 컬럼명 배열
const headers = ["번호", "시간", "리그", "핸디캡", "홈팀", "VS", "원정팀", "배당1", "배당2", "배당3", "상태"];

// 패딩 함수 - 문자열을 지정된 너비로 맞춰줍니다
function padString(str, width) {
  // 문자열이 null이거나 undefined인 경우 빈 문자열로 처리
  const text = str === null || str === undefined ? '' : String(str);
  
  // 한글은 2칸, 영문/숫자/기호는 1칸 차지하는 것을 고려하여 실제 표시 너비 계산
  let displayWidth = 0;
  for (let i = 0; i < text.length; i++) {
    // 한글 및 전각 문자(CJK 문자 등)는 2칸으로 계산
    if (/[\u3131-\uD79D]|[\u3000-\u303F]|[\u3040-\u309F]|[\u30A0-\u30FF]|[\uFF00-\uFFEF]|[\u4E00-\u9FAF]|[\u2E80-\u2EFF]|[\uA960-\uA97F]|[\uAC00-\uD7AF]/.test(text[i])) {
      displayWidth += 2;
    } else {
      displayWidth += 1;
    }
  }
  
  // 남은 공간을 공백으로 채워 고정 너비 만들기
  const padding = width - displayWidth;
  if (padding > 0) {
    return text + ' '.repeat(padding);
  } 
  // 너비를 초과한 경우 문자열 자르기 (선택적)
  return text.substring(0, width - 1) + '…';
}

// 헤더 출력
let headerLine = '';
headers.forEach(header => {
  headerLine += padString(header, columnWidths[header]) + ' | ';
});
console.log(headerLine);

// 구분선 출력 - 헤더와 데이터 사이에 구분선 추가
let separatorLine = '';
headers.forEach(header => {
  separatorLine += '-'.repeat(columnWidths[header]) + '-+-';
});
console.log(separatorLine);

// 각 경기 정보를 파싱하고 고정 너비로 출력
gameLists.forEach((game) => {
  const gameInfo = [];
  
  // 번호
  gameInfo.push(game.querySelector('.a1')?.innerText.trim() || '');
  // 시간
  gameInfo.push(game.querySelector('.a2')?.innerText.trim() || '');
  // 리그
  gameInfo.push(game.querySelector('.a4')?.innerText.trim() || '');
  // 핸디캡 또는 언더오버
  gameInfo.push((game.querySelector('.hp') || game.querySelector('.un') || game.querySelector('.hm'))?.innerText.trim() || '');
  // 홈팀
  gameInfo.push(game.querySelector('.a6 span.tn')?.innerText.trim() || '');
  // VS
  gameInfo.push(':');
  // 원정팀
  gameInfo.push(game.querySelector('.a8 span.tn')?.innerText.trim() || '');
  // 배당1
  gameInfo.push(game.querySelectorAll('.a9 .pt')[0]?.innerText.trim() || '-');
  // 배당2
  gameInfo.push(game.querySelectorAll('.a9 .pt')[1]?.innerText.trim() || '-');
  // 배당3
  gameInfo.push(game.querySelectorAll('.a9 .pt')[2]?.innerText.trim() || '-');
  // 상태
  const state = game.querySelector('.tag')?.innerText.trim() || '';
  gameInfo.push(state);
  
  // 🎯 상태가 "경기전"인 경우에만 출력
  if (state === "경기전") {
    let formattedLine = '';
    
    // 각 필드를 고정 너비로 패딩
    headers.forEach((header, idx) => {
      formattedLine += padString(gameInfo[idx], columnWidths[header]) + ' | ';
    });
    
    console.log(formattedLine);
  }
});

 

Html 태그에 있는 .gameinfo 태그안에 각 경기에 ul 태그속에 있기 때문에 ul 태그를 순환하면서 각 자식요소인 li 태그를 파싱하면 된다.

 

간단하게 ForEach문으로 순환하면서 innertext를 속성을 console.log로 출력하면 되지만, 그러면 시인성이 떨어지기 때문에 너비와 적당한 여백을 주어서 보기 좋은 테이블 형태로 보여지게 하는 코드이다.

 

 

 

 

1. 데이터 선택 및 기본 설정

 
javascript
// 모든 경기 정보 목록을 찾습니다.
const gameLists = document.querySelectorAll('.gameinfo ul');
  • 웹페이지에서 .gameinfo ul 선택자를 사용하여 모든 경기 정보 목록을 가져온다.
 
 
// 각 열의 너비 설정 (문자 단위)
const columnWidths = {
  번호: 5,
  시간: 16,
  리그: 8,
  핸디캡: 10,
  홈팀: 12,
  VS: 2,
  원정팀: 12,
  배당1: 8,
  배당2: 8,
  배당3: 8,
  상태: 8
};
  • 각 열의 고정 너비를 문자 단위로 설정하게 되는데, 이 값들은 출력 시 각 열이 차지할 공간을 정의하게 된다.
 
 
// 컬럼명 배열
const headers = ["번호", "시간", "리그", "핸디캡", "홈팀", "VS", "원정팀", "배당1", "배당2", "배당3", "상태"];
  • 테이블 헤더의 순서를 정의

2. 문자열 패딩 함수

 
 
// 패딩 함수 - 문자열을 지정된 너비로 맞춰줍니다
function padString(str, width) {
  // 문자열이 null이거나 undefined인 경우 빈 문자열로 처리
  const text = str === null || str === undefined ? '' : String(str);
  
  // 한글은 2칸, 영문/숫자/기호는 1칸 차지하는 것을 고려하여 실제 표시 너비 계산
  let displayWidth = 0;
  for (let i = 0; i < text.length; i++) {
    // 한글 및 전각 문자(CJK 문자 등)는 2칸으로 계산
    if (/[\u3131-\uD79D]|[\u3000-\u303F]|[\u3040-\u309F]|[\u30A0-\u30FF]|[\uFF00-\uFFEF]|[\u4E00-\u9FAF]|[\u2E80-\u2EFF]|[\uA960-\uA97F]|[\uAC00-\uD7AF]/.test(text[i])) {
      displayWidth += 2;
    } else {
      displayWidth += 1;
    }
  }
  
  // 남은 공간을 공백으로 채워 고정 너비 만들기
  const padding = width - displayWidth;
  if (padding > 0) {
    return text + ' '.repeat(padding);
  } 
  // 너비를 초과한 경우 문자열 자르기 (선택적)
  return text.substring(0, width - 1) + '…';
}

이 함수는 매우 중요한 부분으로, 테이블의 일정한 너비를 유지하는 핵심 기능이다.

  • null이나 undefined 값은 빈 문자열로 처리
  • 한글과 같은 CJK(중국어, 일본어, 한국어) 문자는 콘솔에서 영문자보다 더 넓게 표시되므로, 한글은 2칸, 영문/숫자/기호는 1칸으로 계산
  • 정규식 /[\u3131-\uD79D]|[\u3000-\u303F]|..../는 한글과 기타 아시아 언어 문자를 감지
  • 계산된 실제 표시 너비를 기준으로 남은 공간을 공백으로 채워 모든 열이 일정한 너비를 갖도록 하게 된다.
  • 문자열이 지정된 너비를 초과하면 자르고 '…'을 추가...

3. 헤더와 구분선 출력

 
 
// 헤더 출력
let headerLine = '';
headers.forEach(header => {
  headerLine += padString(header, columnWidths[header]) + ' | ';
});
console.log(headerLine);

// 구분선 출력 - 헤더와 데이터 사이에 구분선 추가
let separatorLine = '';
headers.forEach(header => {
  separatorLine += '-'.repeat(columnWidths[header]) + '-+-';
});
console.log(separatorLine);
  • 헤더 행을 생성하고 출력하고, 각 헤더는 padString 함수를 사용하여 지정된 너비로 패딩
  • 헤더 아래에 구분선을 추가하여 가독성을  올려 보았다.

4. 데이터 파싱 및 출력

 
 
// 각 경기 정보를 파싱하고 고정 너비로 출력
gameLists.forEach((game) => {
  const gameInfo = [];
  
  // 번호
  gameInfo.push(game.querySelector('.a1')?.innerText.trim() || '');
  // 시간
  gameInfo.push(game.querySelector('.a2')?.innerText.trim() || '');
  // (중략)
  
  // 🎯 상태가 "경기전"인 경우에만 출력
  if (state === "경기전") {
    let formattedLine = '';
    
    // 각 필드를 고정 너비로 패딩
    headers.forEach((header, idx) => {
      formattedLine += padString(gameInfo[idx], columnWidths[header]) + ' | ';
    });
    
    console.log(formattedLine);
  }
});
  • 각 경기 정보를 DOM에서 선택자를 사용하여 추출
  • Optional Chaining (?.)을 사용하여 요소가 없는 경우에도 오류가 발생하지 않도록하는데, 절대적으로 필요하다
  • 상태가 "경기전"인 경기만 출력.. 약간의 Filter기능이라고 생각하면 된다.
  • 각 필드는 padString 함수를 사용하여 지정된 너비로 패딩
  • 최종적으로 형식이 지정된 행을 콘솔에 출력

기술적 특징

  1. 선택적 체이닝(Optional Chaining): ?.를 사용하여 DOM 요소가 존재하지 않을 때 안전하게 처리
  2. 널 병합(Nullish Coalescing): || 연산자를 사용하여 값이 없을 때 기본값을 제공
  3. 정규식: 한글과 다른 CJK 문자를 감지하기 위한 복잡한 유니코드 범위 정규식을 사용
  4. 필터링: 특정 조건(상태가 "경기전")에 맞는 항목만 출력

 

 

이걸 굳이 왜 이렇게 해야 하는지라고 물을수도 있지만, 자바스크립트를 배우게 되면 자꾸 콘솔창에서 놀고 싶어지게 된다.

ㅋㅋㅋ

 

 

이렇게 얻은 innertext 값은 

Ai 분석을 통해서

 

4. 종합 베팅 전략 및 조합

핵심 단일 베팅 추천 (신뢰도 순)

  1. 바야돌리드 +1.0 (2.04) - ★★★★☆
  2. 덴버 너게츠 +10.5 (1.78) - ★★★★☆
  3. 시애틀 매리너스 승 (3.20) - ★★★★☆
  4. 인디애나 페이서스 +8.5 (1.71) - ★★★☆☆
  5. RC셀타 승 (2.95) - ★★★☆☆
  6. 샌프란시스코 자이언츠 승 (2.75) - ★★☆☆☆

최적 조합 베팅 전략

조합 1: 안정적 접근 (3경기)

  • 바야돌리드 +1.0 (2.04)
  • 덴버 너게츠 +10.5 (1.78)
  • 시애틀 매리너스 +2.5 (1.34)
  • 총 배당: 4.87
  • 특징: 높은 적중 가능성, 중간 수익률
  • 베팅 단위: 표준 단위의 100%

조합 2: 균형 접근 (4경기)

  • 바야돌리드 +1.0 (2.04)
  • 덴버 너게츠 +10.5 (1.78)
  • 인디애나 페이서스 +8.5 (1.71)
  • 언더 7.5 시애틀 vs 뉴욕 (1.68)
  • 총 배당: 10.45
  • 특징: 중간 적중 가능성, 높은 수익률
  • 베팅 단위: 표준 단위의 50%

조합 3: 공격적 접근 (5경기)

  • 바야돌리드 승 (4.15)
  • 덴버 너게츠 승 (4.57)
  • RC셀타 승 (2.95)
  • 시애틀 매리너스 승 (3.20)
  • 언더 2.5 바야돌리드 vs 지로나 (1.86)
  • 총 배당: 134.53
  • 특징: 낮은 적중 가능성, 극도로 높은 수익률
  • 베팅 단위: 표준 단위의 10%

조합 4: 언더/오버 집중 (4경기)

  • 언더 229.5 클리블랜드 vs 인디애나 (1.77)
  • 언더 221.5 오클라호마 vs 덴버 (1.74)
  • 언더 2.5 바야돌리드 vs 지로나 (1.86)
  • 언더 7.5 시애틀 vs 뉴욕 (1.68)
  • 총 배당: 9.67
  • 특징: 중간 적중 가능성, 적절한 수익률
  • 베팅 단위: 표준 단위의 60%

조합 5: 핸디캡 중심 (5경기)

  • 인디애나 +8.5 (1.71)
  • 덴버 +10.5 (1.78)
  • 바야돌리드 +1.0 (2.04)
  • 시애틀 +2.5 (1.34)
  • 샌프란시스코 +2.5 (1.27)
  • 총 배당: 8.37
  • 특징: 높은 적중 가능성, 안정적 수익률
  • 베팅 단위: 표준 단위의 80%

5. 결론 및 핵심 인사이트

  1. NBA 플레이오프: 높은 핸디캡 라인에서 언더독(+핸디캡)에 가치 존재
  2. 라리가: 바야돌리드의 홈 경기는 강력한 역배 가치 제공
  3. MLB: 시애틀 vs 뉴욕 경기에서 홈팀 승 베팅에 가치 발견
  4. 종합 전략: 핸디캡 베팅이 단일 승리 베팅보다 안정적 가치 제공
  5. 자금 관리: 공격적 조합(3번)은 소액만 배정하고, 안정적 조합(1번, 5번)에 중점 배치 권장

이런 형태로 뽑아낼 수 있다.

 

 

암튼 자바스크립트의 기본 함수와 태그요소를 움직이는 코드이지만 거의 뼈대가 되는 구문들이니까 천천히 공부해 보길 바란다.

 

오늘도 맛있는 코드 냠냠

 

댓글