Уведомления

Группа в Telegram: @pythonsu

#1 Окт. 13, 2021 12:25:20

Cyr
Зарегистрирован: 2012-09-08
Сообщения: 13
Репутация: +  0  -
Профиль   Отправить e-mail  

Парсинг html таблиц от FastReport

Генератор отчётов FastReport выгружает таблички в html, в которых ячейки представлены тегами <div>
Это выглядит так:

 <div class="s8" style="left:946.39px;top:61.83px;width:61.37px;height:42.89px;"><div class="s7">Стоимость, руб.</div></div>
<div class="s10" style="left:88.33px;top:61.83px;width:53.81px;height:42.89px;"><div class="s9">Дата</div></div>
<div class="s12" style="left:143.14px;top:61.83px;width:46.25px;height:42.89px;"><div class="s11">Время</div></div>
<div class="s14" style="left:190.39px;top:61.83px;width:492.29px;height:42.89px;"><div class="s13">Вид услуги</div></div>
<div class="s16" style="left:683.68px;top:61.83px;width:63.26px;height:42.89px;"><div class="s15">Набранный номер, точка доступа</div></div>
<div class="s18" style="left:747.94px;top:61.83px;width:93.5px;height:42.89px;"><div class="s17">Местонахождение при использовании услуги</div></div>
<div class="s20" style="left:842.44px;top:61.83px;width:102.95px;height:42.89px;"><div class="s19">Объём</div></div>
<div class="s22" style="left:-0.5px;top:61.83px;width:87.83px;height:42.89px;"><div class="s21">Номер устройства (IMEI)</div></div>
<div class="s23" style="left:0px;top:106.22px;width:1009.26px;height:24.45px;">&nbsp;</div>
<div class="s25" style="left:88.33px;top:105.72px;width:53.81px;height:23.45px;"><div class="s24">04.10.2021</div></div>
<div class="s27" style="left:143.14px;top:105.72px;width:46.25px;height:23.45px;"><div class="s26">06:54:47</div></div>
<div class="s29" style="left:190.39px;top:105.72px;width:492.29px;height:23.45px;"><div class="s28">Исх. на мобильные дом. региона, Билайн; Пензенская Область, моб. связь, Билайн</div></div>
<div class="s31" style="left:683.68px;top:105.72px;width:63.26px;height:23.45px;"><div class="s30">89699900000</div></div>
<div class="s33" style="left:747.94px;top:105.72px;width:93.5px;height:23.45px;"><div class="s32">Пензенская обл.</div></div>
<div class="s35" style="left:842.44px;top:105.72px;width:48.14px;height:23.45px;"><div class="s34">9,00</div></div>
<div class="s37" style="left:-0.5px;top:105.72px;width:87.83px;height:23.45px;"><div class="s36"></div></div>
<div class="s38" style="left:891.58px;top:105.72px;width:53.81px;height:23.45px;"><div class="s24">Секунда</div></div>
<div class="s40" style="left:946.39px;top:105.72px;width:61.37px;height:23.45px;"><div class="s39">0,00</div></div>
<div class="s23" style="left:0px;top:130.67px;width:1009.26px;height:24.45px;">&nbsp;</div>
Как получить данные из этого отчёта? С учётом того что некоторые ячейки могут быть пустые.
Я пробовал так:
kol4 = tree.xpath('//div[contains(@style, "left:190.39px;")]/div/text()') #Вид услуги
print (kol4, len(kol4))
kol7 = tree.xpath('//div[contains(@style, "left:842.44px;")]/div/text()') #Объём
print (kol7, len(kol7))
kol8 = tree.xpath('//div[contains(@style, "left:891.58px")]/div/text()') #Ед.изм.
print (kol8, len(kol8))
kol4 = tree.xpath('//div[(@class="s29" or @class="s47")]/div/node()') #Вид услуги
kol7 = tree.xpath('//div[(@class="s35" or @class="s53")]/div/node()') #Объём
kol8 = tree.xpath('//div[(@class="s38" or @class="s56")]/div/node()') #Ед.изм.
Но из-за пустых ячеек, которые xpath не берёт, получается разное количество значений. И записи по строкам невозможно составить правильно.

