Уведомления

Группа в Telegram: @pythonsu

#1 Март 4, 2014 21:42:19

zlodiak
От: Россия
Зарегистрирован: 2014-01-19
Сообщения: 159
Репутация: +  0  -
Профиль   Адрес электронной почты  

организация процедурного кода

ребят, подскажите пожалуйста как писать парсеры

я вот набросал для примера парсер(букв много, но программу читать не надо, вопрос по организации кода). но одолевают меня сомнения. как ни старался код не дублировать, всё таки получилась программа трудная для чтения. и дело не в том, что используется процедурный стиль. я уверен, что можно организовать код более красиво

если это возможно, то объясните пожалуйста кратко на словах где я не прав и что можно улучшить. или парсеры такие и должны быть и этот вполне нормальный?

ещё раз повторюсь, что вопрос не по программе, а именно по способу организации кода

# win7 pithon33
import pprint
import requests
import lxml.etree
import lxml.html
import lxml.cssselect
import re
import os
import sys
import shutil
import urllib
def get_doc(url):
    try:
        req = requests.get(url)
    except Exception:
        print('Error open. __', Exception)
    else:
        html = req.text
        doc = lxml.html.document_fromstring(html)
        return doc
                    
def record_xml(xml, fileName='xml.xml'):
    try:
        with open(fileName, "w") as file:
            file.write(xml)
    except Exception:
        print('Error record', Exception)
    else:
        print('record ok')
def get_list_pages(doc):
    listPages = []
    #listPages = doc.xpath('//table//td[@class="pages"]/a')
    listLinks = doc.cssselect('table.pagn td.pages a')
    for elem in listLinks:
        href = elem.get('href')
        listPages.append(href)
    if not listPages:
        print('Error forming list pages')
    else:
        return listPages
    
def get_list_goods(listPages):
    listGoodsLinks = []
    #i = 0
    for path in listPages:
        doc = get_doc(path)
        listGoods = doc.xpath('//div[@id="ResultSetItems"]/table//h3/a')
        #i = i + 1
        #if i > 1: break
        for elem in listGoods:
            href = elem.get('href')
            listGoodsLinks.append(href) 
    if not listGoodsLinks:
        print('Error forming list goods')
    else:
        return listGoodsLinks
def get_info(listGoodsLinks):
    #i = 0
    result = {}
    for url in listGoodsLinks:
        doc = get_doc(url)
        
        try:
            title = doc.xpath('//h1[@id="itemTitle"]/text()')[0].strip()
        except Exception:
            title = 'None'
        try:
            priceUSD = [t.replace('\xa0', ' ') for t in doc.xpath('//span[@itemprop="price"]/text()')] or [t.replace('\xa0', ' ') for t in doc.xpath('//span[@id="mm-saleDscPrc"]/text()')]
            priceUSD = priceUSD[0]
            #search = re.search('([\d, ]+)$', priceUSD)
            #priceUSD = search.group(1)
            priceUSD = priceUSD.replace('US $', '')
        except Exception:
            priceUSD = 'None'
        try:
            priceRUR = [t.replace('\xa0', ' ') for t in doc.xpath('//div[@id="prcIsumConv"]/span/text()')]
            priceRUR = priceRUR[0]
            priceRUR = priceRUR.replace(u' руб', '')
        except Exception:
            priceRUR = 'None'            
            
        try:
            condition = doc.xpath('//div[@id="vi-itm-cond"]/text()')[0].strip()
        except Exception:
            condition = 'None'
            
        try:
            timeLeft = doc.xpath('//span[@id="vi-cdown_timeLeft"]/text()')[0].strip()
        except Exception:
            timeLeft = 'None'
        try:
            sellerName = doc.xpath('//span[@class="mbg-nw"]/text()')[0].strip()
        except Exception:
            sellerName = 'None'
        try:
            salesQuantity = doc.xpath('//span[@class="mbg-l"]/a/text()')[0].strip()
        except Exception:
            salesQuantity = 'None'
        print(title, priceUSD, priceRUR, condition, timeLeft, url, sellerName, salesQuantity, end='\n\n')
        listImages = []
        thumbs = doc.xpath('//td[@class="tdThumb"]/div/img')
        for thumb in thumbs:
            thumbSrc = thumb.get('src')
            #print(thumbSrc)
            imageSrc = thumbSrc.replace('_39', '_57')
            #print(imageSrc)
            listImages.append(imageSrc)
        info = {
                    'title': title,
                    'price_usd': priceUSD,
                    'price_rur': priceRUR,
                    'condition': condition,
                    'time_left': timeLeft,
                    'seller_name': sellerName,
                    'sales_quantity': salesQuantity,
                    'imagesWeb': listImages
        }
        result[url] = info
        newCatalog = create_dir(title)
        for imgPath in listImages:
            download_image(imgPath, catalog=newCatalog)
        
        #i = i + 1
        #if i > 2: break
    return result
