#coding=utf-8 # скачивание html, исправление, поиск картинок, скачка картинок # # import urllib2 import urllib import urlparse import socket import md5 import random import os import chardet import html5lib import html5lib.treewalkers import html5lib.serializer from BeautifulSoup import BeautifulSoup BeautifulSoup.NESTABLE_TAGS['strong'] = [] BeautifulSoup.NESTABLE_TAGS['b'] = [] BeautifulSoup.NESTABLE_TAGS['i'] = [] BeautifulSoup.NESTABLE_TAGS['em'] = [] BeautifulSoup.NESTABLE_TAGS['var'] = [] BeautifulSoup.NESTABLE_TAGS['cite'] = [] BeautifulSoup.NESTABLE_TAGS['p'] = [] BeautifulSoup.NESTABLE_TAGS['pre'] = [] import log class WebError(Exception): ''' объект - исключение ''' def __init__(self, value = 'Web Error'): self.value = value def __str__(self): return repr(self.value) def url_fix(s, charset='utf-8'): """Sometimes you get an URL by a user that just isn't a real URL because it contains unsafe characters like ' ' and so on. This function can fix some of the problems in a similar way browsers handle data entered by the user: >>> url_fix(u'http://de.wikipedia.org/wiki/Elf (Begriffsklärung)') 'http://de.wikipedia.org/wiki/Elf%20%28Begriffskl%C3%A4rung%29' :param charset: The target charset for the URL if the url was given as unicode string. """ if isinstance(s, unicode): s = s.encode(charset, 'ignore') scheme, netloc, path, qs, anchor = urlparse.urlsplit(s) path = urllib.quote(path, '/%') qs = urllib.quote_plus(qs, ':&=') return urlparse.urlunsplit((scheme, netloc, path, qs, anchor)) def do(url, is_img, rez_folder, progres): """главная рабочая функция""" try: data, real_url = download_html(url) except (urllib2.HTTPError, urllib2.URLError, IOError, ValueError), er: log.warning('Download error %s' % (er)) raise WebError, "Bad url" progres.level = 1 progres.save() data = decoding(data) data = correct(data) if is_img: data, img_list = process_images(data, rez_folder, url) progres.level = 2 progres.save() img_download(img_list, rez_folder) return data def correct(data): """корректировка кривого html""" log.debug('start html correct') #p = html5lib.HTMLParser(tree=treebuilders.getTreeBuilder("BeautifulSoup")) #soup = p.parse(data) #out_data = unicode(soup) document = html5lib.HTMLParser().parse(data) tokens = html5lib.treewalkers.getTreeWalker('simpleTree')(document) out_data = ''.join(html5lib.serializer.HTMLSerializer(omit_optional_tags = False, quote_attr_values = True).serialize(tokens)) log.debug('end of html correct') return out_data def decoding(data): ''' определяем кодировку файла и декодируем в юникод ''' tmp = chardet.detect(data) #определяем кодировку log.info('Detected encoding: %s' % (tmp)) new_data = data.decode(tmp['encoding']) return new_data def process_images(data, source_folder, url): ''' обработка картинок в html: замена путей картинок на пути в файловой системе, составление списка картинок для скачки Входные переменные: data - HTML source_floder - папка, где будут картинки url - адрес страницы, откуда скачивался html возвращает HTML с замененными путями и список картинок ''' log.debug('start img process') soup = BeautifulSoup(data) #вычисляем базовый урл try: base_url = soup.html.head.base['href'] except (KeyError, TypeError, AttributeError), er: base_url = url log.debug('Base url = %s' % base_url) imgs_list = {} img_tags = soup.findAll(name = 'img') log.info('Find %s images' % (len(img_tags))) for img_tag in img_tags: if img_tag.get('src', None): #делаем абсолютный урл new_url = urlparse.urljoin(base_url, img_tag['src']) if new_url in imgs_list: img_name = imgs_list[new_url] else: #генерим имя картинки img_name = "img" + md5.new(str(random.random())).hexdigest()[:10] #img_name = md5.new(str(random.random())).hexdigest()[:10] #заносим в словарь урл и имя файла imgs_list[new_url] = img_name # меняем ссылку на путь + имя файла img_tag['src'] = img_name log.info('%s images to download' % (len(imgs_list))) new_data = str(soup).decode('utf8') #после beatefulsoap приходится декодировать log.debug('end of img process') return new_data, imgs_list def download_html(url): ''' скачка html страницы возвращает html страничку и real URL of the page fetched. This is useful because it may have followed a redirect. ''' log.debug('start url downloading') socket.setdefaulttimeout(20) opener = urllib2.build_opener() request = urllib2.Request( url_fix(url), None, {"User-Agent": "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.8.1.8) Gecko/20071008 Firefox/2.0.0.8"}) handle = opener.open(request) text = handle.read() realurl = handle.geturl() handle.close() log.debug('end of url downloading: %s' % (realurl)) return text, realurl def img_download(files_list, folder): ''' входные параметры: files_list - словарь {url: file_name}....} folder - папка для сохранения возвращает: словарь {url: реузльтат}....} где результат: 0 - если не получилось, 1- если получилось ''' log.debug('%s images for download' % len(files_list)) rez = {} socket.setdefaulttimeout(20) opener = urllib2.build_opener() for url, file_name in files_list.items(): log.debug('Download image: %s into: %s' % (url, file_name)) try: request = urllib2.Request( url_fix(url), None, {"User-Agent": "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.8.1.8) Gecko/20071008 Firefox/2.0.0.8"}) handle = opener.open(request) data = handle.read() handle.close() except (urllib2.HTTPError, urllib2.URLError, IOError, ValueError), er: log.warning('Downloading image: %s error: %s' % (url, er)) rez[url] = 0 else: file(os.path.join(folder, file_name), 'wb').write(data) rez[url] = 1 log.info('Downloaded %s images from %s' % (len(rez), len(files_list))) return rez if __name__ == '__main__': file('tmp/temp.htm', 'w').write(do('http://revolver.ru', True, 'tmp').encode('UTF-8'))