본문 바로가기

Programming/Python tips

How to become a web crawling master

크롤링 매력에 빠지다..!

내 첫 크롤링은 cheerio 와 puppeteer 라는 자바스크립트 모듈을 활용한 네이버 실시간 검색 순위 크롤링이였다. 그 순위의 목록을 가져오고 가공해서 내 터미널에 띄우는 그 순간! 희열을 느꼈다. 크... 이거구나. 이런 매력이 있구나..! 크롤링은 개발자에게 아주 중요한 기술 중 하나이다. DB 에 데이터가 없다면 쌓일때 까지 기다려야 하고, 유의미한 정보가 아니라면 그 DB 는 쓸모가 없다. 웹 상에는 우리가 상상도 못할 만큼의 데이터가 떠있다. 잘 안보이게 가려놓거나 의도적으로 막아두지 않는 이상 크롤링 또는 스크래핑 이라는 기술로 데이터를 긁어올 수 있다. 이렇게 모인 데이터는 또 다른 가능성을 열어주고 환경을 만들어 주기도 한다. 최근 핀테크에서 스크래퍼라는 파트가 생겨서 사용자의 금융데이터를 종합해 개인의 금융상태와 관리가 용이하도록 하는 서비스가 성장하고 있다.

 

이전 프로젝트에서도 카카오맵에서 검색된 식당목록을 크롤링해서 데이터를 활용하고 싶었지만 대부분 비동기적인 컨텐츠로 이루어져있어서 쉽게 크롤링하지 못했다. 몇번 IP 가 막히다보니 잘못된 방법으로 크롤링하고 있다는 것을 알고 방향을 바꾸기도 했다. 중요한건, 굉장히 흥미롭고 재밌었다. 물론 원하는대로 크롤링이 동작하진 않았지만, "이거... 뚫고 싶다...!" 라는 강력한(?) 마음의 소리를 들었다. 사실 접근하면 안되는 방법으로 접근하려고 했다가 IP 가 블럭당한 것도 있다.ㅎㅎ

 

창과 방패의 싸움이라고 했던가. 방패를 뚫을 수 있는 창을 만들 수 있다면, 그 창을 막을 방패도 만들 수 있지 않을까?

 

크롤링의 매력에 빠져버렸다.

 

파이썬과 첫만남

새로운 시작, 첫만남은 언제나 신선하고 흥분된다. 이번 프로젝트가 그랬다. 그 동안은 자바스크립트 node.js 와 express 를 활용했었는데 이번 프로젝트에서는 기획하면서 정확히 말하자면 Django 를 활용해 보자고 의견이 있었다. 세계적으로 유명한 기업들이 서버 프레임워크로 Django 를 활용하기 때문에 배우고 싶었고, 프로젝트로 진행하는데 이견 없이 확정됐다. 파이썬은 어렵지 않다, 쉽다, 라는 얘기를 많이 들었었는데 자바스크립트와 비교해서 좋았던 점은, 무엇이 더 낫다 보다도 파이썬에서는 신경쓰지 않아도 되는 부분이 있어서 어렵지 않게 공부했다. 예를 들어, 선언을 하지 않는다는 점, 순차적으로 읽어지기 때문에 들여쓰기로 스코프를 나누고, 자바스크립트와 비슷한 개념으로 동작하기 때문에 재밌게 공부하고 프로젝트를 시작했다. 그렇게 우리는 서로 익숙해지기 시작했다.

 

"자바스크립트야, 세미콜린 너무 귀찮아. 미안."

 

Django 를 처음 시작하는 사람이 있다면 깃헙에 살짝 정리해 놓은 노트가 있는데 설치하고 기본적인 세팅을 하는데 도움이 될 것 같다.

https://github.com/ekklesia11/TIL

 

ekklesia11/TIL

Today I Learn. Contribute to ekklesia11/TIL development by creating an account on GitHub.

github.com


Expo application QR code deploy version (Android Only)

프로젝트명: Secondhand

