ReactNative로 간단한 앱 만들기 파트1

개요

예전부터 생각하던 간단한 서비스가 있었다. 바로 24시간 카페 찾는 서비스! 사실 비슷한 서비스는 존재한다. 구글지도에 나오는 영업시간 정보라던가, 어떤 분이 개발하신 “밤새미”라던가. 하지만 밤새미는 최근 갱신이 잘 안된 것 같고, 구글지도는 솔직히 가끔 틀려서 믿기가 어려웠다. 그래서 ReactNative를 공부하는 김에 직접 개발에 도전하기로 했다!

무엇이 필요할까?

대략적인 컨셉

서비스 이름이나 컨셉 뭐 이런것도 중요하고, 세부 기능도 중요하지만… 가장 중요한 것은 24시 영업하는 카페에 대한 정보가 필요했다. 이번 글에서는 이 정보들을 가져올 크롤러에 대해 간단히만 이야기하고 넘어가겠다. (사실… 자꾸 글을 안쓰니 미뤄서… 이렇게 써야 완성할것 같아서 쓰는중이다. 나중에 더 수정될 예정…!)

24시 카페정보 수집하기

먼저, 서울에 영업중인 프랜차이즈 카페들 중 24시간 영업을 하고 있는 프랜차이즈 카페들을 뒤져서 리스트로 만들었다.

  • 탐앤탐스
  • 할리스커피
  • 엔제리너스
  • 카페베네
  • 커핀그루나루
  • 커피스미스
  • 요거프레소 (사이트 리뉴얼로 인한 수정 필요)

당장 찾아본 데이터는 위와 같았다. 대부분의 브랜드들이 거의 24시 영업을 하고 있지는 않았다. Covid-19의 영향인지, 그 이후로 영업시간을 단축한 경우도 있었다. 어떤 브랜드의 경우 24시간 영업을 하지만, 조건부로 하는 경우도 있었다.

크롤러 개발 목표 설정

장기적으로 공식 정보를 바탕으로 서비스에서 사용할 데이터를 갱신할 예정이므로, 사이트 구조가 바뀌어도 작동하도록 개발하는 것이 옳으나… 시간적 문제도 있으니 일단 그냥 대충 초기 데이터만 수집하는 것을 목표로 크롤러를 만드는 것을 목표로 했다. (사실 무엇보다 제대로 짜는건 모르겠다 _ 나중에 scrapy를 좀 공부해야겠다.)

Seoul_24h_cafe

seoul_24h_cafe 코드는 여기서 확인할 수 있다. (현재 수정중이라 private 상태. 완성 후 공개 예정) 구조는 간단하다. 각 카페별로 파싱모듈을 나누고, 가져온 데이터를 취합한다. 취합한 데이터를 DB로 보내주면 되는 구조이다. OOP를 제대로 배워본 적이 없어, 구조가 조금 어색할 수는 있다.

Robots.txt

아무리 크롤러를 잘 만들지는 못해도… 지킬건 지켜야한다. 특정 사이트를 대상으로 크롤러를 짤 예정이라 robots.txt를 확인하는 모듈까지는 필요가 없겠지만, 혹시라도 존재하는지 수동으로 확인했다. (나중에 필요시 추가 예정) 생각보다 robots.txt가 없는 경우가 많았다.

  • 카페베네
1
2
User-agent: * 
Allow: /
  • 커피스미스
1
2
3
4
5
6
7
8
9
# XML Sitemap & Google News Feeds version 4.5.1 - http://status301.net/wordpress-plugins/xml-sitemap-feed/
Sitemap: http://coffeesmith.co.kr/sitemap.xml

User-agent: *
Disallow: /wp/wp-admin/

User-agent: Yeti
Allow: /
Disallow: /wp/wp-admin/

2021년 3월 16일 기준으로 2개 회사에서만 robots.txt가 있었고, 딱히 내가 하려는 크롤링 범위에 제한되는 경우는 없었다. 그럼 이제 딱히 크롤러를 돌리지 못할 이유가 없으니 데이터를 수집하러 가자!

데이터 수집

데이터 수집은 내가 잘 만들었거나, 엄청난 아이디어가 있는 것도 아니었기 때문에 특별히 설명할 내용은 없다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class TomTom:
def __init__(self):
print("---Starting TomTom---")
self.uid = []
self.storelist = []

