GitHub Actions으로 혈액정보 크롤링 만들기

2innnnn0·2021년 6월 6일
0

Github Action을 활용한 크롤러 웹 페이지 만들기 기반으로 작성된 문서입니다. 아주 간단한 CI Workflow를 구현해서 공유드립니다.

Github Action이란?

GitHub Actions makes it easy to automate all your software workflows, now with world-class CI/CD. Build, test, and deploy your code right from GitHub. Make code reviews, branch management, and issue triaging work the way you want.

GitHub Actions를 사용하면 이제 세계적인 CI/CD를 통해 모든 소프트웨어 워크플로우를 쉽게 자동화할 수 있습니다. GitHub에서 바로 코드를 빌드, 테스트 및 배포합니다. 코드 검토, 지점 관리 및 문제 해결 작업을 원하는 대로 수행할 수 있습니다.
  • 용어 개념
    • CI
      • Continuous Integration
      • 테스트와 빌드를 자동으로 진행하는 프로세스
    • CD
      • Continuous Deploy, Continuous Delivery
      • 배포 자동화
    • 대표적으로 Jenkins, Travis CI가 있음.
  • 언제 사용하는가?
    • 테스트 코드
      • DA는 데이터가 정확하게 잘 들어가는지 정합성 체크할 때 사용할 수 있음.
    • 배포
    • 자동화 스크립트 (크롤링)
    • 다양한 버전/환경에서의 확인
      • Python Version : 3.5, 3.6, 3.7
      • OS : 맥, 리눅스, 윈도우
  • 어떻게 작동되는가?
    • ACTION(일종의 TRIGGER)이 발생되면 해당 코드가 실행됨.
    • ACTION에는 PUSH, SCEHDULE, PR, COMMENT 등이 있음.
  • 가격
    • Public repo : 무료, 단 limit이 있음
    • Private repo : 링크 참고

BASE

  1. Github Repository 하나를 파기.
  1. [ACTIONS] 탭에서 "set up a workflow yourself" 를 선택.
    • main.yml 파일 생성
    • 아래 내용을 복사 붙여넣기 하기.
        name: helloGithubAction
         
        on: [push]

        jobs:
          build:
            runs-on: ubuntu-latest
            steps:
             # run 뒤에는 실제 작동하는 코드를 넣어야 합니다.
            - name: hello world 출력!!
              run: echo Hello, world!
            - name: 디렉토리 출력!!
              run: ls -al
            - name: 파이썬 버전 출력!!
              run: python -V
  1. Start Commit 을 눌러서 신규 파일을 생성
  1. Action 탭에서 좀전에 설정한 Action 스크립트의 결과를 확인할 수 있음.

헌혈 데이터 크롤링 시연

  • 혈액 정보를 크롤링하는 자동화 스크립트 시연

BloodInfo.yml

name: BloodInfo Bot (혈액정보 봇)

on: [push]
#     schedule:
#       - cron: '10 1 * * *' # UST 가 default. UST 23:00는 한국시간 08:

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: [3.6, 3.7] # 파이썬 3.6, 3.7 버전으로

    steps:
    - uses: actions/checkout@v2
    - name: Set up python ${{ matrix.python-version }}
      uses: actions/setup-python@v1
      with:
        python-version: ${{ matrix.python-version }}
    - name : Python pip upgrade # 파이썬 pip 업그레이드
      run: python -m pip install --upgrade pip 
    
    - name: Install python package # 파이썬 관련 패키지를 설치
      run: |        
        pip install beautifulsoup4
        pip install requests
                
    - name: Run! # send_to_weather.py 파일을 실행! 
      env:
        SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
      run: | 
        python send_to_bloodinfo.py
  • (주의) ${{ secrets.SLACK_WEBHOOK_URL }} 은 보안상 1회성 보내지고 비활성화 됩니다.

send_to_bloodinfo.py

import requests
from bs4 import BeautifulSoup
import csv
import json
import datetime
import os

today = str(datetime.datetime.today().date())
response = requests.get("https://www.bloodinfo.net/bloodstats_stocks.do")
html = response.text

soup = BeautifulSoup(html, 'html.parser')

oneStep = soup.select('.mb10')[0]

# 1일 소요량
전체소요량 = int(oneStep.select('tbody > tr')[0].select('td')[1].text.replace(',',''))
O소요량 = int(oneStep.select('tbody > tr')[0].select('td')[2].text.replace(',',''))
A소요량 = int(oneStep.select('tbody > tr')[0].select('td')[3].text.replace(',',''))
B소요량 = int(oneStep.select('tbody > tr')[0].select('td')[4].text.replace(',',''))
AB소요량 = int(oneStep.select('tbody > tr')[0].select('td')[5].text.replace(',',''))

