브라우저 자동화
Puppeteer를 사용하여 웹 브라우저를 자동화하고 동적 콘텐츠를 스크래핑하는 방법을 알아봅니다.
브라우저 자동화란?
브라우저 자동화는 실제 브라우저를 프로그래밍 방식으로 제어하는 기술입니다:
- 동적 웹사이트 스크래핑 (JavaScript 렌더링)
- 자동화된 테스트 수행
- 스크린샷 및 PDF 생성
- 폼 제출 및 클릭 자동화
Puppeteer 통합 설정
skills:
builtin:
- "browser-automation"
browser:
headless: true
timeout: 30000
기본 사용법
웹페이지 이동 및 스크린샷
export default {
name: 'browser-skill',
async execute(context) {
const { page } = await this.launchBrowser();
await page.goto('https://example.com', {
waitUntil: 'networkidle0'
});
await page.screenshot({ path: 'workspace/screenshot.png' });
return { success: true, result: '스크린샷 저장 완료' };
}
};
페이지 내용 추출
export default {
name: 'content-scraper',
async execute(context) {
const { page } = await this.launchBrowser();
await page.goto('https://news.example.com');
const title = await page.$eval('h1', el => el.textContent);
const links = await page.$$eval('a', anchors =>
anchors.map(a => ({ text: a.textContent, href: a.href }))
);
return { success: true, data: { title, links } };
}
};
동적 콘텐츠 스크래핑
JavaScript 렌더링 대기
async execute(context) {
const { page } = await this.launchBrowser();
await page.goto('https://dynamic-app.example.com');
// 특정 요소가 나타날 때까지 대기
await page.waitForSelector('.data-loaded', { timeout: 10000 });
const data = await page.evaluate(() => window.app.getData());
return { success: true, data };
}
무한 스크롤
async execute(context) {
const { page } = await this.launchBrowser();
await page.goto('https://infinite-scroll.example.com');
let previousHeight = 0;
let items = [];
while (true) {
const currentHeight = await page.evaluate(() => document.body.scrollHeight);
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
await page.waitForTimeout(2000);
const newItems = await page.$$eval('.item', elements =>
elements.map(el => ({ title: el.textContent, url: el.href }))
);
items = [...items, ...newItems];
if (currentHeight === previousHeight) break;
previousHeight = currentHeight;
}
return { success: true, items };
}
폼 자동화
로그인 자동화
async execute(context) {
const { page } = await this.launchBrowser();
await page.goto('https://example.com/login');
await page.type('#username', 'myusername');
await page.type('#password', 'mypassword');
await page.click('#login-button');
await page.waitForNavigation();
await page.screenshot({ path: 'workspace/after-login.png' });
return { success: true, result: '로그인 완료' };
}
검색 자동화
async execute(context) {
const { page } = await this.launchBrowser();
await page.goto('https://google.com');
await page.type('textarea[name="q"]', 'OpenClaw AI');
await page.keyboard.press('Enter');
await page.waitForSelector('.g');
const results = await page.$$eval('.g', items =>
items.map(item => ({
title: item.querySelector('h3')?.textContent,
link: item.querySelector('a')?.href
}))
);
return { success: true, results };
}
PDF 및 스크린샷
PDF 생성
await page.pdf({
path: 'workspace/article.pdf',
format: 'A4',
printBackground: true,
margin: { top: '1cm', right: '1cm', bottom: '1cm', left: '1cm' }
});
전체 페이지 스크린샷
await page.screenshot({
path: 'workspace/full-page.png',
fullPage: true
});
성능 최적화
불필요한 리소스 차단
await page.setRequestInterception(true);
page.on('request', (request) => {
if (['image', 'font', 'media'].includes(request.resourceType())) {
request.abort();
} else {
request.continue();
}
});
병렬 처리
const urls = ['https://site1.com', 'https://site2.com', 'https://site3.com'];
const { browser } = await this.launchBrowser();
const results = await Promise.all(
urls.map(async (url) => {
const page = await browser.newPage();
await page.goto(url);
const title = await page.title();
await page.close();
return { url, title };
})
);
await browser.close();
모범 사례
특정 조건을 기다리는 방법을 사용합니다:
// 올바른 예 - 특정 조건 대기
await page.waitForSelector('.loaded');
await page.waitForNavigation();
브라우저를 반드시 닫습니다:
try {
const { page } = await this.launchBrowser();
await page.goto(url);
} finally {
await page.close();
}
동적 콘텐츠 추출 전 대기:
await page.goto(url);
await page.waitForSelector('.data');
const data = await page.$eval('.data', el => el.textContent);
참고: