문서

문서

ActiCrawl을 사용하여 웹 스크래핑 워크플로를 자동화하는 방법을 알아보세요

데이터 추출

ActiCrawl의 강력한 추출 엔진을 사용하여 웹 페이지에서 구조화된 데이터를 추출하는 방법을 익히세요. 간단한 CSS 선택자부터 고급 AI 기반 추출까지 다양한 기술을 배워보세요.

추출 기초

ActiCrawl은 웹 페이지에서 데이터를 추출하는 여러 방법을 지원합니다:

  • CSS 선택자: 표준 CSS 구문을 사용한 요소 선택
  • XPath: 고급 경로 기반 선택
  • JSON-LD: JSON-LD 스크립트에서 구조화된 데이터 추출
  • 정규식 패턴: 정규 표현식을 사용한 텍스트 추출
  • AI 추출: AI가 데이터를 이해하고 지능적으로 추출

CSS 선택자 추출

가장 일반적이고 간단한 방법:

javascript
const result = await client.scrape({
  url: 'https://example.com/product',
  extract: {
    title: 'h1.product-title',
    price: '.price-current',
    description: '.product-description',
    image: 'img.main-image@src',
    rating: '.rating@data-rating'
  }
});

console.log(result.extracted);
// {
//   title: "프리미엄 무선 헤드폰",
//   price: "₩399,000",
//   description: "고품질 오디오...",
//   image: "https://example.com/img/product.jpg",
//   rating: "4.5"
// }

속성 추출

@attribute를 사용하여 특정 속성 추출:

javascript
extract: {
  imageUrl: 'img#product-image@src',
  imageAlt: 'img#product-image@alt',
  linkHref: 'a.product-link@href',
  dataId: 'div.product@data-product-id',
  metaDescription: 'meta[name="description"]@content'
}

다중 요소

요소 배열 추출:

javascript
extract: {
  // 단일 요소
  title: 'h1',

  // 다중 요소
  features: {
    selector: 'li.feature',
    multiple: true
  },

  // 중첩 추출
  reviews: {
    selector: '.review',
    multiple: true,
    extract: {
      author: '.reviewer-name',
      rating: '.stars@data-rating',
      comment: '.review-text',
      date: '.review-date'
    }
  }
}

XPath 추출

CSS로 처리할 수 없는 복잡한 선택:

python
result = client.scrape(
    url='https://example.com/article',
    extract={
        # 특정 레이블 뒤의 텍스트
        'author': '//span[text()="작성자:"]/following-sibling::text()',

        # 헤더별 테이블 셀
        'price': '//th[text()="가격"]/following-sibling::td/text()',

        # 복잡한 조건
        'in_stock': '//div[@class="availability" and contains(text(), "재고 있음")]',

        # 부모 탐색
        'category': '//li[@class="current"]/parent::ul/@data-category'
    }
)

고급 추출 패턴

테이블 추출

테이블에서 구조화된 데이터 추출:

javascript
const tableData = await client.scrape({
  url: 'https://example.com/data',
  extract: {
    table: {
      selector: 'table#data-table',
      type: 'table',
      headers: 'auto', // 또는 지정: ['이름', '가격', '재고']
      skipRows: 1 // 헤더 행 건너뛰기
    }
  }
});

// 결과:
// [
//   { 이름: "제품 A", 가격: "₩100,000", 재고: "재고 있음" },
//   { 이름: "제품 B", 가격: "₩200,000", 재고: "재고 없음" }
// ]

리스트 추출

구조화된 리스트 추출:

python
extract = {
    'products': {
        'selector': '.product-grid .product-card',
        'type': 'list',
        'extract': {
            'name': 'h3',
            'price': '.price',
            'image': 'img@src',
            'specs': {
                'selector': '.spec',
                'multiple': True
            }
        }
    }
}

페이지네이션 데이터

여러 페이지에 걸친 데이터 추출:

javascript
const crawler = await client.crawl({
  startUrl: 'https://example.com/products?page=1',
  pagination: {
    nextSelector: 'a.next-page@href',
    maxPages: 10
  },
  extract: {
    products: {
      selector: '.product',
      multiple: true,
      extract: {
        name: '.product-name',
        price: '.product-price'
      }
    }
  }
});

텍스트 처리

깨끗한 텍스트 추출

노이즈를 제거하고 깨끗한 텍스트 얻기:

javascript
extract: {
  articleText: {
    selector: 'article',
    textOnly: true,
    clean: true // 추가 공백, 광고 등 제거
  },

  // 사용자 정의 정리
  description: {
    selector: '.description',
    process: (text) => {
      return text
        .replace(/\s+/g, ' ')
        .trim()
        .substring(0, 200);
    }
  }
}

정규식 추출

정규식 패턴을 사용한 추출:

python
extract = {
    'phone': {
        'selector': '.contact',
        'regex': r'\b\d{3}[-.]?\d{3}[-.]?\d{4}\b'
    },
    'email': {
        'selector': '.contact',
        'regex': r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}'
    },
    'price': {
        'selector': '.price-text',
        'regex': r'₩[\d,]+\.?\d*',
        'type': 'float'  # 숫자로 변환
    }
}

JSON-LD와 마이크로데이터

페이지에 포함된 구조화된 데이터 추출:

javascript
const result = await client.scrape({
  url: 'https://example.com/product',
  extract: {
    // JSON-LD에서 추출
    structured: {
      selector: 'script[type="application/ld+json"]',
      type: 'json',
      parse: true
    },

    // 마이크로데이터 추출
    product: {
      selector: '[itemtype="https://schema.org/Product"]',
      microdata: true
    }
  }
});

AI 기반 추출

AI가 데이터를 이해하고 추출하도록 하기:

javascript
const result = await client.scrape({
  url: 'https://example.com/article',
  aiExtract: {
    // 자연어 쿼리
    author: "이 글의 작성자는 누구인가요?",
    publishDate: "언제 게시되었나요?",
    mainPoints: "주요 포인트는 무엇인가요? (목록)",
    sentiment: "전반적인 감정은 어떤가요?",

    // 구조화된 추출
    product: {
      query: "제품 정보를 추출하세요",
      schema: {
        name: "string",
        price: "number",
        features: "array",
        available: "boolean"
      }
    }
  }
});

사용자 정의 AI 프롬프트

python
ai_extract = {
    'summary': {
        'prompt': '이 기사를 3개의 요점으로 요약하세요',
        'max_tokens': 150
    },
    'entities': {
        'prompt': '언급된 모든 회사명, 사람, 위치를 추출하세요',
        'format': 'json'
    },
    'classification': {
        'prompt': '이 콘텐츠를 다음 중 하나로 분류하세요: 뉴스, 블로그, 제품, 문서',
        'choices': ['뉴스', '블로그', '제품', '문서']
    }
}

데이터 변환

타입 변환

추출된 데이터를 적절한 타입으로 변환:

javascript
extract: {
  price: {
    selector: '.price',
    type: 'number', // "₩29,900"을 29900으로 변환
    currency: 'KRW'
  },
  inStock: {
    selector: '.availability',
    type: 'boolean', // "재고 있음"을 true로 변환
    truthy: ['재고 있음', '사용 가능']
  },
  rating: {
    selector: '.stars',
    type: 'float',
    attribute: 'data-rating'
  },
  publishDate: {
    selector: '.date',
    type: 'date',
    format: 'YYYY-MM-DD'
  }
}

사용자 정의 변환

사용자 정의 처리 함수 적용:

python
def process_price(value):
    # 통화 기호 제거 및 float 변환
    return float(value.replace('', '').replace(',', ''))

def normalize_date(value):
    # 다양한 날짜 형식 변환
    from dateutil import parser
    return parser.parse(value).isoformat()

extract = {
    'price': {
        'selector': '.price',
        'transform': process_price
    },
    'date': {
        'selector': '.published',
        'transform': normalize_date
    }
}