# 현재 혈액보유량
전체혈액보유량 = int(oneStep.select('tbody > tr')[1].select('td')[1].text.replace(',',''))
O혈액보유량 = int(oneStep.select('tbody > tr')[1].select('td')[2].text.replace(',',''))
A혈액보유량 = int(oneStep.select('tbody > tr')[1].select('td')[3].text.replace(',',''))
B혈액보유량 = int(oneStep.select('tbody > tr')[1].select('td')[4].text.replace(',',''))
AB혈액보유량 = int(oneStep.select('tbody > tr')[1].select('td')[5].text.replace(',',''))

# 보유상태
전체보유상태 = oneStep.select('tbody > tr')[2].select('td')[1].text.replace(',','')
O보유상태 = oneStep.select('tbody > tr')[2].select('td')[2].text.replace(',','')
A보유상태 = oneStep.select('tbody > tr')[2].select('td')[3].text.replace(',','')
B보유상태 = oneStep.select('tbody > tr')[2].select('td')[4].text.replace(',','')
AB보유상태 = oneStep.select('tbody > tr')[2].select('td')[5].text.replace(',','')

print(전체소요량, B소요량, O소요량, A소요량, AB소요량)
print(전체혈액보유량, O혈액보유량, A혈액보유량, B혈액보유량, AB혈액보유량)
print(전체보유상태, O보유상태, A보유상태, B보유상태, AB보유상태)

final_data = f"var 전체소요량 = '{전체소요량}';\n\
var B소요량 = '{B소요량}';\n\
var O소요량 = '{O소요량}';\n\
var A소요량 = '{A소요량}';\n\
var AB소요량 = '{AB소요량}';\n\
var 전체혈액보유량 = '{전체혈액보유량}';\n\
var O혈액보유량 = '{O혈액보유량}';\n\
var A혈액보유량 = '{A혈액보유량}';\n\
var B혈액보유량 = '{B혈액보유량}';\n\
var AB혈액보유량 = '{AB혈액보유량}';\n\
var 전체보유상태 = '{전체보유상태}';\n\
var O보유상태 = '{O보유상태}';\n\
var A보유상태 = '{A보유상태}';\n\
var B보유상태 = '{B보유상태}';\n\
var AB보유상태 = '{AB보유상태}';\n\
"

with open('bloodinfo_data.js', "w", encoding="UTF-8-sig") as f_write:
    f_write.write(final_data)

# l = [전체소요량, B소요량, O소요량, A소요량, AB소요량, 전체혈액보유량, O혈액보유량, A혈액보유량, B혈액보유량, AB혈액보유량, 전체보유상태, O보유상태, A보유상태, B보유상태, AB보유상태]
# l
#######################
# send to slack
#######################
# # Slack 인커밍 웹훅
# slack_payload = {"text": f"*{today}* 오늘의 혈액정보입니다. :drop_of_blood: \n 1. 전체 소요량: {전체소요량} \n :o2:소요량 : {O소요량} \n :a:소요량 : {A소요량} \n :b:소요량 : {B소요량} \n :ab:소요량 : {AB소요량} \n \n 2. 전체혈액보유량 : {전체혈액보유량} \n :o2:혈액보유량 : {O혈액보유량} \n :a:혈액보유량 : {A혈액보유량} \n :b:혈액보유량 : {B혈액보유량} \n :ab:혈액보유량 : {AB혈액보유량} \n \n 3. 전체보유상태 : {전체보유상태} \n :o2:보유상태 : {O보유상태} \n :a:보유상태 : {A보유상태} \n :b:보유상태 : {B보유상태} \n :ab:보유상태 : {AB보유상태} \n ```*적정혈액보유량은 일평균 5일분이상입니다.*```"}
# # 슬랙에 쏩니다!
# req = requests.post(url=SLACK_WEBHOOK_URL, data=json.dumps(final_data))
# print(req)

결과 사진

느낀점

  • 무료이면서 쉽고 간단하게 CI/CD를 경험할 수 있는 툴.
  • DA분들도 간단하게 사용해보면 좋을거 같아서 가져와봄. 일단 배워두면 나중에라도 써먹을 데가 있지 않을까?
  • (주의) 처음에는 슬랙으로 쏴주는 것을 했는데 슬랙 보안상 API키값이 Public으로 노출되면 자동으로 삭제되도록 안정장치가 되어있었음. 해서 슬랙으로 쏘는 것은 다른 방법으로 실행해야함.

참고

profile
성장하고 싶은 데이터분석가.

0개의 댓글