Python/2.7 for fun.
벤쿠버 유학생을 위한 craigslist에서 다운타운 방찾기.
개선 : http://qkqhxla1.tistory.com/501
개요.
캐나다 벤쿠버에 도착한지 좀 되서, '다운타운'으로 이사가고자 하는 사람들이 많은데, craigslist사이트에서 찾는게 처음인 만큼 뭘 어떻게 찾아야 할지, 필터는 어떻게 걸어야 좋은지, 어떻게 해야 효율적으로 다운타운에 있는 방만 찾을지 모르는 사람들을 위한 프로그램을 만들었다.
요점 : craigslist에서 다운타운에 위치한 방만 찾아서 갖고오기.
1. craigslist에서 downtown으로 검색했을때의 결과 링크 전부 파싱.
2. 링크중에서 되도록 다운타운에 위치할 가능성이 높은 링크만 가져옴.
(원래 계획은 머신러닝까지 이용해서 downtown이란 글자가 어디에 얼마나 들어가있냐 그런 정보로 판별하려고 했지만 힘들다는걸 깨닫고, 글 속에 to downtown같이 '다운타운까지 몇분' 이렇게 확실한 글자들만 제거하고 순수하게 downtown이라는 단어만 있으면 다운타운으로 간주하고 프로그래밍)
3. 중복 제거 후 잘 분류 후 Gui로 출력, 알아서 정렬, 보기좋게 배열.
4. 더 비싸게, 더 싸게 갈수 있는 버튼 붙이기.
사실 원래 하려고했던 실제계획.
1. 위의 1과 동일
2. 링크중에서 location이 있는 부분을 전부 가져온다.
3. 바로 앞 포스팅에서 썼던 구글맵 api를 이용해서 location에 있는 부분을 검색한 후 위도, 경도 좌표를 전부 가져옴.
4. 다운타운 중간에 한 점을 찍고 적당히 원을 그려봐서 다운타운을 덮는지 확인. 다 덮는다 싶은 반지름의 최소값을 저장해두고, 원의 중심점도 따로 저장해둔다.
5. 4에서 원의 방정식을 만들고, 3에서의 위도 경도 좌표가 원 안에 있는지 확인하는 방정식을 세워서 내가 그린 원 안에 있는지 체크한다(다운타운내부에 있는지 확인용도)
6. 다운타운 안에 있는게 확인되면 해당 위치를 gui에 추가한다.
7. 위의 3과 동일....
못한이유 : 3에서의 구글맵 api가 정확하지 않다. 구글 길찾기에서의 결과값과 api를 사용했을때의 결과값이 달라서 값을 신뢰할수가 없다. 그래서 복잡한거 다 지우고 그냥 제목에 downtown이 있으면(to downtown은 제외) 다 가져오는걸로 알고리즘 변화..
추가.
뭔가 정확한것 같은 링크 발견
http://debug.so/apis/maps.php/google/address?query=49.244551,-123.0432271
결과.
Expensive나 cheap으로 더싼, 더비싼 가격대의 집을 볼수있다.
친구가 생각한 개선해야될점.
1. 원하는 가격대 입력
2. 버튼이 너무 많다. (내가 봐도 너무많다.)
써본후 내가 생각한 개선해야할점.
1. 원하거나 제외할 검색어 입력 상자.(ex)난 남자이므로 only for girl같은 표현은 필요없다, robson st에 있는 방을 얻고 싶다. 제목에 robson이 있는것만 찾자.)
2. 하이퍼링크로 링크를 열 경우 창이 뒤로 가서 다시가져오기 불편. 창 오른쪽에 검색결과 출력창이나, 포커스를 바꾸거나, 다르게 할것.
3. 친구 말대로 버튼은 두개만 놔두고 원하는 가격대 입력가능하도록 수정 또는 아래에 1 2 3 4 5 이렇게 링크를 걸어놔서 적당히 넘어갈수있게 만들것
참고.
1. 스택오버플로우에서 pyqt4용 progress bar 예시를 하나 가져왔다. 파싱시간이 오래걸리므로 진행바를 이용해 보여주기 위함이었다. 기본 예제. (만들고 나서 필요없다는걸 깨달음.)
# -*- encoding: cp949 -*- import sys, time from PyQt4 import QtGui, QtCore class ProgressBar(QtGui.QWidget): def __init__(self, parent=None, total=20): super(ProgressBar, self).__init__(parent) self.progressbar = QtGui.QProgressBar() self.progressbar.setMinimum(1) self.progressbar.setMaximum(total) self.button = QtGui.QPushButton('Start') self.button.clicked.connect(self.handleButton) main_layout = QtGui.QGridLayout() main_layout.addWidget(self.button, 0, 0) main_layout.addWidget(self.progressbar, 0, 1) self.setLayout(main_layout) self.setWindowTitle('Progress') self._active = False def handleButton(self): if not self._active: self._active = True self.button.setText('Stop') if self.progressbar.value() == self.progressbar.maximum(): self.progressbar.reset() QtCore.QTimer.singleShot(0, self.startLoop) else: self._active = False def closeEvent(self, event): self._active = False def startLoop(self): while True: time.sleep(0.05) #나중에 요부분을 잘 교체해주면 될듯. value = self.progressbar.value() + 1 self.progressbar.setValue(value) QtGui.qApp.processEvents() if (not self._active or value >= self.progressbar.maximum()): break self.button.setText('Start') self._active = False app = QtGui.QApplication(sys.argv) bar = ProgressBar(total=101) bar.show() sys.exit(app.exec_())
2. pyqt4에서의 체크박스 예제. (나중에 써먹기 위함)
http://zetcode.com/gui/pyqt4/widgets/
3. 코딩.
# -*- encoding: cp949 -*- import sys, time from PyQt4 import QtGui, QtCore import urllib2 import re import itertools class ProgressBar(QtGui.QWidget): def __init__(self, parent=None, total=100): super(ProgressBar, self).__init__(parent) self.progressbar = QtGui.QProgressBar() self.progressbar.setMinimum(1) self.total = total self.separate_part_switch = False self.down_switch = False self.separate_part_count = 0 self.information = '' self.totalcount = 0 self.button_index = [0,1,3,4,6,7] self.button_text = ['Expensive','Cheap','More Expensive','Much Cheaper','The Most Expensive','The Cheapest'] self.button_function = [self.up, self.down, self.moreup, self.moredown, self.last, self.first] self.progressbar.setMaximum(total) self.button = [QtGui.QPushButton('Find room') for i in range(6)] #추가. for i in range(6): self.button[i].clicked.connect(self.button_function[i]) self.main_layout = QtGui.QGridLayout() self.main_layout.addWidget(self.button[0], 0, 1) self.boxwidget = [] self.search_index = 0 self.setLayout(self.main_layout) self.setGeometry(400,200,350,50) #추가 self.setWindowTitle('Find a good room in downtown!') self._active = False def up(self): for i in range(6): self.main_layout.addWidget(self.button[i], self.button_index[i] ,1) self.main_layout.addWidget(self.progressbar, 9, 1) if self.search_index+100 <= self.totalcount: self.search_index += 100 self.handleButton() def down(self): if self.separate_part_count > 1: self.separate_part_count -= 1 elif self.separate_part_count == 1: self.separate_part_count -= 1 self.search_index -= 100 self.separate_part_switch = False else: self.search_index -= 100 if self.search_index < 0: self.search_index = 0 self.handleButton() def first(self): self.search_index = 0 self.separate_part_switch = False self.handleButton() def last(self): self.search_index = int(int(self.totalcount)/100)*100 self.separate_part_switch = False self.handleButton() def moreup(self): if self.search_index+200 <= self.totalcount: self.search_index += 200 self.handleButton() def moredown(self): self.search_index -= 200 self.separate_part_count = 0 self.separate_part_switch = False if self.search_index < 0: self.search_index = 0 self.handleButton() def handleButton(self): if not self._active: self._active = True for b in self.button: b.setText('Stop') if self.progressbar.value() == self.progressbar.maximum(): self.progressbar.reset() for i in range(self.main_layout.count()): #그릴때마다 매번 하이퍼링크 지우고 새로그림. self.main_layout.itemAt(i).widget().close() self.progressbar = QtGui.QProgressBar() self.progressbar.setMinimum(1) self.progressbar.setMaximum(self.total) for i in range(6): self.button[i] = QtGui.QPushButton(self.button_text[i]) self.button[i].clicked.connect(self.button_function[i]) for i in range(6): self.main_layout.addWidget(self.button[i], self.button_index[i], 1) self.main_layout.addWidget(self.progressbar, 9, 1) QtCore.QTimer.singleShot(0, self.startLoop) else: self._active = False def closeEvent(self, event): self._active = False def chunks(self, l, n): n = max(1, n) return [l[i:i + n] for i in range(0, len(l), n)] def get_information(self): page = urllib2.urlopen('http://vancouver.craigslist.ca/search/van/roo?s='+str(self.search_index)+'&max_price=1500&query=downtown&sort=priceasc').read() if page.find('the harvest moon wanes') != -1: self.search_index = 0 page = urllib2.urlopen('http://vancouver.craigslist.ca/search/van/roo?s='+str(self.search_index)+'&max_price=1500&query=downtown&sort=priceasc').read() self.totalcount = re.findall('<span class="totalcount">(\d+)</span>',page)[0] page = page.split('</section>\n</section>\n')[1].split('<div id="noresult_text">')[0] href = re.split(r'<p class="row"\s+data-pid="\d+">|<p class="row"\s+data-pid="\d+"\s+data-repost-of="\d+">',page)[1:] href = [i for i in href if i.lower().find('downtown') != -1 and i.lower().find('to downtown') == -1] information = [] for h in href: link = re.findall(r'<a href="(.*?)"',h)[0] infor = '%-5s %s' %(re.findall(r'<span class="price">(.*?)</span>',h)[0],re.findall(r'<a href=".*?" data-id="\d+" class="hdrlnk">(.*?)</a>',h)[0]) if h.find('<small>') != -1: infor += re.findall(r'<small>(.*?)</small>',h)[0] information.append([infor,link]) dic = map(lambda x:[int(re.findall(r'\$(\d+)\s+',x[0])[0]),x[0],x[1]],dict((x[0], x) for x in information).values()) return map(lambda x:[x[1],x[2]],sorted(dic)) def add_hyperlink(self, information, index): self.boxwidget = [] self.progressbar.setMaximum(len(self.information)) if not self.separate_part_switch else self.progressbar.setMaximum(len(self.information[self.separate_part_count])) for t in information: label = QtGui.QLabel('<a href="http://vancouver.craigslist.ca'+t[1]+'">'+t[0]+'</a>') label.setOpenExternalLinks(True) self.main_layout.addWidget(label,index,0) self.boxwidget.append(label) index += 1 value = self.progressbar.value() + 1 self.progressbar.setValue(value) QtGui.qApp.processEvents() if (not self._active or value >= self.progressbar.maximum()): #stop버튼 제어용. break self.setGeometry(400,200,350,300) #추가 for i in range(6): self.button[i].setText(self.button_text[i]) self._active = False if self.separate_part_switch: self.separate_part_count += 1 if self.separate_part_count == len(self.information): self.separate_part_switch = False self.separate_part_count = 0 def startLoop(self): if not self.separate_part_switch: self.information = self.get_information() if len(self.information) >= 20: self.separate_part_switch = True self.information = self.chunks(self.information,20) index = 0 if self.separate_part_switch: self.add_hyperlink(self.information[self.separate_part_count],index) else: self.add_hyperlink(self.information,index) app = QtGui.QApplication(sys.argv) bar = ProgressBar(total=100) bar.show() sys.exit(app.exec_())
4. py2exe
주의. 타이틀바에 있는 ico도 포함하게 하려면 py2exe로 만든 다음에 exe폴더와 같은 폴더 안에 vancouver.ico를 넣어줘야된다.
#-*- coding: cp949 -*- from distutils.core import setup import py2exe, glob setup( windows=[{'script':'hello.py','icon_resources':[(1, 'vancouver.ico')]}], #console=['hello.py']가 아니라 앞을 windows로 주면 보기싫은 콘솔 창이 사라짐. data_files=[ ('C:\\Users\\Ko\\Desktop\\python exe\\'+u'벤쿠버방\\'+'imageformats',glob.glob('C:\\Python27\\Lib\\site-packages\\PyQt4\\plugins\\imageformats\\*.*')) ], options = {'py2exe': { 'bundle_files':3, 'dist_dir':'C:\\Users\\Ko\\Desktop\\python exe\\'+u'벤쿠버방', 'includes': ['sip','PyQt4.QtNetwork'], #그냥 py2exe로 만들었을때 이러한 모듈이 없다고 에러뜸. 이거 추가. "dll_excludes": ["MSVCP90.dll"] #이거 없다고 exe가 안만들어지는데 굳이 필요 없으므로 필요 없다고 표시해둠. }}, zipfile = None )
'Python > 2.7 for fun.' 카테고리의 다른 글
시력 테스트 자동화 스크립트 2. (0) | 2016.05.07 |
---|---|
벤쿠버 유학생을 위한 craigslist에서 다운타운 방찾기 2 (0) | 2015.12.30 |
아이들용 덧셈색칠퍼즐 만들기. (0) | 2015.08.14 |
시력 테스트 자동화 스크립트 1. (0) | 2015.06.26 |
pyqt를 이용한 나만의 웹툰 뷰어 만들기3 (개선,기능추가) (4) | 2015.06.25 |
'Python/2.7 for fun.'의 다른글
- 현재글벤쿠버 유학생을 위한 craigslist에서 다운타운 방찾기.