조건부 추출

조건에 따른 추출:

javascript
extract: {
  // 요소가 존재하는 경우 추출
  salePrice: {
    selector: '.sale-price',
    optional: true
  },

  // 조건부 추출
  availability: {
    conditions: [
      {
        selector: '.in-stock',
        exists: true,
        value: '재고 있음'
      },
      {
        selector: '.out-of-stock',
        exists: true,
        value: '재고 없음'
      }
    ],
    default: '알 수 없음'
  },

  // 대체 선택자
  title: {
    selectors: [
      'h1.product-title',
      'h2.title',
      'meta[property="og:title"]@content'
    ]
  }
}

성능 최적화

선택적 추출

필요한 것만 추출:

javascript
// 나쁨 - 전체 페이지를 추출한 후 필터링
const result = await client.scrape({
  url: 'https://example.com',
  format: 'json'
});
const title = result.content.querySelector('h1').text;

// 좋음 - 필요한 데이터만 추출
const result = await client.scrape({
  url: 'https://example.com',
  extract: {
    title: 'h1'
  }
});

배치 추출

여러 URL에서 효율적으로 추출:

python
urls = [
    'https://example.com/product/1',
    'https://example.com/product/2',
    'https://example.com/product/3'
]

results = client.batch_scrape(
    urls=urls,
    extract={
        'name': 'h1',
        'price': '.price',
        'stock': '.availability'
    },
    concurrency=5
)

오류 처리

추출 실패를 우아하게 처리:

javascript
extract: {
  price: {
    selector: '.price',
    required: true,
    onError: 'skip' // 또는 'default' 또는 'fail'
  },

  description: {
    selector: '.description',
    default: '설명이 없습니다',
    maxLength: 500
  },

  images: {
    selector: 'img.product-image@src',
    multiple: true,
    validate: (urls) => urls.filter(url => url.startsWith('https'))
  }
}

실제 사례

이커머스 제품 추출

javascript
const productExtractor = {
  extract: {
    product: {
      name: 'h1[itemprop="name"]',
      brand: '[itemprop="brand"]',
      price: {
        selector: '[itemprop="price"]@content',
        type: 'number'
      },
      currency: '[itemprop="priceCurrency"]@content',
      availability: {
        selector: '[itemprop="availability"]@href',
        transform: (val) => val.includes('InStock')
      },
      images: {
        selector: '.product-images img@src',
        multiple: true,
        limit: 5
      },
      features: {
        selector: '.feature-list li',
        multiple: true
      },
      rating: {
        value: '[itemprop="ratingValue"]@content',
        count: '[itemprop="reviewCount"]'
      }
    }
  }
};

기사/블로그 추출

python
article_extractor = {
    'extract': {
        'article': {
            'title': 'h1.article-title',
            'author': '.author-name',
            'publishDate': {
                'selector': 'time[datetime]@datetime',
                'type': 'date'
            },
            'category': '.category a',
            'tags': {
                'selector': '.tag',
                'multiple': True
            },
            'content': {
                'selector': '.article-content',
                'clean': True,
                'markdown': True  # 마크다운으로 변환
            },
            'relatedArticles': {
                'selector': '.related-article',
                'multiple': True,
                'extract': {
                    'title': 'h3',
                    'url': 'a@href'
                }
            }
        }
    }
}

모범 사례

  1. 구체적인 선택자 사용: 더 구체적인 선택자가 더 빠르고 신뢰할 수 있습니다
  2. 추출된 데이터 검증: 항상 중요한 데이터를 검증하세요
  3. 누락된 데이터 처리: 기본값과 선택적 플래그를 사용하세요
  4. 선택자 테스트: 프로덕션 전에 여러 페이지에서 테스트하세요
  5. 변경 사항 모니터링: 추출 실패에 대한 알림을 설정하세요
  6. AI를 현명하게 사용: AI 추출은 강력하지만 더 비쌉니다

다음 단계