Отредактировано Cyr (Окт. 13, 2021 15:53:49)

Офлайн

#2 Окт. 13, 2021 14:03:05

Cyr
Зарегистрирован: 2012-09-08
Сообщения: 13
Репутация: +  0  -
Профиль   Отправить e-mail  

Парсинг html таблиц от FastReport

Решил тупо в лоб эту задачу

def spliting(line):
start=line.rfind('">')+2
end=line.rfind('</div></div>')
mes='"'+line[start:end]+'"'
return mes

f = open('c:/Downloads/Детализация.html', encoding="utf-8")
for line in f:
if line.rfind('left:190.39px;')>0:
print (spliting(line), end=';')
elif line.rfind('left:842.44px;')>0:
print (spliting(line), end=';')
elif line.rfind('left:891.58px;')>0:
print (spliting(line))

Отредактировано Cyr (Окт. 13, 2021 14:17:55)

Офлайн

#3 Окт. 13, 2021 14:47:34

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

Парсинг html таблиц от FastReport

Cyr
С учётом того что некоторые ячейки могут быть пустые.
Проблема в том, что ты ищешь по атрибуту style. Это самое глупое, что можно было сделать.

Cyr
Как получить данные из этого отчёта?
Где здесь отчёт? Это лишь маленький кусочек чего-то целого. И что значит “некоторые ячейки могут быть пустые”? Это отсутствие тегов для них или просто наличие тегов, но отсутствие текста в них?



Отредактировано py.user.next (Окт. 13, 2021 14:48:29)

Офлайн

#4 Окт. 13, 2021 15:11:54

Cyr
Зарегистрирован: 2012-09-08
Сообщения: 13
Репутация: +  0  -
Профиль   Отправить e-mail  

Парсинг html таблиц от FastReport

py.user.next
Проблема в том, что ты ищешь по атрибуту style. Это самое глупое, что можно было сделать.
Отступы слева в стиле определяют положение колонок таблицы. По ним определяем что это за колонка. По class тоже можно искать. Но в одной колонке они бывают с несколькими именами.

py.user.next
Где здесь отчёт? Это лишь маленький кусочек чего-то целого.
Это заголовок и первая строка из таблицы. Дальше там строки повторяются.

py.user.next
И что значит “некоторые ячейки могут быть пустые”? Это отсутствие тегов для них или просто наличие тегов, но отсутствие текста в них?
Отсутствие текста в последних тегах div.

Отредактировано Cyr (Окт. 13, 2021 15:49:32)

Офлайн

#5 Окт. 13, 2021 22:21:58

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

Парсинг html таблиц от FastReport

Cyr
Отступы слева в стиле определяют положение колонок таблицы.
Не, это понятно. Просто стили меняются каждые три секунды. По ним нельзя ориентироваться, потому что через неделю их сделают другими и вся твоя программа сломается из-за этого. И так будет повторяться каждый раз. Надо более стабильные признаки выбирать (один признак или устойчивую группу признаков).

Cyr
По class тоже можно искать.
Приведи полный пример. Приведи таблицу на три записи хотя бы. На ней и можно поискать стабильные признаки для определения строк и для определения полей в строках. Не говорю колонки, так как колонки не всегда являются минимальными элементами, составляющими строку.

Cyr
Отсутствие текста в последних тегах div.
Отсутствующий текст видно хорошо. Надо просто по-другому записывать его поиск. То есть не факт, что там именно XPath надо применять.

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



Отредактировано py.user.next (Окт. 13, 2021 22:33:05)

Офлайн

#6 Окт. 13, 2021 22:52:50

xam1816
Зарегистрирован: 2020-05-11
Сообщения: 1309
Репутация: +  113  -
Профиль   Отправить e-mail  

Парсинг html таблиц от FastReport

Выложи сюда вот этот файл

 c:/Downloads/Детализация.html

Офлайн

#7 Окт. 14, 2021 09:17:14

