Нечетный столбец возвращается при очистке с lxml - PullRequest
0 голосов
/ 26 апреля 2011

Я изучаю Python и пытаюсь создать скребок для сбора данных о деталях с сайта поставщиков.Теперь моя проблема заключается в том, что я получаю различное количество столбцов из строк моей проанализированной таблицы, где я знаю, что каждая строка имеет одинаковое количество столбцов.Я должен упускать из виду эту проблему, и после двух дней пробовать разные вещи, я прошу еще несколько слов о моем коде, чтобы найти мою ошибку.Несчастный опыт написания кода на Python, без сомнения, является моим самым большим препятствием.

Во-первых, данные.Вместо того, чтобы вставлять HTML, который я храню в своей базе данных, я дам вам ссылку на живой сайт, который я просканировал и сохранил в своей базе данных.Первая ссылка эта .

Проблема в том, что я получаю в основном правильные результаты.Тем не менее, время от времени я получаю искаженные значения в счетчике столбцов.Кажется, я не могу найти причину.

Вот пример ошибочного результата:

----------------------------------------------------------------------------------
Record: 1 Section:Passenger  /  Light Truck Make: ACURA SubMake: 
Model: CL SubModel:  Year: 1997 Engine: L4 1.6L 1590cc
----------------------------------------------------------------------------------
Rec:1 Row 6 Col 1 part Air Filter
Rec:1 Row 6 Col 2 2 
Rec:1 Row 6 Col 3 part_no 46395
Rec:1 Row 6 Col 4 filter_loc 
Rec:1 Row 6 Col 5 engine 
Rec:1 Row 6 Col 6 vin_code V6 3.0L 2997cc
Rec:1 Row 6 Col 7 comment Engine Code J30A1

** Обратите внимание, что значение двигателя было смещено в поле vin_code.

И доказательство того, что это работает некоторое время:


Record: 2 Section:Passenger  /  Light Truck Make: ACURA SubMake: 
Model: CL SubModel:  Year: 1998 Engine: L4 1.6L 1590cc
----------------------------------------------------------------------------------
Rec:3 Row 4 Col 1 part Oil Filter
Rec:3 Row 4 Col 2 2 
Rec:3 Row 4 Col 3 part_no 51334
Rec:3 Row 4 Col 4 filter_loc 
Rec:3 Row 4 Col 5 engine L4 2.3L 2254cc
Rec:3 Row 4 Col 6 vin_code
Rec:3 Row 4 Col 7 comment Engine Code F23A1

** Обратите внимание, что поля в этой записи выстроены в ряд ...

Я подозреваю, что естьчто-то в ячейках таблицы мой парсер не ищет или я пропустил что-то тривиальное.

Вот важная часть моего кода:

# Per Query
while records:
    # Per Query Loop
    #print str(records)
    for record in records:
        print 'Record Count:'+str(rec_cnt)
        items = ()
        item = {}
        source = record['doc']
        page = html.fromstring(source)

        for rows in page.xpath('//div/table'):
            #records = []
            item = {}
            cntx = 0
            for row in list(rows):
                cnty = 0 # Column Counter
                found_oil = 0 # Found oil filter record flag
                data = {} # Data
                # Data fields
                field_data = {'part':'',   'part_no':'', 'filter_loc':'',  'engine':'',  'vin_code':'',  'comment':'', 'year':''}
                print
                print '----------------------------------------------------------------------------------'
                print 'Record: '+str(record['id']), 'Section:'+str(record['section']),  'Make: '+str(record['make']),   'SubMake: '+str(record['submake'])
                print  'Model: '+str(record['model']),  'SubModel: '+str(record['submodel']),  'Year: '+str(record['year']),  'Engine: '+str(record['engine'])
                print '----------------------------------------------------------------------------------'

                #
                # Rules for extracting data columns
                # 1. First column always has a link to the bullet image
                # 2. Second column is part name
                # 3. Third column always empty
                # 4. Fourth column is  part number
                # 5. Fith column is empty
                # 6. Sixth column is part location
                # 7. Seventh column is always empty
                # 8. Eigth column is engine size
                # 9. Ninth column is vin code
                # 10. Tenth column is COmment
                # 11. Eleventh column does not exist.
                #
                for column in row.xpath('./td[@class="blackmedium"][text()="0xa0"] | ./td[@class="blackmedium"][text()="\n"]/text() | ./td[@class="blackmeduim"]/img[@src]/text()  | ./td[@class="blackmedium"][text()=""]/text() | ./td[@class="blackmedium"]/b/text() | ./td[@class="blackmedium"]/a/text() |./td[@class="blackmedium"]/text() | ./td[@class="blackmedium"][text()=" "]/text() | ./td[@class="blackmedium"][text()="&#160"]/text() | ./td[@class="blackmedium"][text()=None]/text()'): 
                    #' | ./td[position()>1]/a/text() | ./td[position()>1]/text() | self::node()[position()=1]/td/text()'):
                    cnty+=1
                    if ('Oil Filter' == column.strip() or 'Air Filter' == column.strip()) and found_oil == 0:
                        found_oil = 1

                    if found_oil == 1:
                        print 'Rec:'+str(rec_cnt), 'Row '+str(cntx),  'Col '+str(cnty),  _fields[cnty],  column.strip()
                        #cnty+= 1
                        #print
                    else:
                        print 'Rec: '+str(rec_cnt),  'Col: '+str(cnty)

                    field_data[ str(_fields[cnty]) ] = str(column.strip())
                    #cnty = cnty+1

                # Save data to db dest table
                if found_oil == 1:
                    data['source_id'] = record['id']
                    data['section_id'] = record['section_id']
                    data['section'] = record['section']
                    data['make_id'] = record['make_id']
                    data['make'] = record['make']
                    data['submake_id'] = record['submake_id']
                    data['submake'] = record['submake']
                    data['model_id'] = record['model_id']
                    data['model'] = record['model']
                    data['submodel_id'] = record['submodel_id']
                    data['submodel'] = record['submodel']
                    data['year_id'] = record['year_id']
                    data['year'] = record['year']
                    data['engine_id'] = record['engine_id']
                    data['engine'] = record['engine']
                    data['part'] = field_data['part']
                    data['part_no'] = field_data['part_no']
                    data['filter_loc'] = field_data['filter_loc']
                    data['vin_code'] = field_data['vin_code']
                    data['comment'] = conn.escape_string(field_data['comment'])

                    data['url'] = record['url']
                    save_data(data)
                    print 'Filed Data:'
                    print field_data

                cntx+=1
            rec_cnt+=1
    #End main per query loop 
    delay() # delay if wait was passed on cmd line
    records = get_data()
    has_offset = 1
    #End Queries

Спасибо всем за помощь и ваши глаза ...

Ответы [ 4 ]

0 голосов
/ 28 апреля 2011

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

#
# get_column_index()
# returns a dict of column names/column number pairs
#
def get_column_index(row): 
    index = {}
    col_no = 0
    td = None
    name = ''
    for col_no,  td in enumerate(row,  start=0):
        mystr = str(td.xpath('string()').encode('ascii',  'replace'))
        name =  str.lower(mystr).replace(' ', '_')
        idx = name.replace('.', '')
        index[idx] =  col_no

    if int(options.verbose) > 2:
        print 'Field Index:',  str(index)

    return index




