Python

객체 지향 프로그래밍 - 크롤러 개발

foxlee 2021. 10. 12. 17:18

크롤러 개발 히스토리

1. 2019년 5월 하나의 특허 사이트에서 특허데이터들을 엑셀로 다운받는 크롤러를 개발(개인 프로젝트)

2. 2020년 4월 특정 아이템에 대해 여러 사이트에서 아이템들에 대한 데이터들을 수집하는 크롤러 개발

3. 2020년 8월 여러 뉴스 사이트에서 뉴스 기사들을 수집하는 크롤러 개발

4. 2021년 10월 여러 영어 사전 사이트에서 영어 구어체의  정의와 예제를 수집하는 크롤러 개발 

 

1번 크롤러

  • 2,3,4 크롤러와 다른 점은 하나의 사이트에서만 데이터를 수집했었다. 개발을 막 시작했던 당시에 개발했고, 클래스가 아닌 함수로 하나의 파이썬 파일에 다 적었다.
      • 리팩토링
        • 변수 -> title -> t, abstract -> abs 등과 같이 짧게 네이밍 했던 부분 고침
        • 데이터 구조 -> patents = [{title: ~, date: ~, }, {title: ~, date: ~, }] 로 고침
        • 변수 선언은 미리 x
        • list comprehension 코드 줄임 
    # 수정 전
    
    ## 수정 사항 : 객체의 리스트로 만들고 변수 최소화 및 객체 키값은 단어 줄이지 않고 그대로
    t = []  
    da = []  
    inv = []
    abs = []
    html = driver.page_source
    soup = BeautifulSoup(html, "html.parser")
    title = soup.select("#title")
    for content in title:
    	con = content.text
    	title_list = con.split("\n")
    	t.append(title_list[-2])
    
    date = soup.find(
    "div", {"class": "publication style-scope application-timeline"}
    )
    da.append(date.text)
    
    inventors = soup.select("#link") 
    for inventor in inventors:
    	invent = inventor.text
    	invent_list = invent.split("\n")
    	if invent_list[0].find("Application filed by") == 0:
    		names = invent_list[0][21:]
    		inv.append(names)
            
    abstract = soup.find("abstract")
    if abstract == None:
    	abs_text = "None"
    else:
    	abs_text = abstract.text[1:-2]
    abs.append(abs_text)
    
    
    # 수정 후
    patents = []
    html = driver.page_source
    soup = BeautifulSoup(html, "html.parser")
    inventors_links = soup.select(
    	"dl.important-people  #link.style-scope.state-modifier"
    )
    patents.append(
    	{
            "title": soup.find("h1", id="title").text.strip(),
            "date": soup.find("div", class_="publication").text.strip(),
            "abstract": soup.find("div", class_="abstract").text.strip(),
            "inventors": [inventor.text for inventor in inventors_links],
    	}
    }
      • 크롤링 관련 없는 부분
        • 데이터 수집, 저장하는 부분이 같은 함수내에 존재 기능 분리
        • 기능 분리 및 파일 분리도 필요

 

2번 크롤러

  • 매주 1회에 크론탭으로 실행하였고, 로그를 추가함

3번 크롤러

  • 2번 크롤러 개발 후 거의 비슷한 스타일로 개발함

4번 크롤러

  • 이 글을 작성하게 된 계기
  • 2,3번의 경우 크롤러 클래스로 개발했지만 공통으로 적용되는 로직들을 상속하지 않고 중복되는 부분이 많이 이부분을 개선해서 코드를 줄이고 가독성을 높이려고 함
  • 1~3번 크롤러의 경우 selenium 으로 개발했고, bs4의 존재를 알고있었으나 기존 코드들과 비슷하게 개발하다가 4번 크롤러의 개발의 경우 1~3번에서 필요한 더보기 버튼 클릭이나 로그인 작업들이 필요없고 html만 파싱하면 필요한 데이터들을 다 수집할 수 있어서 requests로 요청하여 html을 bs4로 파싱했다.
  • 2~3번의 경우 반복되는 코드가 정말 많았다. 사실 특정 소스에서 태그들을 파싱해오는 작업은 태크 path 또는 class 이름 등의 선택자들이 다를뿐 로직 자체는 동일했다. 이로 인해 각 파싱하는 부분마다 에러 처리 하는 부분도 반복이 많이 되어서 코드양이 불필요하게 많았다.
    • 위와 같은 것을 해결하기 위해서  태그 선택, 텍스트 추출 등의 기능을 상속하도록 했다.
  • 위의 과정을 거치면서 리팩토링 책에서 읽었던 부분들을 조금씩 적용해봤다.
    • 역할을 나누자
      • 데이터를 수집하는 크롤러와 수집해야하는 대상을 요청하고 수집된 데이터를 저장을 요청하는 API핸들러를 따로 나누었다. 
    • 함수는 하나의 일만 하자
      • 파싱 함수 안에 다 넣지 않고 파싱 url 설정, 수집할 대상 데이터 키워드 설정, 데이터 저장 등 각각 함수를 만들어 호출하도록 나눴다. 
      • 가독성이 좋아졌다.
    • 기능 변경/ 리팩토링 진행 시 변경. 추가된 부분이 완성될때까지는 기존 코드는 유지 후 테스트 한 후에 기존 코드를 삭제 했다.
      • 불필요한 디버깅이 줄어들었다.
    • 기능 변경, 리팩토링 등으로 하나의 목표만 선택하고 진행해서 개발 속도, 디버깅 속도가 빨라졌다.
    • 불필요한 변수 선언 자제
      • 반환해야하는 데이터가 간단한 계산으로 얻어진다면 변수 선언하지 않고 바로 반환
    • 변수 선언은 함수 시작부분이 아닌 변수가 사용되는 부분에서 선언
      • 리팩토링 책을 읽기 전까지는 함수에서 선언부 바로 아래에 그 함수에서 선언되는 모든 변수를 다 선언했고, 그게 보기 좋다고 느꼈다. 하지만 코드가 길어지면서 그 변수가 실제 사용되는 곳 바로 위에 선언해야 개발 비용(개발 속도)가 빨라진다는 것을 느꼈다. 
  • 아쉬운 점 
    • 기능 변경, 리팩토링 진행 시 세분화하여 깃 커밋을 하지 않았다.
    • 개발을 진행하기 전에 bs4, selenium 또는 다른 선택지에 대해 고민해보지 않고 진행했다가 시간을 조금 낭비했다.

 

추가할 내용

- 1~4 크롤러들의 코드, 깃 레포 확인해서 비교해보기

- 객체지향의 사실과 오해 책 내용과 비교해서 생각해보기