winglet: 간단한 web crawler
글쓴이: lifthrasiir / 작성시간: 월, 2005/03/28 - 4:10오전
wget -R 비스무리한 기능을 하는 파이썬 프로그램입니다. win32에서 wget 쓰기도 뭐하고 해서 간단하게 만든 프로그램인데 우째 하다 보니까 버전이 1.4까지 갔군요. -.-
실행 방법은 다음과 같습니다.python winglet.py -n30 http://pandora.sapzil.info/dev/obfuscation/index.php
유일한-_- 옵션인 -n##는 동시에 파일을 긁어 올 쓰레드의 갯수를 지정합니다. 기본값은 10입니다.
이 프로그램으로 페이지를 긁어 댈 때 파일 이름이 정해지는 규칙은 간단합니다. http://pandora.sapzil.info/dev/obfuscation/index.php를 저장한다고 하면, 이 파일은 pandora.sapzil.info\dev\obfuscation\index.php(win32의 경우) 식으로 디렉토리가 만들어져서 저장됩니다. index.php 같은 파일 이름이 생략되면 index.html로 가정합니다. http://로 시작하는 URL은 일단 무시하게 되어 있는데 (무한히 도는 걸 방지하기 위해) 이건 나중에 고쳐야 할 것 같습니다;
이 코드는 GNU GPL에 따라 배포됩니다. code snippet에서도 볼 수 있습니다.
"""winglet 1.4 -- Simple web crawler implementation using thread. Kang Seonghoon (Tokigun) @ TokigunStudio 2005 Distributable under the GNU GPL. """ import HTMLParser, urllib2, urlparse import os, sys, thread, Queue, sets class parser(HTMLParser.HTMLParser): map = { 'a': ('href',), 'script': ('src',), 'embed': ('src',), 'iframe': ('src',), 'frame': ('src',), 'img': ('src', 'lowsrc'), 'body': ('background',), 'table': ('background',), 'tr': ('background',), 'td': ('background',), 'applet': ('code',), 'link': ('href',), 'area': ('href',), } def __init__(self): HTMLParser.HTMLParser.__init__(self) self.urls = [] def geturls(self): return self.urls def handle_starttag(self, tag, attrs): _attrs = {} for key, value in attrs: _attrs[key] = value if tag == 'meta' and _attrs.get('http-equiv','').lower() == 'refresh': self.urls.append(_attrs['content'].split(';url=')[1]) else: attrmap = self.map.get(tag, ()) for key, value in attrs: if key in attrmap: self.urls.append(value) def parse(url): try: handle = urllib2.urlopen(url) data = handle.read() except: return (None, '', []) mimetype = handle.info()['content-type'] if mimetype.startswith('text/html') or mimetype.startswith('application/xml+xhtml'): p = parser() try: p.feed(data) except: print ' >> malformed html: %s' % url return (True, data, []) u = {} for uu in p.geturls(): if uu.find(':') >= 0: continue u[urlparse.urljoin(url, uu)] = True next = u.keys() else: next = [] return (mimetype.startswith('text/') or mimetype.startswith('application/xml')), data, next def writefile(url, data): domain, path = urlparse.urlparse(url)[1:3] realpath = [domain] + path.split('/') try: os.makedirs(os.path.join(*realpath[:-1])) except: pass if realpath[-1] == '': realpath[-1] = 'index.html' file(os.path.join(*realpath), 'wb').write(data) ############################################################ queue = Queue.Queue() finished = sets.Set() threads = [] def process(n, url): if url not in finished: finished.add(url) print '%d: %s' % (queue.qsize(), url) try: istext, data, next = parse(url) if istext is not None: writefile(url, data) for u in next: if u not in finished: queue.put(u) except: raise threads[n] = '' print 'done. (%d threads are alive)' % (len(threads)-threads.count('')) def usage(apppath): print __doc__ print "Usage: python %s [-n##] urllist..." % apppath print " -n## sets a number of crawler threads." def main(argv, nthreads=10): apppath = argv.pop(0) while len(argv) and argv[0][0] == '-': arg = argv.pop(0) if arg in ('-h', '--help'): usage(apppath); return 0 elif arg.startswith('-n') and arg[2:].isdigit(): nthreads = int(arg[2:]) if len(argv) == 0: usage(apppath); return 0 for x in argv: queue.put(x) threads.extend([''] * nthreads) while not queue.empty() or threads != [''] * nthreads: if queue.empty(): continue for x in xrange(len(threads)): if not threads[x]: break else: continue url = queue.get() threads[x] = url thread.start_new_thread(process, (x, url)) return 0 if __name__ == '__main__': try: sys.exit(main(sys.argv)) except KeyboardInterrupt: print "Aborted. Current Queue:" for x in xrange(len(threads)): print '%2d: %s' % (x, threads[x]) sys.exit(1)
- 토끼군
Forums:
댓글 달기