def GetUid(self):
post = urllib.parse.urlencode({'keyword' : '서울', 'info4_1': '24시간'}).encode('UTF-8')
url = urllib.request.Request("https://www.tomntoms.com/store/domestic_store_search.html", post)
url.add_header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko")
data = urllib.request.urlopen(url)
data = BeautifulSoup(data, features="html.parser", from_encoding='utf-8')
result = str(data.find_all('script')[17])
self.uid = re.findall('\'([0-9]{0,3})\'', result)

def GetStoreInfo(self):
for i in self.uid:
url = urllib.request.Request("https://www.tomntoms.com/pop/pop_store_info.html?uid="+str(i))
url.add_header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko")
data = urllib.request.urlopen(url)
data = BeautifulSoup(data, features="html.parser", from_encoding='utf-8')

result_type = "C1"
result_name = data.find('h2', class_='tit')
result_location = data.find('p', class_='desc')
result_tel = data.find('a', class_='desc ff-roboto')

#print(result_name.text, result_location.text, result_tel.text)
self.storelist += [{ 'type' : result_type, 'name' : result_name.text, 'location' : result_location.text, 'tel' : result_tel.text, 'time' : '24시간' }]
return self.storelist

위가 탐앤탐스의 경우인데, 프랜차이즈 하나마다 클래스를 만드는 식으로 큰 틀을 잡았다. 대부분의 프랜차이즈들이 리스트에서는 세부정보(정확하게는 내가 필요한 정보들)를 보여주지 않고, 세부정보를 보여주는 페이지가 따로 있었다.

regexr 사이트님... 정말 감사합니다...

내가 필요한 정보가 “서울”내에서 “24시”영업을 하는 지점의 정보였기 때문에 리스트에서 해당 지점들의 uid를 1차적으로 수집한 뒤, uid를 바탕으로 세부정보를 조회하는 구조를 사용했다. 정규표현식을 사용하는게 오히려 더 편할 것 같아, 이번에는 많이 사용했다.

여기서도 많이 배웠다.

파이썬 정규표현식(re) 사용법 에서도 많이 배웠다. 혹시라도 필요하다면 정리를 되게 잘 해둔 것 같으니 참고하면 좋을 것 같다. 어쨋든… 대부분의 경우엔 이런 방식으로 빠르게 구현이 가능했다. 문제는… 지속 가능한 크롤러로 바꿀때 고생은 좀 하겠지만… 공부 좀 하면 답이 나오겠지… 라고 생각하고 있다.

데이터 가공

취합한 데이터들을 모두 모았으면 이제 가공을 해야한다. 가공이라고 해봤자 특별한 내용은 없다. 여러 프랜차이즈들의 정보를 취합하다보니 약간씩 수집한 데이터들의 차이가 존재했다.

  • 지점명에 프랜차이즈 명이 들어가는 경우
1
{'type': 'C3', 'name': '엔젤 불광역', 'location': '서울 은평구 대조동6-5번지 신흥빌딩 1층', 'tel': '02-389-9571  ', 'time': '24시간'}
  • 전화번호가 T.으로 시작하는 경우
1
{'type': 'C1', 'name': '논현점', 'location': '서울특별시 강남구 강남대로118길 43 ', 'tel': 'T.02-516-1030', 'time': '24시간'}
  • 영업 시간이 일부만 2시간인 경우
1
{'type': 'C5', 'name': '퍼플멤버스 신도림 라운지', 'location': '서울특별시 구로구 경인로 661 (신도림동)', 'tel': '02-2068-0048', 'time': '일~목(공휴일) 08:00~25:00, 금~토 24Hours'}
1
{'type': 'C7', 'name': '이태원점', 'location': '서울특별시 용산구 이태원로 153, 2층', 'tel': '02-797-6740', 'time': '월~목,일 9:00-02:00, 금토 9:00-08:00(24시간 영업)'}

예시와 함께 정리한 내용을 위에서 확인할 수 있다. 큰 문제는 없지만, 그래도 통일성을 위해 약간의 정리를 하고자 했다. 일단 “000점”과 같은 형태로 지점명을 통일하고, 전화번호 형식은 숫자만을 남겨두고자 했다. 영업시간 같은 경우에도 정리를 하고 싶었으나… 양식이 생각보다 너무 제멋대로라서 일단은 보류했다.

가공 방식은 간단하다. 정규표현식을 주로 사용해서 데이터가 올바른 형태인지 확인하고, 수정해주는 과정을 거쳤다.

데이터 전송

까지 현재 작성 완료. 추후 개발 완료시 더 추가될 예정.

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×