def create_dir(title):
    title = secureChars(title)
    newCatalog = make_catalog(title)
    return newCatalog
def make_catalog(catalog):
    try: 
        os.mkdir(catalog)
    except Exception as ex:
        print(Exception, ex)
    else:
        return catalog
def change_catalog(catalog):
    try: 
        os.chdir(catalog)
    except Exception as ex:
        print(Exception, ex)
    else:
        return True
        
    
def make_elements(info):
    parent = lxml.etree.Element('data')
    for url in sorted(info):
        child = lxml.etree.Element('item', href=url)
        for key, value in info[url].items():
            if type(value) == list:
                child2 = lxml.etree.Element(key)
                for val in value:
                    child3 = lxml.etree.Element('image')
                    child3.text = val
                    child2.append(child3)
                child.append(child2)
                parent.append(child)
            else:
                child2 = lxml.etree.Element(key)
                child2.text = value
                child.append(child2)
                parent.append(child)
    return parent
def ready_os(catalog):
    for rootdir, dirnames, filenames in os.walk(catalog):
        #print(rootdir, '==')
        for subdirname in dirnames:
            try:
                dirName = os.path.join(rootdir, subdirname)
            except Exception as exc:
                print(exc)
                sys.exit()
            else:
                print(dirName)
                shutil.rmtree(dirName)
        for filename in filenames:
            try:
                fileName = os.path.join(rootdir, filename)
            except Exception as exc:
                print(exc)
                sys.exit()
            else:
                print(fileName)
                os.remove(fileName)
            
    if os.path.exists(catalog):
        print('warning. catalog exists')
        #чистим каталог
        for the_file in os.listdir(catalog):
            file_path = os.path.join(catalog, the_file)
            try:
                if os.path.isfile(file_path):
                    os.unlink(file_path)
            except Exception as exc:
                print(exc)
                sys.exit()
    else:
        dataCatalog = make_catalog(catalog)
        if not dataCatalog:
            print('error catalog create')
            sys.exit()
    if not change_catalog(catalog):
        print('error change catalog')
        sys.exit()
    return
def download_image(path, catalog):
    img = urllib.request.urlopen(path).read()
    search = re.search('.*\/(.+?)\/(.+?)\.(.+)$', path)
    name = search.group(1)
    ext = search.group(3)
    name = secureChars(name)
    ext = secureChars(ext)
    realPath = os.getcwd()
    fullPath = os.path.join(realPath, catalog, name + '.' + ext)   
    with open(fullPath, "wb") as f:
        f.write(img)
def secureChars(word):
    replace_chars = ['\\', '/', ':', '*', '?', '"', '<', '>', '|', '-' , ' ', '!']
    for char in replace_chars:
        word = word.replace(char, '_')
    return word
     
