Scrapy를 활용한 crawler를 만든 후 firebase database에 저장하기

Scrapy framework를 활용하여 간단한 crawler를 만든 다음 그 결과물을 firebase database에 저장하는 예제를 만들어 보았습니다.

대상은 요새 자주 가는 okky.kr의 구인광고로 하였고 구인광고글의 제목, 내용, 작성일 혹은 수정일, 작성자를 가져오도록 하였습니다.


사전 준비 사항

  • scrapy에 대한 사전 지식 — 설치 및 사용법
  • firebase 계정 및 credential 파일
  • pyrebase 설치

구현

현재부터 2일전까지의 구인글을 가져와서 존재하지 않거나 존재하더라도 업데이트가 되었다면 db에 업데이트하도록 하였습니다.

import scrapy
import datetime
import re
import pyrebase
from scrapy.http import Request
from scrapy.exceptions import CloseSpider

class OkkySpider(scrapy.Spider):
name = "okky"
root_url = "https://okky.kr"
list_url = root_url + "/articles/recruit?max=20&sort=id&order=desc"
daydiff = 2
page = 20
offset = 0

def __init__(self):
#your own firebase config and credential
config = {
"apiKey": "apiKey",
"authDomain": "projectId.firebaseapp.com",
"databaseURL": "https://databaseName.firebaseio.com",
"storageBucket": "projectId.appspot.com",
"serviceAccount": "path/to/serviceAccountCredentials.json"
"serviceAccount": "credential.json"
}

firebase = pyrebase.initialize_app(config)
auth = firebase.auth()
token = auth.create_custom_token(config["apiKey"])
self.dbUser = auth.sign_in_with_custom_token(token)
self.db = firebase.database()

def start_requests(self):
yield Request(self.list_url, callback=self.parse)

def parse(self, response):
items = response.xpath('//li[re:test(@class, "^list-group-item")]')

for item in items:
heading_item = item.xpath('.//h5[re:test(@class, "^list-group-item-heading")]//a')
author_item = item.xpath('.//a[@class="nickname"]/text()')

title = heading_item.xpath('text()').extract()[0].strip()
detail_url = heading_item.xpath('@href').extract()[0].strip()
writer = author_item.extract()[0].strip()

#상세 페이지 요청
params = {'title': title, 'writer': writer}
yield Request(self.root_url + detail_url, callback=self.parse_detail, meta=params)

#다음 페이지
self.offset += self.page
yield Request(self.list_url + "&offset={}".format(self.offset), callback=self.parse)

def parse_detail(self, response):
key = re.findall('\d+', response.url)[0]
content = response.xpath('//article[@class="content-text"]').extract()[0]
timeagos = response.xpath('//span[@class="timeago"]/text()').extract()
write_time = timeagos[1] if len(timeagos) > 1 else timeagos[0]
write_time = datetime.datetime.strptime(write_time.strip(), '%Y-%m-%d %H:%M:%S.0')

#지정된 날짜 이상 차이나면 중단
diff = datetime.datetime.now() - write_time
if diff.days >= self.daydiff:
raise CloseSpider('day_exceeded')

params = {'title': response.meta['title'],
'writer': response.meta['writer'],
'content': re.sub('<.?article[^>]*>', '', content).strip()}

item = self.db.child("okky").child(key).get(self.dbUser['idToken'])

#존재하는 데이터인지 확인
if item.val() != None:
server_time = item.val()['write_time']
server_time = datetime.datetime.strptime(server_time, '%Y-%m-%dT%H:%M:%SZ')

#서버에 저장된 시간과 작성일이 다르면 업데이트
if write_time != server_time:
params['write_time'] = write_time.isoformat() + "Z"
params['status'] = 'updated'
self.db.child("okky").child(key).set(params, self.dbUser['idToken'])
#존재하지 않으면 신규
else:
params['write_time'] = write_time.isoformat() + "Z"
params['status'] = 'new'
self.db.child("okky").child(key).set(params, self.dbUser['idToken'])

결과

firebase database에 다음과 같이 저장되었습니다.

차후 과제

  • Information extraction from text
One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.