DuckDB Webbed Extension: XML 및 HTML 처리 확장 기능
요약
DuckDB의 Webbed Extension은 XML 및 HTML 처리를 위한 포괄적인 확장 기능입니다. 이 기능을 통해 XPath 기반 데이터 추출과 지능형 스키마 추론을 활용하여 구조화된 문서에 대한 SQL 네이티브 분석이 가능해집니다. 대용량 파일 처리 시 SAX 스트리밍 파서를 사용하여 메모리 효율성을 높였으며, 여러 파일을 병렬로 읽는 기능도 제공합니다.
핵심 포인트
- XML/HTML 문서를 DuckDB에서 직접 분석 및 추출 가능
- XPath를 이용한 강력하고 정교한 데이터 추출 지원
- 대용량 파일 처리를 위한 SAX 기반 스트리밍 파서 구현
- 스키마 자동 추론과 다양한 데이터 타입 감지 기능 제공
DuckDB Webbed Extension
A comprehensive XML and HTML processing extension for DuckDB that enables SQL-native analysis of structured documents with intelligent schema inference and powerful XPath-based data extraction.
(DuckDB용 포괄적인 XML 및 HTML 처리 확장 기능으로, 지능형 스키마 추론과 강력한 XPath 기반 데이터 추출을 통해 구조화된 문서에 대한 SQL 네이티브 분석을 가능하게 합니다.)
Features Overview
🔍 XML & HTML Processing
- Parse and validate XML/HTML documents (XML/HTML 문서 구문 분석 및 유효성 검사)
- Extract data using XPath expressions (XPath 표현식을 사용하여 데이터 추출)
- Convert between XML, HTML, and JSON formats (XML, HTML, JSON 형식 간 변환)
- Read files directly into DuckDB tables (파일을 DuckDB 테이블에 직접 읽기)
📊 Smart Schema Inference
- Automatically flatten XML documents into relational tables (XML 문서를 관계형 테이블로 자동 평탄화)
- Intelligent type detection (dates, numbers, booleans) (지능형 타입 감지 (날짜, 숫자, 부울))
- Configurable element and attribute handling (구성 가능한 요소 및 속성 처리)
- Custom datetime format control with presets and format strings (사전 설정 및 형식 문자열을 사용한 사용자 정의 날짜/시간 형식 제어)
🚀 Streaming for Large Files
- SAX-based streaming parser for files exceeding
maximum_file_size(maximum_file_size를 초과하는 파일을 위한 SAX 기반 스트리밍 파서) - Peak memory proportional to a single record, not the entire file (전체 파일이 아닌 단일 레코드에 비례하는 최대 메모리 사용량)
- Automatic fallback: DOM for small files, SAX for large files (자동 폴백: 소형 파일은 DOM, 대용량 파일은 SAX 사용)
- Controlled by
streamingparameter (default: true) (streaming매개변수(기본값: true)로 제어)
⚡ Multi-File Parallelism
read_xml/read_html(and the_objectsvariants) process a glob or list of files across threads — one worker per file (read_xml/read_html(및_objects변형)은 파일을 스레드 전반에 걸쳐 처리합니다. 파일당 하나의 워커를 사용합니다.)- Order-preserving: output stays in file order regardless of thread count (DuckDB batch-index reassembly), so results are deterministic (순서 유지: 출력 결과는 스레드 수와 관계없이 파일 순서를 유지하며(DuckDB 배치 인덱스 재조립), 따라서 결과가 결정론적입니다.)
- Glob matches are sorted lexicographically for stable, filesystem-independent order; an explicit list of paths keeps the order you provide (Glob 일치는 안정적이고 파일 시스템에 독립적인 순서로 사전순 정렬됩니다. 명시적인 경로 목록은 제공한 순서를 유지합니다.)
- Single-file reads are unchanged (one file → one worker → the existing serial path) (단일 파일 읽기는 변경되지 않습니다(하나의 파일 → 하나의 워커 → 기존 직렬 경로)).
🛠 Production Ready
- Built on libxml2 for robust parsing (견고한 구문을 위해 libxml2 기반)
- Comprehensive error handling (포괄적인 오류 처리)
- Memory-safe RAII implementation (메모리 안전 RAII 구현)
- 89 test suites, 2,900+ assertions (89개의 테스트 스위트, 2,900개 이상의 단언문)
🔧 콘텐츠 파싱 함수
| Function | Description | Example |
|---|---|---|
parse_html(content) | HTML 콘텐츠 문자열을 파싱합니다. | SELECT parse_html('<p>Hello</p>') |
| ... |
🎯 데이터 추출 함수
| Function | Description | Example |
|---|---|---|
xml_extract_text(xml, xpath) | XPath를 사용하여 모든 텍스트 일치 항목을 추출합니다 (LIST(VARCHAR)). | SELECT xml_extract_text(xml, '//title')[1] |
| ... |
🌐 HTML 추출 함수
| Function | Description | Example |
|---|---|---|
html_extract_text(html) | HTML에서 모든 텍스트를 추출합니다 (VARCHAR). | SELECT html_extract_text(html) |
| ... |
🗂️ HTML 테이블 추출 함수
| Function | Return Type | Description | Example |
|---|---|---|---|
html_extract_tables(html) | TABLE | 테이블을 행으로 추출합니다 (테이블 함수). | SELECT * FROM html_extract_tables(html_string) |
| ... |
🔄 형식 변환 함수
| Function | Description | Example |
|---|---|---|
xml_to_json(xml, ...) | XML을 JSON으로 변환하며 구성 가능한 옵션을 제공합니다. | SELECT xml_to_json('<person><name>John</name></person>') |
json_to_xml(json) | JSON을 XML로 변환합니다. | SELECT json_to_xml('{"name":"John"}') |
xml_to_json 매개변수:
force_list: 항상 JSON 배열로 변환할 요소 이름의 배열입니다.attr_prefix: 속성(attribute)에 대한 접두사입니다 (기본값:'@').text_key: 텍스트 콘텐츠를 위한 키입니다 (기본값:'#text').namespaces: 네임스페이스 처리 방식:'strip'(기본값),'expand','keep'xmlns_key: 네임스페이스 선언을 위한 키입니다 (기본값: 빈 문자열/비활성화).empty_elements: 빈 요소를 처리하는 방법:'object'(기본값),'null','string'
Python xmltodict 호환성:
Python 스타일의 xmltodict 동작을 위해 다음 기본값을 가진 매크로를 생성합니다:
CREATE MACRO xmltodict(xml,
attr_prefix := '@',
text_key := '#',
...
HTML 테이블 추출 매크로:
HTML 테이블을 JSON 또는 DuckDB 테이블로 타입 추론과 함께 변환합니다:
-- HTML 테이블을 JSON 배열로 추출
CREATE MACRO html_table_to_json(html, idx := 0) AS (
WITH raw_table AS (
...
📋 분석 및 유틸리티 함수
| Function | Description | Example |
|---|---|---|
xml_stats(xml) | 문서 통계 가져오기 | SELECT xml_stats('<root><item/><item/></root>') |
| ... |
사용 예시
📖 기본 XML 처리
-- XML 파일 로드 및 유효성 검사
SELECT filename, xml_valid(xml) as is_valid
FROM read_xml_objects('data/*.xml', filename=true);
...
🌐 HTML 데이터 추출
-- HTML 페이지에서 모든 링크 추출
SELECT
(unnest(html_extract_links(html))).href as url,
...
🔍 고급 스키마 추론
-- 사용자 정의 스키마 옵션으로 XML 읽기
SELECT * FROM read_xml('products.xml',
ignore_errors=true,
...
🎼 선택적 자식이 있는 반복 요소
문서가 요소를 반복하고 그 요소들 중 일부에만 특정 자식이 있는 경우, record_element를 사용하여 반복되는 요소마다 한 행을 얻습니다. 존재하지 않는 자식은 NULL로 반환되므로 행이 원본과 정렬된 상태를 유지합니다.
-- MusicXML: 대부분의 음표는 <alter>가 없습니다 (C), 일부만 있습니다 (C# = 1, Bb = -1)
SELECT number AS measure,
n.pitch.step AS note,
...
record_element := 'measure'를 사용하면 측정값 자체의 속성(number)이 열로 유지되고 <note> 자식들이 타입 지정된 STRUCT[]로 중첩됩니다. 만약 조상(ancestor) 속성이 필요하지 않다면 대신 record_element := 'note'를 사용합니다. 이 경우 음표마다 한 행을 직접 얻지만 측정값 번호는 접근할 수 없게 됩니다.
타입에 유의하세요: accidental, octave 및 duration은 텍스트가 아닌 INTEGER입니다.
두 개의 추출 호출을 사용하지 마세요. 매력적인 접근 방식은 정렬되지 않은 리스트를 반환합니다. 왜냐하면 선택적 자식 요소가 부모 요소보다 적은 항목을 생성하기 때문입니다:
-- 하지 말 것: 리스트의 길이가 달라 zip 할 수 없습니다
SELECT xml_extract_text(xml, '//note/pitch/step') AS step, -- [C, B, B, D] 4개 항목
xml_extract_text(xml, '//note/pitch/alter') AS alter -- [1, -1] 2개 항목
...
이 두 개의 알터가 어떤 음에 속했는지 복구할 방법이 없습니다. 서브스크립팅도 도움이 되지 않습니다: [1]은 전체 문서의 첫 번째 음을 가져올 뿐, 행당 하나의 음을 가져오는 것이 아닙니다. 대신 record_element로 처리하세요. (이미 레코드로 다시 읽을 수 없는 열에 있는 XML의 경우, xml_extract_text_each(xml, context_xpath, relative_xpath)는 누락된 항목에 대해 NULL이 아닌 컨텍스트 노드당 하나의 항목을 반환합니다.)
대용량 파일: 스키마를 고정하세요. maximum_file_size보다 큰 파일은 SAX 스트리밍으로 파싱되며, SAX 환경에서는 **컬럼 세트(column set)**가 첫 번째 sample_size 레코드(기본값 10240)에서 엄격하게 가져옵니다. 이 창 이후에 처음 나타나는 자식 요소는 스키마에서 완전히 누락됩니다 — 이는 NULL 컬럼이 아니라 `Binder Error: Could not find key
| 시도 | 레코드 3001에서 처음 발견된 컬럼을 감지함 |
|---|---|
sample_size := 3000 | 아니요 |
| ... | |
sample_size := -1과 all_varchar는 자연스러운 추측이며 어느 것도 작동하지 않습니다: 둘 다 추론된 타입에 맞지 않는 out-of-sample 값을 처리합니다 (Configuration Options의 sample_size 참고). 이는 컬럼이 한 번도 발견되지 않은 경우와는 다른 문제입니다. 타입을 확장한다고 해서 누락된 컬럼이 추가되지는 않습니다. |
이는 자식이 선택 사항이면서 희귀한 문서(예: 샘플 크기를 초과할 만큼 충분히 긴 악보 속의 MusicXML 실수 사례)에 중요합니다.
🔄 형식 변환 (Format Conversions)
-- JSON API 응답을 XML로 변환
WITH api_data AS (
SELECT '{"users":[{"name":"Alice","age":25},{"name":"Bob","age":30}]}' as json_response
...
🔧 XML 처리 및 유틸리티 (XML Processing & Utilities)
-- 공백을 제거하여 XML 최소화(Minify)하기
SELECT xml_minify('<root>
<item>...
📊 고급 HTML 테이블 처리 (Advanced HTML Table Processing)
-- 예시: HTML 테이블에서 제품 데이터를 가져와 적절한 DuckDB 테이블로 만들기
-- 단계 1: raw 테이블 데이터를 얻기 위해 html_extract_tables 사용
...
출력 형식 (Output Formats)
🔗 링크 추출 결과 (Link Extraction Result)
html_extract_links(html) → LIST<STRUCT>
[
{
"text": "External Link",...
🖼️ 이미지 추출 결과 (Image Extraction Result)
html_extract_images(html) → LIST<STRUCT>
[
{
"alt": "Test Image",...
📊 테이블 추출 결과 (Table Extraction Result)
html_extract_tables(html) → TABLE(table_index, row_index, columns)
┌─────────────┬───────────┬───────────┐
│ table_index │ row_index │ columns │
├─────────────┼───────────┼───────────┤
...```
### 📈 **XML 통계 결과 (XML Statistics Result)**
```sql
xml_stats(xml) → STRUCT
{
"element_count": 15,
"attribute_count": 8,
...
고급 기능 (Advanced Features)
🎛️ 구성 옵션 (Configuration Options)
모든 파일 읽기 함수는 다음 매개변수를 지원합니다:
read_xml('pattern',
ignore_errors=true, -- 파싱할 수 없는 파일 건너뛰기
maximum_file_size=1048576, -- 바이트 단위 최대 파일 크기
...
매개변수 상세 정보:
AI 자동 생성 콘텐츠
본 콘텐츠는 개발 자료 GitHub · ec1fbd44의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기