2.1 Node.js로 크롤링하기

서버리스 크롤러의 파일 구조는 다음과 같습니다.

environment
└── serverless-crawler  : Crawler
   ├── handler.js  : Lambda에서 trigger하기 위한 handler
   ├── handler.test.js  : Local에서 handler를 trigger하기 위한 스크립트
   ├── config.yml : serverless.yml에서 사용하기 위한 변수
   ├── package.json
   └── serverless.yml : Serverless Framework config file


Cloud 9 콘솔에서 터미널을 열고 serverless-crawler 디렉터리를 생성하고 npm을 초기화합니다.

ec2-user:~/ $ cd ~/environment
ec2-user:~/environment $ mkdir serverless-crawler && cd  \    
                   ec2-user:~/environment/serverless-crawler $ npm init -y


다음으로는 npm module을 설치합니다. 개발을 위해서는 aws-sdk가 필요하므로 설치합니다. 참고로 람다Lambda는 aws-sdk를 기본적으로 포함하므로, aws-sdk는 dev-dependency에 넣어 배포시 제외되도록 합니다.

  • Dependencies
    • cheerio : HTML페이지를 파싱하고, 결과 데이터를 수집하기 위한 API를 제공합니다.

    • got : 사람에게 친숙하고 강력한 HTTP 요청 라이브러리로, HTTP를 쉽게 요청할 수 있다. 용량이 몇 MB에 불과하다.

    • dynamoose : DynamoDB를 사용하기 쉽도록 모델링하는 도구입니다.

  • Dev-Dependencies
    • aws-sdk : AWS 리소스를 사용하기 위한 SDK입니다.

    • serverless : 서버리스 프레임워크입니다.


$ npm i -S cheerio got dynamoose
$ npm i -D aws-sdk serverless

다음 파일을 소스 코드를 참고해 편집합니다.


  • serverless-crawler/config.yml

AWS_REGION: ap-northeast-2
STAGE: dev
DEPLOYMENT_BUCKET: USERNAME-serverless-hands-on-1    # USERNAME 수정 필요!


  • serverless-crawler/handler.js

const got = require('got');
const cheerio = require('cheerio');
const dynamoose = require('dynamoose');

require('aws-sdk').config.region = "ap-northeast-2";

const PortalKeyword = dynamoose.model('PortalKeyword', {
   portal: {
       type: String,
       hashKey: true
   },
   createdAt: {
       type: String,
       rangeKey: true
   },
keywords: {
type: Array
}
}, {
   create: false, // Create a table if not exist,
});

exports.crawler = async function (event, context, callback) {
try {
let naverKeywords = [];
let daumKeywords = [];

const result = await Promise.all([
got('https://naver.com'),
got('http://daum.net'),
]);
const createdAt = new Date().toISOString();

const naverContent = result[0].body;
const daumContent = result[1].body;
const $naver = cheerio.load(naverContent);
const $daum = cheerio.load(daumContent);

// Get doms containing latest keywords
$naver('.ah_l').filter((i, el) => {
return i===0;
}).find('.ah_item').each(((i, el) => {
if(i >= 20) return;
const keyword = $naver(el).find('.ah_k').text();
naverKeywords.push({rank: i+1, keyword});
}));
$daum('.rank_cont').find('.link_issue[tabindex=-1]').each((i, el) => {
const keyword = $daum(el).text();
daumKeywords.push({rank: i+1, keyword});
});

// console.log({
// naver: naverKeywords,
// daum: daumKeywords,
// });

await new PortalKeyword({
portal: 'naver',
createdAt,
keywords: naverKeywords
}).save();
await new PortalKeyword({
portal: 'daum',
createdAt,
keywords: daumKeywords
}).save();

return callback(null, "success");
} catch (err) {
callback(err);
}
}


  • serverless-crawler/handler.test.js

const crawler = require('./handler').crawler;

crawler({}, {}, (err, result) => {
   if(err) return console.error(err);
   console.log(result);
});


  • serverless-crawler/package.json

...

...

 "description": "AWSKRUG Serverless Group의 첫번째 핸즈온 Part.2 웹크롤러 만들기입니다.😁",

 "main": "index.js",

 //// 이 스크립트 영역을 복사해서 붙여넣어줍니다.

 "scripts": {

   "test": "node handler.test.js",

   "deploy": "serverless deploy"

 },

 ////

 "repository": {

   "type": "git",

   "url": "git+https://github.com/novemberde/serverless-crawler-demo.git"

 },

 "keywords": [],

 "author": "",

...


  • serverless-crawler/serverless.yml

service: ServerlessHandsOnPart2

provider:
 name: aws
 runtime: nodejs8.10
 memorySize: 256
 timeout: 30
 stage:  ${file(./config.yml):STAGE}
 region: ${file(./config.yml):AWS_REGION}
 deploymentBucket: ${file(./config.yml):DEPLOYMENT_BUCKET}
 environment:
   NODE_ENV: production
 iamRoleStatements:
   - Effect: Allow
     Action:
       - dynamodb:DescribeTable
       - dynamodb:Query
       - dynamodb:Scan
       - dynamodb:GetItem
       - dynamodb:PutItem
       - dynamodb:UpdateItem
       - dynamodb:DeleteItem
     Resource: "arn:aws:dynamodb:${opt:region, self:provider.region}:*:*"

functions:
 crawler:
   handler: handler.crawler
   events:
     - schedule: rate(10 minutes)