winglet: 간단한 web crawler

lifthrasiir의 이미지

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: 

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.