이번 서비스는 중고시장 가격이 적정한지 판단하기 어렵기 때문에 브랜드와 모델별로 종합적인 평균가격을 제시한다. 실제로 중고거래가 되는 카테고리는 굉장히 광범위하기 때문에 이번 프로젝트에서는 작고 단단한 서비스를 만들기 위해서 하나의 카테고리를 기준으로 확장성을 고려하면서 진행되었다.

 

많은 중고거래 상품중에 사용 기간은 짧지만 거래가 활발하고 특정 대상을 위한 상품이 무엇인지 생각했다. 그 중에서 육아용품이 괜찮다고 판단했다. 가장 필수적으로 사용되는 유모차는 아이들의 연령과 사용 용도에 따라 짧은 사용기간에 비해 가격적인 부담이 꽤 높아 중고를 찾는 이용자의 수가 높다는 걸 알았다. 그렇게 유모차와 함께 프로젝트가 시작됐다.

 

프로젝트 플로우 다이어그램

 

사용 기술 스택

 

서비스 시연

 

크롤러의 고민

서비스 구현을 놓고 크롤러로서 크게 두가지 고민을 했다.

  1. 어디서, 그리고 우리가 원하는 데이터를 모두 가져올 수 있을까?

  2. 여러 크롤링 함수를 동시에 실행 시킬 수는 없을까?

첫번째 질문: 어디서, 그리고 우리가 원하는 데이터를 모두 가져올 수 있을까?

 

결론부터 말하자면 No! 다.

 

구글이나 네이버와 같은 검색엔진 사이트들은 로봇이라는 크롤러로 검색결과를 가져와 보여준다. 그렇다면, 어떤 정보를 주고 어떤 정보를 공개하지 않을 것인가? 웹을 구성하고 만들때 고려해야 하는 부분이기도 하다. 개발 레벨에서는 크게 고려되진 않겠지만 프로덕션 레벨에서는 꼭 고려되어야 하는 부분이다. 모든 데이터가 검색되거나 하진 않기 때문이다. 유용한 정보, 공개되고 빠르게 검색되어지기 위해서는 이 로봇의 특성을 이용할 필요가 있다.

 

각 웹 주소뒤에 /robots.txt 를 추가해서 요청하면 크롤러에게 어떤 부분이 허락된 정보이고 허락되지 않은 정보인지 알려줄 수 있다. 로봇 크롤러가 이 파일에 접근해서 공개된 정보만을 가져가는 것이다. 이 파일이 추가되지 않은 사이트도 물론 존재한다. 크롤링을 진행할 때는 크로링하려는 페이지의 robots.txt 를 꼭 확인하면 좋다. 서버 개발자 입장에서는 무분별한 크롤링은 서버에 과부하를 주고 의도치 않은 과금을 야기할 수도 있기 때문에 막아놓거나 제한을 걸어둔다. 또한 저작권를 침해할 우려가 있기 때문에 조심할 필요가 있다.

 

또 한가지, 원하는 데이터를 크롤링하기 위해서 고려해야 하는 부분은 접근 방식이다. 컴퓨터가 페이지에 접근하는 방식과 실제로 유저가 접근하는 방식이 다르기 때문에 실제 유저처럼 접근하지 않으면 크롤링을 못하는 경우도 종종 있다. 이러한 여러 문제들을 피해 크롤링을 하려면 beautifulsoup4 이나 selenium 같은 모듈을 잘 활용해야 한다.

 

 

Beautifulsoup4 + Selenium

  beautifulsoup4 selenium
장점 원하는 태그 데이터 수집 후 파싱 => 바로 활용가능

1. 비동기 컨텐츠 수집가능

2. 실제 유저 접근 방식으로 접근가능

단점 비동기 컨텐츠 접근불가 느려질 가능성이 있다. => 크롤링을 위한 도구로 개발되지 않음

 

 

Using beautifulsoup4 crawling example

1
2
3
4
5
6
7
8
9
10
11
12
13
import requests
from bs4 import BeautifulSoup
 
req = requests.get('https://www.naver.com')
html = req.text
 
soup = BeautifulSoup(html, 'html.parser')
# 크롬에서 소스코드를 열고 원하는 태그 위에서 copy > copy selector 를 하면 쉽게 태그 경로를 얻을 수 있다. 
topKeywords = soup.select('span.ah_k')
 