if __name__ == "__main__":
    url = 'http://ebay.com/'
    query = 'http://www.ebay.com/sch/i.html?_sacat=0&_nkw=dw+drum+set&_frs=1'
    doc = get_doc(query)
    catalog = 'data'
    #готовим каталог, в котором будут храниться скачанные данные
    ready_os(catalog)
    #получаем набор линков страниц с товарами
    listPages = get_list_pages(doc)    
    pprint.pprint(listPages)
    #получаем набор линков на страницы конкретного товара
    listGoodsLinks = get_list_goods(listPages)
    pprint.pprint(listGoodsLinks)
    #получаем набор данных для 
    info = get_info(listGoodsLinks)
    #строим xml-дерево
    xml = make_elements(info)
    xmlPretty = lxml.etree.tounicode(xml, pretty_print=True)
    print(xmlPretty)    
    
    #запись xml в текстовый файл
    record_xml(xmlPretty, 'ebay.xml')

Отредактировано zlodiak (Март 4, 2014 21:56:25)

Офлайн

#2 Март 4, 2014 22:28:25

terabayt
От: Киев
Зарегистрирован: 2011-11-26
Сообщения: 1099
Репутация: +  103  -
Профиль   Отправить e-mail  

организация процедурного кода

Я хочу познать Дао программирования, но мне впадло думать! Я хочу зарабатывать много денег, но вместо того чтобы думать, я напишу на форум, пусть поправят мой код, чтобы он был красивым! Ведь на форумах сидят люди, которым больше нечем заняться, кроме как исправлять чужой код, длиной в 250 строк!
Вы хотите писать красивый код, а что вы для этого сделали? написали на форум???
Я могу исправить ошибку, показать правильный путь, подсказать какойто модуль и т.д. НО тратить полчаса на улучшения программы!….
Я отвечаю на вопросы, чтобы помочь человеку стать лучше, он пытается чтото сделать сам, ищет, но не может найти или нашел, но не работает и прибегает к помощьи людей, которые возможно встречали данную ошибку или внимательней читали документацию. А вы… Я не могу понять, что вы думали, когда писали такое???

набросал для примера парсер(букв много, но программу читать не надо, вопрос по организации кода)
А, извините, я не увидел, как же я сразу не понял, что здесь всегото нужно приложить ладоню к экрану и экстрасенсорными сбособностями увидеть бегло написанный код)))
или парсеры такие и должны быть и этот вполне нормальный?
НЕТ! смотря какой сайт, и смотря что вам нужно. У меня есть парсер на 50 строк, а есть парсер для большого збора и анализа информации на 600 строк. НО, я никому не писал, посмотрите на мой код, не читайте все 600 строк, просто скажите, я молодец?
ещё раз повторюсь, что вопрос не по программе, а именно по способу организации кода
как ни старался код не дублировать, всё таки получилась программа трудная для чтения.
как я могу улучшить код, не вникая в него, как я могу показать вам как не дублировать код, не читая его?!



————————————————
-*- Simple is better than complex -*-

Отредактировано terabayt (Март 4, 2014 22:47:25)

Офлайн

#3 Март 5, 2014 00:40:44

dimy44
От: Евпатория
Зарегистрирован: 2012-04-21
Сообщения: 463
Репутация: +  42  -
Профиль  

организация процедурного кода

Резко высказались, человек не Вам домой письмо прислал. День не сложился? А может, кто за чашечкой кофе что и полезного ответит, это ж форум. Он не наглеет, не попрошайничает, по-нормальному спросил, чего Вы?

Офлайн

#4 Март 5, 2014 01:27:46

terabayt
От: Киев
Зарегистрирован: 2011-11-26
Сообщения: 1099
Репутация: +  103  -
Профиль   Отправить e-mail  

организация процедурного кода

dimy44
Да, спасибо, день - дерьмо! Может и резко, но немного нравоучений не помешает. И такие же потом на работу идут. Я уже четвертый день пытаюсь решить неришимую проблему и с каждым днем все хуже и хуже. Просто, если сейчас не наставить на правильный путь, то потом, придя на работу, ему будет очень сложно! Ненавижу работать “в команде”!!!!!!
PS. Спасибо что выслушали, мне это очень помогло! Может начать вести дневник)))



