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:


댓글 달기