DEV Community

Cover image for 숨겨진 데이터가 있는 HTML 테이블: 보이지 않는 것을 스크래핑하는 방법
circobit
circobit

Posted on

숨겨진 데이터가 있는 HTML 테이블: 보이지 않는 것을 스크래핑하는 방법

테이블에 10개의 열이 표시됩니다. 내보내기를 합니다. CSV에도 10개의 열이 있습니다.

그런데 페이지에는 15개의 열 데이터가 있습니다. 나머지 5개는 어디로 갔을까요?

HTML 테이블에는 보이는 것보다 더 많은 데이터가 포함되어 있는 경우가 많습니다. 숨겨진 열, data 속성, 축소된 행—모두 기본적인 추출 방법으로는 보이지 않습니다.

보이지 않는 데이터를 찾고 추출하는 방법을 알아보겠습니다.

숨겨진 데이터의 유형

1. CSS로 숨겨진 열

가장 단순한 경우입니다. 열이 DOM에 존재하지만 CSS로 숨겨져 있습니다.

<th style="display: none;">내부 ID</th>
<td style="display: none;">12345</td>
Enter fullscreen mode Exit fullscreen mode

또는 클래스를 통해:

.hidden-column { display: none; }
Enter fullscreen mode Exit fullscreen mode

사이트가 이렇게 하는 이유: 모바일 반응형(작은 화면에서 열 숨기기), JavaScript용 내부 데이터, 점진적 공개.

감지 방법: DevTools를 열고 테이블을 검사하여 display: none 또는 visibility: hidden이 있는 셀을 찾습니다.

2. Data 속성

HTML5는 모든 요소에 사용자 정의 data-* 속성을 허용합니다. 테이블은 이 속성을 사용하여 JavaScript가 접근하지만 사용자에게는 보이지 않는 메타데이터를 저장합니다.

<tr data-id="12345" data-category="electronics" data-stock="47">
  <td>노트북</td>
  <td>₩999,000</td>
</tr>
Enter fullscreen mode Exit fullscreen mode

보이는 테이블에는 "노트북"과 "₩999,000"만 표시됩니다. 하지만 행에는 세 가지 추가 데이터 포인트가 있습니다.

일반적인 data 속성:

  • data-id — 내부 식별자
  • data-sort-value — 정렬용 숫자 값 (표시는 "2024년 1월"이지만 정렬에는 202401이 필요할 때)
  • data-raw — 서식 없는 값 (표시는 "₩12억"이지만 데이터는 1200000000)
  • data-href — 링크 URL

3. Title 및 툴팁 속성

title 속성은 마우스를 올렸을 때 추가 정보를 포함하는 호버 텍스트를 제공합니다.

<td title="업데이트: 2024-01-15 14:32:00 UTC">1월 15일</td>
Enter fullscreen mode Exit fullscreen mode

셀에는 "1월 15일"이 표시됩니다. 전체 타임스탬프는 툴팁에 있습니다.

4. 축소/확장 가능한 행

드릴다운 기능이 있는 테이블은 클릭할 때까지 세부 행을 숨깁니다.

<tr class="parent-row">
  <td>카테고리 A</td>
  <td>₩50,000,000</td>
</tr>
<tr class="child-row" style="display: none;">
  <td>— 하위 카테고리 A1</td>
  <td>₩30,000,000</td>
</tr>
<tr class="child-row" style="display: none;">
  <td>— 하위 카테고리 A2</td>
  <td>₩20,000,000</td>
</tr>
Enter fullscreen mode Exit fullscreen mode

데이터는 존재합니다. 단지 펼쳐지지 않았을 뿐입니다.

5. 지연 로드 콘텐츠

일부 테이블은 일부만 표시하고 스크롤하거나 "더 보기"를 클릭하면 JavaScript를 통해 더 많은 데이터를 로드합니다.

<table id="results">
  <!-- 처음 20행만 렌더링 -->
</table>
<button onclick="loadMore()">더 많은 결과 보기</button>
Enter fullscreen mode Exit fullscreen mode

나머지 데이터는 로딩을 트리거할 때까지 DOM에 없습니다.

숨겨진 데이터 추출

CSS로 숨겨진 열 추출

JavaScript (브라우저 콘솔에서):

// 모든 셀에서 display:none 제거
document.querySelectorAll('td, th').forEach(el => {
  el.style.display = '';
});
Enter fullscreen mode Exit fullscreen mode

이제 정상적으로 내보내면 열이 보입니다.

Python + BeautifulSoup:

BeautifulSoup은 CSS를 무시합니다. 가시성에 관계없이 모든 요소를 추출합니다.

from bs4 import BeautifulSoup

soup = BeautifulSoup(html, 'html.parser')
table = soup.find('table')

for row in table.find_all('tr'):
    cells = [td.get_text(strip=True) for td in row.find_all(['td', 'th'])]
    print(cells)  # 숨겨진 열 포함
Enter fullscreen mode Exit fullscreen mode

Data 속성 추출

JavaScript 방식:

const rows = document.querySelectorAll('table tr');
const data = [];

rows.forEach(row => {
  const rowData = {
    // 보이는 텍스트
    cells: Array.from(row.querySelectorAll('td')).map(td => td.textContent),
    // Data 속성
    id: row.dataset.id,
    category: row.dataset.category,
    stock: row.dataset.stock
  };
  data.push(rowData);
});

