티스토리 뷰
반응형
참고
- puppeteer로 크롤링을 연습하던 중 다음과 같은 이슈가 있었다.
- 1. docker-compose up시 node main.js가 실행되지 않음.
- 2. 1.을 해결했더니 puppeteer.launch가 동작하지 않음.
- 3. 2.를 해결했더니 screenshot의 한글이 깨짐.
- 4. 3.을 해결했더니 screenshot 이미지가 일부만 찍히고 나머지는 하얗게 잘림.
코드
1. node main.js가 실행되지 않는 이슈
- WebStorm에서는 정상 동작하는 것이 유독 Docker image를 생성한 후에는 실행되지 않고 있었다.
- 트러블 슈팅 이전의 Dockerfile은 다음과 같았다.
FROM node:14-alpine
MAINTAINER ingnoh "ingnoh@tistory.com"
WORKDIR /crawler
COPY ./main.js ./
COPY ./package*.json ./
COPY ./configs/config.json ./configs/config.json
RUN apk update
RUN apk upgrade
RUN apk --no-cache add tzdata && \
cp /usr/share/zoneinfo/Asia/Seoul /etc/localtime && \
echo "Asia/Seoul" > /etc/timezone
RUN npm install
CMD [ "npm", "start" ]
- 원인은 너무 간단하게도, 아래와 같이 alpine 이미지에 chromium이 설치되어 있지 않았기 때문이었다.
- puppeteer는 기본적으로 Chrome 브라우저를 사용하여 동작하는데, 정작 크롬이 없으니 당연한 결과였다...
[~] # 테스트용 node:14-alpine 컨테이너 실행
[~] docker run -d node:14-alpine sleep 10000
Unable to find image 'node:14-alpine' locally
14-alpine: Pulling from library/node
ddad3d7c1e96: Already exists
9e293fdbeaa6: Already exists
22be2f3b7648: Already exists
d7a13222786c: Already exists
Digest: sha256:cc1a31b2f4a3b8e9cdc6f8dc0c39a3b946d7aa5d10a53439d960d4352b2acfc0
Status: Downloaded newer image for node:14-alpine
8f7f7025886eafc2a43ed0a71284fc687e3315b0fe266d09f0199bac23a9c09e
[~] # 생성된 컨테이너에 chromium이 존재하는지 확인
[~] docker exec -it 8f7f sh
/ # cd /usr/local/bin
/usr/local/bin # ls
docker-entrypoint.sh node nodejs npm npx yarn yarnpkg
- Dockerfile을 다음과 같이 수정하여 chromium을 설치해주자.
FROM node:14-alpine
MAINTAINER ingnoh "ingnoh@tistory.com"
WORKDIR /crawler
COPY ./main.js ./
COPY ./package*.json ./
COPY ./configs/config.json ./configs/config.json
RUN apk update
RUN apk upgrade
# 아래를 추가
RUN apk add --no-cache udev ttf-freefont chromium
RUN apk --no-cache add tzdata && \
cp /usr/share/zoneinfo/Asia/Seoul /etc/localtime && \
echo "Asia/Seoul" > /etc/timezone
RUN npm install
CMD [ "npm", "start" ]
- 또한 launch 시에도 다음과 같이 작성하여 chromium 경로를 사용하도록 설정한다.
/* 수정 전
const browser = await puppeteer.launch();
*/
// 수정 후
const browser = await puppeteer.launch({
executablePath: '/usr/bin/chromium-browser',
});
2. puppeteer.launch API가 동작하지 않는 이슈
- 작성된 main.js 코드는 다음과 같았다.
// 전략
async function crawling() {
const full_path = prefix + host + path;
let result = 0;
console.log(`\n${++count}. crawling ${getTime()}`);
console.log('[START] start crawling with target: ' + full_path);
console.log('[OPEN] launch new headless browser!');
const browser = await puppeteer.launch({
executablePath: '/usr/bin/chromium-browser',
});
const page = await browser.newPage();
// 후략
- 그러나 docker-compose up으로 인한 실행 결과는 다음과 같았으며, 코드가 더 이상 실행되지 않고 freeze 되는 현상이 발생했다.
gt-crawler | 1. crawling 2021/6/18 11:1:0
gt-crawler | [START] start crawling with target: https://url.com
gt-crawler | [OPEN] launch new headless browser!
# 이후 더 이상 동작하지 않음!
- 원인은 Chrome의 기본 설정에 있었다. 다음의 링크를 참고하자.
- 기본적으로, 도커는 컨테이너를 실행할 때 64MB 공유 메모리인 /dev/shm를 사용한다.
- 이는 일반적으로 Chrome을 실행하기에는 너무나도 적으며, 이는 Chrome이 커다란 페이지들을 렌더링할 때 crash되는 원인이 된다.
- 이를 해결하려면, docker run --shm-size=1gb 명령어를 사용하여 /dev/shm 크기를 키워준다.
- Chrome 65부터는 이러한 작업도 필요 없으며, 대신 브라우저를 실행할 때 --disable-dev-shm-usage 플래그를 사용한다.
const browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-dev-shm-usage'],
});
- 해당 플래그는 공유 메모리 파일들을 /dev/shm이 아닌 /tmp에 쓰도록 동작한다.
- 코드에 이를 반영하였으며, 다음과 같이 수정한 결과 Docker container를 활용한 웹 브라우저 사용에 성공하였다.
/* 수정 전
const browser = await puppeteer.launch({
executablePath: '/usr/bin/chromium-browser',
});
*/
// 수정 후
const browser = await puppeteer.launch({
executablePath: '/usr/bin/chromium-browser',
args: ['--no-sandbox', '--disable-dev-shm-usage']
});
3. screenshot의 한글이 깨지는 이슈
- alpine에는 한글 관련 폰트가 설치되지 않는다. 다음과 같이 Dockerfile을 수정하여 한글 폰트를 설치해주자!
FROM node:14-alpine
MAINTAINER ingnoh "ingnoh@tistory.com"
WORKDIR /crawler
COPY ./main.js ./
COPY ./package*.json ./
COPY ./configs/config.json ./configs/config.json
RUN apk update
RUN apk upgrade
RUN apk add --no-cache udev ttf-freefont chromium
RUN apk --no-cache add tzdata && \
cp /usr/share/zoneinfo/Asia/Seoul /etc/localtime && \
echo "Asia/Seoul" > /etc/timezone
# 여기서부터 추가
RUN mkdir /usr/share/fonts/nanumfont
RUN wget http://cdn.naver.com/naver/NanumFont/fontfiles/NanumFont_TTF_ALL.zip
RUN unzip NanumFont_TTF_ALL.zip -d /usr/share/fonts/nanumfont
RUN fc-cache -f -v
ENV LANG=ko_KR.UTF-8
ENV LANGUAGE=ko_KR.UTF-8
# 여기까지 추가
RUN npm install
CMD [ "npm", "start" ]
4. screenshot 이미지가 일부만 찍히고 나머지는 하얗게 잘리는 이슈
- 모든 기능이 정상 동작하는 것으로 보이나, 저장된 스크린샷이 일부만 정상적으로 저장되며 나머지는 하얀 공백으로 찍히는 문제가 있었다.
- 정확히는 스크린샷의 가로 길이에는 문제가 없었으나 세로 길이가 충분치 않게 저장되는 상황이었음!
- 또한 screenshot()메소드의 fullPage 역시 true로 설정한 상황이었다.
- page.setViewport의 값을 임의로 변경할 때마다 스크린샷이 정상적으로 저장되는 범위가 달라지는 것으로 미루어 보았을 때, 뷰포트와 관련된 문제일 것이라고 추측하였기에 default viewport를 다음과 같이 확인해보기로 했다.
/* 수정 전
await page.goto(crawling_target)
await page.screenshot({
path: `./screenshots/notice.png`,
fullPage: true
});
*/
// 수정 후
await page.goto(crawling_target)
// 아래 세 줄을 추가하였다.
const { width, height } = await page.viewport();
console.log(`[WIDTH] width length: ${width}`);
console.log(`[HEIGHT] height length: ${height}`);
await page.screenshot({
path: `./screenshots/notice.png`,
fullPage: true
});
- viewport() 메소드는 현재 페이지의 설정을 가져온다. 실제 실행 중 console.log에 의해 출력된 결과는 다음과같다.
// 전략
gt-crawler | [WIDTH] width length: 800
gt-crawler | [HEIGHT] height length: 600
// 후략
- 결국 viewport의 크기가 너무 작아 이미지가 잘리는 문제였으며, 앞서 추가한 세 줄을 지운 후 다음과 같이 수정하여 해결하였다.
/* 수정 전
const crawling_target = prefix + host + href_path;
await page.goto(crawling_target)
await page.screenshot({
path: `./screenshots/notice.png`,
fullPage: true
});
*/
// 수정 후
const crawling_target = prefix + host + href_path;
await page.goto(crawling_target)
// 아래 두 줄을 추가한다.
const height = await page.evaluate(() => document.body.scrollHeight);
await page.setViewport({width:800, height});
await page.screenshot({
path: `./screenshots/notice.png`,
fullPage: true
});
- evaluate() 메소드는 headless 브라우저 내에서 javascript 코드를 실행할 수 있도록 한다.
- goto() 메소드를 통해 페이지로 이동한 후 document.body.scrollHeight를 사용하여 페이지의 세로 크기를 구한다.
- setViewport() 메소드를 통해 페이지의 가로, 세로 크기를 지정한다.
- 이러한 수정 작업 이후의 screenshot은 성공적으로 전체 페이지를 저장하였다!
5. 정리
- 이번 작업에서 조치한 방법은 다음과 같다.
- chrome 브라우저가 설치되어 있는지 확인.
- --disable-dev-shm-usage 설정 확인.
- 한글 폰트 설치 여부 확인.
- viewport 수정.
'Dev. > Node.js' 카테고리의 다른 글
[Node.js] npx란? (짧) (0) | 2021.07.27 |
---|---|
[Node.js] nvm으로 버전 변경하기 (0) | 2021.07.26 |
[Node.js] puppeteer 모바일 (0) | 2021.06.18 |
[Node.js] package.json 기초 (0) | 2021.06.16 |
[Node.js] hello world 웹 서버 만들기 (0) | 2021.03.31 |
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- terraform
- javascript
- hashicorp
- kotlin
- Node.js
- 코딩테스트
- JEST
- Java
- spring boot
- JPA
- postgresql
- Gradle
- Puppeteer
- AWS
- eureka
- AWS IoT
- pgloader
- Git
- shell
- Vault
- mysql
- IntelliJ
- Linux
- dev
- etc
- react
- Spring Cloud Config
- Database
- jQuery
- Docker
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
글 보관함