# topKeywords 는 리스트 형태로 들어가기 때문에 for 문을 쓸 수 있다.
for i in range(010):
      print(topKeywords[i].text)
cs


두번째 질문: 여러 크롤링 함수를 동시에 실행 시킬 수는 없을까?

 

1.  Treading

파이썬은 베이스적으로 single thread 로 되어 있다. 이말은 즉슨, 순차적으로 코드를 실행시킨다는 것이다. 여기서 threading 이라는 방버은 여러개의 threads 를 만들어서 여러 코드를 실행시킨다. 하지만 한가지 문제점이 있다. 파이썬 언어베이스에 Global Interpreter Lock(GIL) 이라는 제한장치가 있어서 하나의 thread 는 하나의 파이썬 오브젝트에 접근 할 수 있다. 따라서 여러개의 threads 를 돌리려고 하면 충돌이 발생하고 race condition 이 발생할 수도 있다. 이렇게 만들어진 이유는 파이썬에서 메모리를 관리하는 방법에 어긋나기 때문이다. Race condition 이란, 여러 thread 가 같은 데이터에 접근 했을 때 각각의 작업을 진행하면서 한쪽의 결과가 반영되지 않는 경우 생기는 문제를 일컫는다. 조금 더 자세한 내용은 아래 블로그를 참고하면 좋다.

 

참고 블로그:

https://dgkim5360.tistory.com/entry/understanding-the-global-interpreter-lock-of-cpython

 

왜 Python에는 GIL이 있는가

Python 사용자라면 한 번 쯤은 들어봤을 (안 들어봤다 해도 괜찮아요) 악명 높은 GIL (Global Interpreter Lock)에 대해 정리해본다. Global Interpreter Lock 그래서 GIL은 무엇인가? Python Wiki에서는 이렇게..

dgkim5360.tistory.com

 

2. Multiprocessing

Multiprocessing 이란, threading 이랑은 다르게 여러개의 파이썬 프로세싱을 실행시켜서 각각의 thread 에서 코드를 실행한다. 다시 말해서, 파이썬 파일을 여러개 열고 각 single thread 에서 코드를 읽는다는 것이다. 이 방법에도 한가지 단점이 있다. OS 환경에 따라서 CPU 메모리 오버헤드가 발생하고, 속도가 느려질 수 있다. 지금의 프로젝트에서 서버환경은 EC2 free tier 의 우분투를 사용했는데 이 환경에서는 적절치 않다고 판단했다. 한 두개는 실행할 수 있지만 만약 크롤링해야 하는 사이트, 카테고리가 늘어날수록 크롤링 함수의 수도 늘어 전체적인 서버 스피드를 죽일 수도 있을 것 같았다. 그래서 기본적이지만 당연한 방법으로 크롤링 함수에서 쓸데없는 작업을 줄이는 함수로 리팩토링이 먼저라고 생각했다.

 

3. 로직 단순화

기존에 상품 전체를 긁어와서 한번에 DB 에 넣는 함수에서 상품정보 게시물 하나씩 DB 로 바로 넣는 함수로 리팩토링 시켰다. 그리고 기존에 크롤링 된 상품 게시물은 크롤링에서 제외시켜 중복 크롤링을 방지시켰다. 조금 시간은 걸리더라도 카테고리 확장시 디버깅이 쉽고 유지보수 및 확장이 쉽다는 장점을 갖는다. 

 

프로젝트를 마치며...

첫번째로 어떤 언어든 프레임워크든 필요에 따라서 배워서 바로 활용 할 수 있겠다 라는 자신감이 생겼다.

두번째로 느낀 점은 주어진 환경에서 최선의 방법을 항상 찾아야 한다고 생각했다. 하지만 프로젝트를 마친 뒤 생각했다. 필요하다면 환경을 바꿔서라도 최고의 방법을 찾아야 한다고. 이런 개발자가 좋은 개발자의 자세가 아닐까 생각해본다.

 

 

프로젝트 Github Link:

https://github.com/ekklesia11/secondhand-server-repo

 

ekklesia11/secondhand-server-repo

Contribute to ekklesia11/secondhand-server-repo development by creating an account on GitHub.

github.com