————————————————
-*- Simple is better than complex -*-

Отредактировано terabayt (Март 5, 2014 01:28:22)

Офлайн

#5 Март 5, 2014 01:34:18

terabayt
От: Киев
Зарегистрирован: 2011-11-26
Сообщения: 1099
Репутация: +  103  -
Профиль   Отправить e-mail  

организация процедурного кода

zlodiak
Если хотите писать красивый код, нужно:
1. Читать внимательно документации.
2. Смотрите открытые проекты. Там бывают очень красивые решения!
Вы сами должны понять, как лучше!



————————————————
-*- Simple is better than complex -*-

Офлайн

#6 Март 5, 2014 12:13:40

JOHN_16
От: Россия, Петропавловск-Камчатск
Зарегистрирован: 2010-03-22
Сообщения: 3292
Репутация: +  221  -
Профиль   Отправить e-mail  

организация процедурного кода

zlodiak
priceRUR = [t.replace('\xa0', ‘ ’) for t in doc.xpath('//div/span/text()')]
priceRUR = priceRUR
priceRUR = priceRUR.replace(u' руб', '')
можно поменять на “конвейрный” прием
priceRUR = [t.replace('\xa0', ' ') for t in doc.xpath('//div[@id="prcIsumConv"]/span/text()')][0].replace(u' руб', '')[/quote]


terabayt
1 еще соглашусь, но с натяжкой. 2 - только для опытных.
terabayt
Я уже четвертый день пытаюсь решить неришимую проблему и с каждым днем все хуже и хуже.
если вам полегчает, то вы не один такой, я тоже, бьюсь над проблемой из разряда “в доках написано” в одном случае код работает в другом тот же не работает. жизнь…она такая.. приходится упорно решать проблемы



_________________________________________________________________________________
полезный блог о python john16blog.blogspot.com

Офлайн

#7 Март 5, 2014 13:12:41

PanovSergey
От: Екатеринбург
Зарегистрирован: 2013-12-29
Сообщения: 269
Репутация: +  19  -
Профиль   Адрес электронной почты  

организация процедурного кода

Не рекомендуется для названия переменных использовать mountain style. Лучше так link_list. При этом сначала пишем сущность link затем принадлежность list.

listLinks
Никогда не нужно отлавливать все исключения, ловите конкретный который вы ждете
try:
        req = requests.get(url)
    except Exception:
        print('Error open. __', Exception)
Такая запись слишком длинная и плохо читается, опять же противоречит pep8.
[t.replace('\xa0', ' ') for t in doc.xpath('//span[@itemprop="price"]/text()')] or [t.replace('\xa0', ' ') for t in doc.xpath('//span[@id="mm-saleDscPrc"]/text()')]
Можно так переписать
    listGoodsLinks = []
    #i = 0
    for path in listPages:
        doc = get_doc(path)
        listGoods = doc.xpath('//div[@id="ResultSetItems"]/table//h3/a')
        #i = i + 1
        #if i > 1: break
        for elem in listGoods:
            href = elem.get('href')
            listGoodsLinks.append(href) 
result = []
for path in path_list:
    doc = get_doc(path)
    goods_list = doc.xpath('//div[@id="ResultSetItems"]/table//h3/a')
    goods_link_list = [elem.get('href') for elem in goods_list]
    result = chain(result, goods_link_list)

Отредактировано PanovSergey (Март 5, 2014 13:16:20)

Офлайн

#8 Март 6, 2014 04:44:36

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 10016
Репутация: +  857  -
Профиль   Отправить e-mail  

организация процедурного кода

zlodiak
всё таки получилась программа трудная для чтения

1)
пиши сверху-вниз:
чтобы написать парсер, нужно первое, второе и третье
чтобы написать первое, нужно четвертое, пятое и шестое
чтобы написать второе, нужно седьмое, восьмое и девятое
чтобы написать третье, нужно десятое, одиннадцатое и двенадцатое
чтобы написать четвёртое, нужно ...

