231118 개발일지 TIL - 프론트단(클라이언트단에서 해외 사이트 서버 크롤링시 CORS 오류) cors 우회 방법

The Web On Everything·2023년 11월 18일
0

개발일지

목록 보기
192/269
post-thumbnail

프론트단(클라이언트단에서 해외 사이트 서버 크롤링시 CORS 오류)

  • CORS 오류
    크롤링한 외부 서버 데이터 html 안의 이미지 불러오기
  1. iframe으로 html을 불러오면 크롤링 해오는 서버의 스타일을 수정할 수 없음
<div id="imageContainer" class="sp_vbox img_fix2"></div>
// iframe 방식
let detailUrl = data.result.globalData.detailModel.detailUrl;
let imageContainer = document.getElementById('imageContainer');
imageContainer.innerHTML = `<iframe src="${detailUrl}" style="width: 100%; height: 100%;">${detailUrl}</iframe>`;
  1. 상세페이지의 상품정보 이미지를 보여주기 위해 작성
"detailUrl": "https://[해외에서 크롤링 해오는 url]" 

detailUrl에 있는 html페이지의 이미지들을 보여주는 코드 작성 (CORS 오류 발생)

// detailUrl내 img 가져오기
let detailUrl = data.result.globalData.detailModel.detailUrl;
fetch(detailUrl)
    .then(response => response.text())
    .then(html => {
        let parser = new DOMParser();
        let doc = parser.parseFromString(html, 'text/html');
        let imageTags = doc.getElementById('img');

        let imageContainer = document.getElementById('imageContainer');
        imageContainer.innerText = '';

        for(let i = 0; i < imageTags.length; i++) {
            imageContainer.appendChild(imageTags[i]);
        }
    })
  1. html2canvas로 페이지를 캡쳐해서 불러오려고 시도(CORS 오류 발생)
<script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>
let detailUrl = data.result.globalData.detailModel.detailUrl;
fetch(detailUrl)
    .then(response => response.text())
    .then(html => {
        let parser = new DOMParser();
        let doc = parser.parseFromString(html, 'text/html');

        document.body.appendChild(doc.body);

        html2canvas(doc.body).then(canvas => {
            document.body.appendChild(canvas);
        });
    });
  1. URL의 리소스를 가져와 텍스트로 변환
    DOMParser를 사용해 html 파싱 후 img태그를 모두 찾아서 보여지게 시도(CORS 오류 발생)
<script src="https://cdn.jsdelivr.net/npm/node-http-server@8.1.5/server/Server.min.js"></script>
async function fetchHTML(url) {
    try {
        let response = await fetch(url);
        let data = await response.text();
        return data;
    } catch (err) {
        console.error(err);
    }
}

function fetchItemDetail(itemId) {
    fetch(`http://192.168.0.158:8000/getItemDetail?id=${itemId}`)
        .then(response => response.json())
        .then(data => {
            let detailUrl = data.result.globalData.detailModel.detailUrl;
            return fetchHTML(detailUrl);
        })
        .then(html => {
            let parser = new DOMParser();
            let doc = parser.parseFromString(html, 'text/html');

            let imageTags = doc.getElementsByTagName('img');
            let imageContainer = document.getElementById('imageContainer');

            for(let i = 0; i < imageTags.length; i++) {
                imageContainer.appendChild(imageTags[i]);
            }
        })
        .catch(error => console.error('Error:', error));
}
  1. JSONP(JSON with Padding) CORS 우회하여 데이터 받아오기 실패(악의적인 서버가 클라이언트에서 스크립트를 실행가능하다고 해서 제외)
    <script>
        // JSONP callback function
        function handleResponse(response) {
            console.log(response);
            // 이미지 URL을 추출하는 코드 추가 필요
        }

        // JSONP request
        const script = document.createElement('script');
        script.src = `https://[url]/api?callback=handleResponse`;
        document.body.appendChild(script);
    </script>

해결된 방법

  1. puppeteer.js를 사용해 Google Chrome 또는 Chromium 웹 브라우저를 제어할 수 있어 이 방법으로 cors 정책을 우회해서 가져왔다.

html 내에 puppeteer.js를 사용해 크롬이나 크로미움 웹 브라우저를 띄워 해외 사이트를 보여주고 있다.

그러다 보니 해외 사이트도 느리고 불러오는 데이터도 많은데 그 안에 html을 또 띄우는 작업을 하다보니 fetch 데이터 불러오기 오류가 자꾸 발생하고 있다.

온로드시 주어진 url을 프록시 서버에 post 요청을 보내 html 콘텐츠를 가져와 DOMParser를 사용해 html 문서로 변환한다.
이렇게 하면 그 안에 내용의 css도 수정 가능하다.

window.onload = function () {
	async function fetchHTML(url) {
		try {
			console.log("fetchHTML");
			console.log(url);

			const proxy = "[프록시 url]";
			const data = {"url": url};

			const response = await fetch(proxy, {
				method: 'POST',
				headers: {
					'Content-Type': 'application/x-www-form-urlencoded',
				},
				body: new URLSearchParams(data).toString()
			});

			const htmlContent = await response.text();
			console.log("ajax_detail");
			console.log(htmlContent);

			const parser = new DOMParser();
			const doc = parser.parseFromString(htmlContent, 'text/html');

			// ID 'detail-root'의 요소
			const element = doc.getElementById('detail-root');
			const container = document.getElementById("imageContainer");

			// 단일 요소를 처리
			if (element) {
				// #detail-root 하위 모든 div에 너비 100%
				const divElements = element.querySelectorAll('div');
				for (let i = 0; i < divElements.length; i++) {
					divElements[i].style.width = '100%';
				}
				// 요소를 복제하여 삽입
				container.appendChild(element.cloneNode(true));
			} else {
				console.log('#detail-root 요소를 찾을 수 없습니다.');
			}

		} catch (error) {
			console.error('Error:', error);
		}
	}

	function fetchItemDetail(itemId) {
		let data = { url: itemId };
		fetch('http://[url]', {
			method: 'POST',
			headers: {
				'Content-Type': 'application/json'
			},
			body: JSON.stringify(data)
		})
				.then(response => {
					console.log(response)
					if (!response.ok) {
						throw new Error('네트워크 응답 오류');
					}
					return response.json();
				})
				.then(data => {

					let detailUrl = data.result.globalData.detailModel.detailUrl;


					return fetchHTML(detailUrl);
				})
				.catch(error => console.error('Error:', error));
	}
 } 

느낀 점
백엔드에서도 해당 페이지 크롤링시 html내에 코드를 긁어오니 js라이브러리를 사용해 불러오는 것으로 이미지 태그나 html내의 코드의 내용들을 가져올 수 없는 현상들이 일어났는데 Puppeteer.js를 사용해 cors 정책을 우회해 데이터를 가져올 수 있다니 신세계였다. 근데 로그가 찍히고 자꾸 요청하다보니 막히는 현상이 일어나는 것 같았다. 작업하고 있는 우리 쪽 ip도 계속 찍힐 거고...

profile
오늘은 무슨 오류를 만날까?! 널 만나러 가는 길~ LOL

0개의 댓글