개발 창고/NodeJS

[Puppeteer] iframe 클릭하기

로이제로 2023. 1. 26. 22:00
반응형
const frameHandle   = await page.$("iframe");
const frame         = await frameHandle.contentFrame();
const input         = await frame.$eval("input[name=input2]", ele => ele.value);

 

 데이터를 자동으로 수집하거나 입력 또는 클릭 등의 수행을 하기 위해서 주로 puppeteer를 사용합니다. 기존 글에서 puppeteer에 대해서 다룬 글들이 있으니 참고하셔도 좋습니다.

 

2022.10.03 - [개발 창고/NodeJS] - [Puppeteer] Element Exists

 

[Puppeteer] Element Exists

// 아이템 체크 let exists = false; try { // 현재 selector >> "#gnb .group_nav .list_nav .nav_item" exists = await page.$eval("#gnb .group_nav .list_nav .nav_item", ele => ele?true:false); }catch (e){ // 에러 처리 console.log(e); } puppeteer를

royzero.tistory.com

 

2022.09.26 - [개발 창고/NodeJS] - [Puppeteer] 현재 페이지 URL 가져오기

 

[Puppeteer] 현재 페이지 URL 가져오기

puppeteer에서 현재 페이지를 가져오려면 page.url() 을 쓰면 됩니다. const puppeteer = require('puppeteer'); (async function main() { try { const browser = await puppeteer.launch({ headless : false, executablePath : '/Applications/Google Ch

royzero.tistory.com

 

오늘은 동일 페이지 내에서 발생하는 iframe 내에서의 오류에 대해 이야기해볼까 합니다.

 

일반적으로 데이터는 $eval을 통하여 가져올 수 있습니다.

(물론 evaluate나 다른 함수들을 통하여도 가능하지만 오늘은 데이터 추출을 위한 예시로 $eval을 사용할 예정입니다.)

 

 아래와 같이 동일 페이지 내에 두개의 html을 두고 테스트해보도록 하겠습니다.

 

(root)
┣ iframe.js
┣ iframe01.html
┗ iframe02.html

 

 현재 작업 파일은 iframe.js이고, 읽어들이려는 파일은 iframe01.html입니다. 그리고 iframe02.html은 iframe02.html에 포함된 iframe에서 호출한 파일입니다.

 

<!-- iframe01.html -->
<html>
    <body>
        <input type="text" name="input1" value="Hello"/>
        
        <iframe src="./iframe02.html"></iframe>
    </body>
</html>
<!-- iframe02.html -->
<html>
    <body>
        <input type="text" name="input2" value="World"/>
    </body>
</html>

 페이지에는 별도의 CSS 작업을 하지 않았으므로 iframe01.html을 들어가 보면 아래와 같이 보여질 겁니다.

 Hello가 들어있는 input1은 일반 페이지의 input이고, World가 들어있는 input2는 네모난 박스 영역이 iframe으로 된 부분입니다. (이 부분은 css로 충분히 감춰지는 부분이므로 소스에서 확인하여 iframe내에 있는지 파악하는 것이 적합합니다.)

 

 이때 일반적으로 input 값을 가져오기 위해서는 아래와 같이 수행하면 됩니다.

const input    = await page.$eval("input[name=input1]", ele => ele.value);

input1에 대해 구현한 내용
input1에 대한 호출 결과

 

 하지만 "World"가 들어있는 input2의 경우 동일하게 호출하면 아래와 같은 오류가 발생합니다.

input1처럼 구현된 input2
찾지 못함

 ※ 만약 해당 오류가 발생하지 않는다면 iframe이 아닌 페이지 내 어느 곳엔가 동일한 selector가 존재할 수 있습니다.

 

 이 경우에는 browser에서 파생된 page에서 찾을 수 없고, page에서 파생된 frame에서 찾아야 합니다.

const frameHandle   = await page.$("iframe");
const frame               = await frameHandle.contentFrame();
const input                = await frame.$eval("input[name=input2]", ele => ele.value);

 1/ page에서 해당 iframe selector를 갖는 frame Handler 조회

    ※ 현재는 iframe이 한 개 이기 때문에 selector로 iframe만 사용했지만, 상황에 따라 iframe id 또는 name을 지정해주어야 합니다.

 2/ 조회한 frameHandle에서 HTMLIFrameElement를 조회

 3/ 해당 HTMLIFrameElement에서 해당 input2를 조회

frame에서 input2를 찾도록 구현한 내용
정상 호출 결과

 

테스트를 위해 작성한 전체 iframe.js파일 내용은 아래와 같습니다.

const puppeteer = require("puppeteer"); // Crawling을 테스트를 위한 API
const path      = require("path");      // 로컬 경로에 있는 iframe01.html 접근하기 위해 사용된 path API

(async() => {
    const browser   = await puppeteer.launch({ headless:false });   // 브라우저 생성
    const page      = await browser.newPage();                      // 브라우저 내 신규 페이지 생성

    // Step. Test페이지로 이동 (동일 파일 내 iframe01.html로 이동)
    const html      = path.resolve(__dirname, "iframe01.html");
    await page.goto(html);
    
    // Step. 일반적인 방법의 input value값 추출
    console.log("############### INPUT 1. 기본 input value 호출 ###############")
    try{
        const input    = await page.$eval("input[name=input1]", ele => ele.value);
        console.log(input);
    }catch(e){
        console.log(e);
    }

    // Step. iframe 내 input value값 추출 (오류)
    console.log("############### INPUT 2. iframe 내 input value 호출 (오류) ###############")
    try{
        const input    = await page.$eval("input[name=input2]", ele => ele.value);
        console.log(input);
    }catch(e){
        console.log(e);
    }

    // Step. iframe 내 input value값 추출 (정상)
    console.log("############### INPUT 2. iframe 내 input value 호출 (정상) ###############")
    try{
        const frameHandle   = await page.$("iframe");
        const frame         = await frameHandle.contentFrame();
        const input         = await frame.$eval("input[name=input2]", ele => ele.value);
        console.log(input);
    }catch(e){
        console.log(e);
    }
})();

반응형