console.log(JSON.stringify(data, null, 2));
Enter fullscreen mode Exit fullscreen mode

Python 방식:

for row in table.find_all('tr'):
    # data 속성 가져오기
    attrs = {k: v for k, v in row.attrs.items() if k.startswith('data-')}

    # 셀 텍스트 가져오기
    cells = [td.get_text(strip=True) for td in row.find_all('td')]

    print(f"Data: {attrs}, Cells: {cells}")
Enter fullscreen mode Exit fullscreen mode

Title 속성 추출

const cells = document.querySelectorAll('table td');
cells.forEach(cell => {
  if (cell.title) {
    console.log(`표시: ${cell.textContent}, 전체: ${cell.title}`);
  }
});
Enter fullscreen mode Exit fullscreen mode

축소된 행 확장

방법 1: 모든 확장 버튼 클릭

// 모든 확장 버튼 찾아서 클릭
document.querySelectorAll('.expand-btn, .toggle-row').forEach(btn => {
  btn.click();
});

// DOM 업데이트 대기 후 내보내기
setTimeout(() => {
  // 내보내기 로직
}, 1000);
Enter fullscreen mode Exit fullscreen mode

방법 2: 숨김 클래스 제거

document.querySelectorAll('.child-row, .detail-row').forEach(row => {
  row.style.display = '';
  row.classList.remove('hidden', 'collapsed');
});
Enter fullscreen mode Exit fullscreen mode

지연 로드 트리거

이것은 좀 더 까다롭습니다. 로딩을 트리거하는 동작을 시뮬레이션해야 합니다.

// 무한 스크롤 트리거를 위해 하단으로 스크롤
window.scrollTo(0, document.body.scrollHeight);

// 또는 "더 보기" 반복 클릭
const loadMore = document.querySelector('.load-more-btn');
while (loadMore && !loadMore.disabled) {
  loadMore.click();
  await new Promise(r => setTimeout(r, 500)); // 로드 대기
}
Enter fullscreen mode Exit fullscreen mode

실제 사례: 정렬 값

스포츠 통계 테이블은 종종 서식이 지정된 숫자를 표시하지만 원시 값으로 정렬합니다.

<td data-sort="1234567">123.4만</td>
<td data-sort="20240115">2024년 1월 15일</td>
<td data-sort="0.347">34.7%</td>
Enter fullscreen mode Exit fullscreen mode

보이는 텍스트만 추출하면 정렬이나 계산이 올바르게 되지 않는 서식 문자열을 얻게 됩니다.

두 값을 모두 추출:

const cells = document.querySelectorAll('table td');
const data = Array.from(cells).map(cell => ({
  display: cell.textContent.trim(),
  sortValue: cell.dataset.sort || cell.textContent.trim()
}));
Enter fullscreen mode Exit fullscreen mode

브라우저 확장 프로그램이 도움이 되는 경우

좋은 테이블 내보내기 도구는 이 중 일부를 자동으로 처리합니다:

  • CSS로 숨겨진 열을 옵션으로 포함 가능
  • Data 속성을 추가 열로 추출 가능
  • 숫자 정규화로 "₩12.3억"을 1230000000으로 파싱 가능

HTML Table Exporter는 병합된 셀과 서식을 적절히 처리하면서 보이는 콘텐츠를 추출합니다. 코드 없이 더 복잡한 추출이 필요한 경우, 코드 없이 웹사이트에서 테이블 스크래핑하기 가이드를 참조하세요.

Data 속성과 깊이 숨겨진 콘텐츠의 경우 위의 JavaScript 접근 방식이 필요할 수 있습니다.

검사 워크플로

테이블을 추출하기 전에:

  1. DevTools 열기 (F12)
  2. 테이블 요소 검사
  3. 확인 사항:
    • 열에 display: none이 있는지
    • 행/셀에 data-* 속성이 있는지
    • 추가 정보가 있는 title 속성이 있는지
    • 숨겨진 자식 행이 있는지
    • "더 보기" 버튼이 있는지
  4. 결정: 보이는 데이터로 충분한지, 숨겨진 데이터가 필요한지?

대부분의 경우 보이는 데이터로 충분합니다. 그러나 그렇지 않을 때, 숨겨진 데이터를 찾고 추출하는 방법을 아는 것이 차이를 만듭니다.

요약

HTML 테이블에는 여러 계층의 데이터가 포함될 수 있습니다:

계층 접근 방법
보이는 텍스트 표준 내보내기
CSS 숨김 열 display:none 제거 또는 BeautifulSoup 사용
Data 속성 JavaScript dataset 또는 Python attrs
Title/툴팁 title 속성 추출
축소된 행 확장 또는 숨김 클래스 제거
지연 로드 먼저 로딩 트리거

데이터는 보통 거기에 있습니다. 어디를 봐야 하는지만 알면 됩니다.


보이는 데이터를 내보내야 하나요? gauchogrid.com/ko/html-table-exporter에서 자세히 알아보거나 Chrome 웹 스토어에서 HTML Table Exporter를 무료로 사용해 보세요.

Top comments (0)