когда так пишешь, получается не только то, что ты изначально задумывал, но и можно делать заглушки

напиши функцию main() и от неё спускайся вниз, пока не дойдёшь до дна

2)
программа (процедура или функция) - это последовательность утверждений
у неё есть первое утверждение и последнее, через которые она присоединяется к программам (процедурам), вызывающим её

zlodiak
def record_xml(xml, fileName='xml.xml'):
    try:
        with open(fileName, "w") as file:
            file.write(xml)
    except Exception:
        print('Error record', Exception)
    else:
        print('record ok')

перед тем как писать программу (процедуру), нужно её разработать
a) первое и последнее утверждение
дано: строка с xml-кодом и имя файла для вывода
получить: строка записана в файл, файл закрыт

b) название, соответствующее заголовку
xml_to_file(xml, ofname)

c) последовательность утверждений
дано: строка с xml-кодом и имя файла для вывода

утв: файл открыт

утв: строка выведена в файл

утв: файл закрыт

получить: строка записана в файл, файл закрыт

здесь видно, что никакого вывода на экран не надо

d) реализация программы
между утверждениями записываешь код, который выполняет переход от одного к другому

дано: строка с xml-кодом и имя файла для вывода

открыть файл

утв: файл открыт

записать строку в файл

утв: строка выведена в файл

закрыть файл

утв: файл закрыт

ничего не делать

получить: строка записана в файл, файл закрыт

итак, чтобы написать парсер, его нужно инициализировать, …



Отредактировано py.user.next (Март 6, 2014 04:47:16)

Офлайн

#9 Март 7, 2014 20:18:33

zlodiak
От: Россия
Зарегистрирован: 2014-01-19
Сообщения: 159
Репутация: +  0  -
Профиль   Адрес электронной почты  

организация процедурного кода

спасибо большое за важную информацию. честно говоря, даже не надеялся на такие подробные ответы

вот по этому есть вопрос, если можно…

PanovSergey
Можно так переписать
    listGoodsLinks = []
    #i = 0
    for path in listPages:
        doc = get_doc(path)
        listGoods = doc.xpath('//div[@id="ResultSetItems"]/table//h3/a')
        #i = i + 1
        #if i > 1: break
        for elem in listGoods:
            href = elem.get('href')
            listGoodsLinks.append(href)
result = []
for path in path_list:
    doc = get_doc(path)
    goods_list = doc.xpath('//div[@id="ResultSetItems"]/table//h3/a')
    goods_link_list = [elem.get('href') for elem in goods_list]
    result = chain(result, goods_link_list)
Отредактировано PanovSergey (Март 5, 2014 13:16:20)


перед написанием кода у меня была подобная мысль, но под впечатлением от этого видео я решил не смешивать 2 разных стиля написания кода. если кратко, то в указанном видео докладчик рассказывает об императивном и декларативном стиле. упрощённо: императивный это последовательность команд(состояний программы), а декларативный это что-то типа конвейера. помня, что явное лучше неявного, я для конкретно этой программы выбрал императивный стиль написания кода

скажите пожалуйста имело ли смысл мне заморачиваться с такими почти филосовскими вещами или это всё совсем не важно? пусть программа простейшая, но я хотел бы научиться писать код максимально осмысленный

ps
даже таких конструкций я стараюсь избегать:
for x in str(array):

а записывать их так:
array = str(array)
for x in array:

Отредактировано zlodiak (Март 7, 2014 20:25:02)

Офлайн

#10 Март 7, 2014 20:49:08

dimy44
От: Евпатория
Зарегистрирован: 2012-04-21
Сообщения: 463
Репутация: +  42  -
Профиль  

организация процедурного кода

даже таких конструкций я стараюсь
избегать: for x in str(array):
a записывать их так:
array = str(array)
for x in array:
а чем первый вариант не угодил? Там все в одном месте и сразу ясно, а тут надо разносить внимание на 2 строки ).

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version