Cyr
Зарегистрирован: 2012-09-08
Сообщения: 13
Репутация: +  0  -
Профиль   Отправить e-mail  

Парсинг html таблиц от FastReport

xam1816
Выложи сюда вот этот файл
Во вложении реальный файл с нереальными номерами.
py.user.next
Отсутствующий текст видно хорошо. Надо просто по-другому записывать его поиск. То есть не факт, что там именно XPath надо применять.
Ну вот корректно работающий скрипт:
 # -*- coding: utf-8 -*-
# Скрипт импорта детализации html в csv
def spliting(line):
    start=line.rfind('">')+2
    end=line.rfind('</div></div>')
    mes=line[start:end]
    return mes.replace('&nbsp;&nbsp;', ' ')
    
f = open('c:/Downloads/Детализация.html', encoding="utf-8")
out = open('c:/Downloads/Детализация.csv', 'w')
for line in f:
    if line.rfind('class="s29"')>0:   # Вид услуги исх.
        out.write ('"'+spliting(line)+'";')
    elif line.rfind('class="s47"')>0: # Вид услуги вх. 
        out.write ('"'+spliting(line)+'";')    
    elif line.rfind('class="s35"')>0: # Объём исх. 
        out.write (spliting(line)+';')
    elif line.rfind('class="s53"')>0: # Объём вх. 
        out.write (spliting(line)+';')         
    elif line.rfind('class="s38"')>0: # Ед.изм. исх
        out.write ('"'+spliting(line)+'"\n')
    elif line.rfind('class="s56"')>0: # Ед.изм. вх. 
        out.write ('"'+spliting(line)+'"\n')
out.close
Наверное есть более правильный метод решения задачи.

Отредактировано Cyr (Окт. 14, 2021 09:32:36)

Прикреплённый файлы:
attachment Детализация.html (220,2 KБ)

Офлайн

#8 Окт. 14, 2021 10:24:31

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

Парсинг html таблиц от FastReport

Cyr
Прикреплённый файлы:
attachment Детализация.html (220,2 KБ)
Там несколько страниц. Их надо соединить в одну? Соединить несколько страниц в одну и потом каждый блок из девяти полей превратить в одну строку в CSV-файле?



Офлайн

#9 Окт. 14, 2021 10:29:52

Cyr
Зарегистрирован: 2012-09-08
Сообщения: 13
Репутация: +  0  -
Профиль   Отправить e-mail  

Парсинг html таблиц от FastReport

py.user.next
Соединить несколько страниц в одну и потом каждый блок из девяти полей превратить в одну строку в CSV-файле?
Мой скрипт корректно выбирает нужные поля (всего 3) по class, соединяет их в строку. И проходит он по всем страницам. div, в которых колонтитулы и остальные поля он игнорирует же.
Скрипт совсем нечитаемый?

Отредактировано Cyr (Окт. 14, 2021 10:43:23)

Офлайн

#10 Окт. 14, 2021 10:44:54

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

Парсинг html таблиц от FastReport

Cyr
Мой скрипт корректно выбирает нужные поля (всего 3) по class из всех страниц.
Опиши, что нужно сделать, без своего скрипта. Словами опиши. Вот файл детализации, из него надо выбрать поля с тем-то и тем-то и сохранить их так-то и так-то.

Потому что твой скрипт реально пугает типа такими вот фрагментами
Cyr
 out.close
Ты не думал, что close - это функция такая и её надо вызвать с помощью круглых скобочек?

Естественно, никто не пишет CSV-файлы, проставляя там разделитель вручную. Да и разделитель в CSV-файлах должен быть запятой, потому что CSV - это Comma-Separated Values, где слово comma переводится как запятая. И признак окончания записи в таком файле не \n, а \r\n. Ну, ты этого не знаешь всего, это видно.

Так что не надо свой чудо-скрипт пихать нам; описывай словами задачу, а мы будем думать уже, как это правильно сделать.



Отредактировано py.user.next (Окт. 14, 2021 10:46:40)

Офлайн

Board footer

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

Powered by DjangoBB

Lo-Fi Version