def run():
    global has_offset
    records = get_data()

    #print 'Records',  records
    rec_cnt = 0

    # Per Query
    while records:
        # Per Query Loop
        #print str(records)
        for record in records:
            if int(options.verbose) > 0:
                print 'Record Count:'+str(rec_cnt)

            items = ()
            item = {}
            source = record['doc']
            page = html.fromstring(source)
            col_index = {}

            for rows in page.xpath('//div/table'):
                #records = []
                item = {}
                cntx = 0
                for row in list(rows):
                    data = {} # Data
                    found_oil = 0 #found proper part flag
                    # Data fields
                    field_data = {'part':'',   'part_no':'', 'part_note':'',  'filter_loc':'',  'engine':'',  'vin_code':'',  'comment':'', 'year':''}

                    if int(options.verbose) > 0:
                        print
                        print '----------------------------------------------------------------------------------'
                        print 'Row'+str(cntx), 'Record: '+str(record['id']), 'Section:'+str(record['section']),  'Make: '+str(record['make']),   'SubMake: '+str(record['submake'])
                        print  'Model: '+str(record['model']),  'SubModel: '+str(record['submodel']),  'Year: '+str(record['year']),  'Engine: '+str(record['engine'])
                        print '----------------------------------------------------------------------------------'

                   # get column indexes
                    if cntx == 1:
                        col_index = get_column_index(row)

                    if col_index != None and cntx > 1:
                        found_oil = 0

                        for col_no,  td in enumerate(row):

                            if ('part' in col_index) and (col_no == col_index['part']):
                                part = td.xpath('string()').strip()
                                if 'Oil Filter' == part or 'Air Filter' == part or 'Fuel Filter' == part or 'Transmission Filter' == part:
                                    found_oil = 1
                                    field_data['part'] = td.xpath('string()').strip()

                            # Part Number
                            if ('part_no' in col_index) and (col_no == col_index['part_no']):
                                field_data['part_no'] = str(td.xpath('./a/text()')).strip().replace('[', '').replace(']', '').replace("'", '')
                                field_data['part_note'] = str(td.xpath('./sup/text()')).strip().replace('[', '').replace(']', '').replace("'", '')

                            # Filter Location
                            if ('filterloc' in col_index) and (col_no == col_index['filterloc']):
                                field_data['filter_loc'] = td.xpath('string()').strip()

                            # Engine
                            if ('engine' in col_index) and (col_no == col_index['engine']):
                                field_data['engine'] = td.xpath('string()').strip()

                            if ('vin_code' in col_index) and (col_no == col_index['vin_code']):
                                field_data['vin_code'] = td.xpath('string()').strip()

                            if ('comment' in col_index) and (col_no == col_index['comment']):
                                field_data['comment'] = td.xpath('string()').strip()

                            if int(options.verbose) == 0:
                                print ',' 


                        if int(options.verbose) > 0:
                            print 'Field Data: ',  str(field_data)
                        elif int(options.verbose) == 0:
                            print '.'

                    # Save data to db dest table
                    if found_oil == 1:
                        data['source_id'] = record['id']
                        data['section_id'] = record['section_id']
                        data['section'] = record['section']
                        data['make_id'] = record['make_id']
                        data['make'] = record['make']
                        data['submake_id'] = record['submake_id']
                        data['submake'] = record['submake']
                        data['model_id'] = record['model_id']
                        data['model'] = record['model']
                        data['submodel_id'] = record['submodel_id']
                        data['submodel'] = record['submodel']
                        data['year_id'] = record['year_id']
                        data['year'] = record['year']
                        data['engine_id'] = record['engine_id']
                        data['engine'] = field_data['engine'] #record['engine']
                        data['part'] = field_data['part']
                        data['part_no'] = field_data['part_no']
                        data['part_note'] = field_data['part_note']
                        data['filter_loc'] = field_data['filter_loc']
                        data['vin_code'] = field_data['vin_code']
                        data['comment'] = conn.escape_string(field_data['comment'])

                        data['url'] = record['url']
                        save_data(data)
                        found_oil = 0

                        if int(options.verbose) > 2:
                            print 'Data:', str(data)

                    cntx+=1
                rec_cnt+=1
        #End main per query loop 
        delay() # delay if wait was passed on cmd line
        records = get_data()
        has_offset = 1
        #End Queries
0 голосов
/ 26 апреля 2011

Можете ли вы передать детали вашего процесса утилизации?Периодические сбои могут быть основаны на разборе HTML-данных.

0 голосов
/ 27 апреля 2011

Кажется, проблема в том, что ваше выражение xpath ищет текстовые узлы.Для пустых ячеек не найдено совпадений, в результате чего ваш код пропускает столбцы.Попробуйте перебрать сами элементы td, а затем «смотреть вниз» от элемента к его содержимому.Для начала:

# just iterate over child elements of the row, which are always td
# use enumerate to easily get a counter for the columns
for col_no, td in enumerate(row, start=1):
    # use the xpath function string() to get the string value for the element
    # this will yield an empty string for empty elements
    print col_no, td.xpath('string()')

Обратите внимание, что использование функции string() xpath в некоторых случаях может быть недостаточно / слишком просто для того, что вы хотите.В вашем примере вы можете найти что-то вроде <td><a>51334</a><sup>53</sup></td> (см. Масляный фильтр).Мой пример дал бы вам «5133453», где вам, возможно, понадобится «51334» (не уверен, было ли это преднамеренно или вы не заметили «пропущенную» часть, если вам нужна только ссылка в гиперссылке, используйте td.findtext('a'))

0 голосов
/ 26 апреля 2011

Обычно, когда я сталкиваюсь с такой проблемой, я делаю две вещи:

  1. Разбейте проблему на более мелкие куски. Используйте функции или классы python для выполнения подмножеств функциональности, чтобы можно было проверить правильность функций по отдельности.
  2. Используйте Python Debugger для проверки кода во время его работы, чтобы понять, где он выходит из строя. Например, в этом случае я бы добавил import pdb; pdb.set_trace() перед строкой, которая говорит cnty+=1.

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

Несколько советов по использованию pdb:

Используйте c, чтобы позволить программе продолжаться (до следующей точки останова или set_trace); Используйте n для перехода к следующей строке в программе. Используйте q, чтобы вызвать исключение (и обычно прерывать).

...