LINUX.ORG.RU

Парсинг кривого html из bash

 ,


0

1

Есть html страница из которой нужно выдергуть данные вот примерно такого плана:


<td id="TableInfo12345" class="col123">Текст что нужно получить из таблицы</td>

Пытаюсь получить данные используя hxselect из пакета html-xml-utils

hxselect -c 'td[id="TableInfo12345"]' < file.html

Постоянно вываливаются ошибки, что тег такой-то не закрыт, не понятно откуда взялся другой тег. Ошибка на ошибке и ошибкой погоняет т.к. пасал сайт студент на коленке. Если использовать нормальную страницу где все поуму то ошибок нет. Вопрос: как правильно распарсивать из bash страницу html если ее сделали рукожопы?

Башем можно, но больно. Я бы брал сразу Perl. И увы, похоже, придётся именно под этот кривой HTML и затачивать. Если вид и кривость HTML заранее известны, тогда можно обойтись малой кровью на регулярках. (Хотя это сильно флеймогонная тема.)

Или задача стоит парсить И нормальный HTML, И кривой (с разных сайтов)?

hobbit ★★★★★
()
Последнее исправление: hobbit (всего исправлений: 1)
Ответ на: комментарий от hobbit

Парсить нужно все подряд включая нормальные сайты где всё нормально написано. Был уже написан скрипт который обрабатывал нормальные сайты, но тут появился кривой и всё встало. Использовать питон не хотелось бы т.к. bash скрипт уже есть и он работает, но у вы только на нормальных сайтах. Может можно как-то игнорировать кривизну html или какой-то программо исправить ошибки?

AnastasiaM
() автор топика
Ответ на: комментарий от AnastasiaM

Парсить нужно все подряд включая нормальные сайты где всё нормально написано.

Это печально.

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

P.S. Наводящий вопрос: должен ли скрипт работать (например) на сайтах с плохо форматированным (но при этом корректным) HTML, где вся таблица в одну строку?

hobbit ★★★★★
()
Последнее исправление: hobbit (всего исправлений: 1)
Ответ на: комментарий от ABW

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

hobbit ★★★★★
()

В свое время дергал из баша xmllint, и всё прекрасно парсилось.

Bagrov ★★★★★
()

что тег такой-то не закрыт, не понятно откуда взялся другой тег

Тег td и не обязан быть закрыт, согласно спецификации, это тебе не xml. Кривой у тебя не документ, а руки. Ты парсишь html-документ xml-парсером.

anonymous
()
Ответ на: комментарий от ABW

tidy не помогает, часть ошибок ушло, а часть осталась. Старые ошибки ушли, но появились другие ошибки.

AnastasiaM
() автор топика
Ответ на: комментарий от anonymous

Ошибка не на тег td, а на другие теги img, meta, div… Если есть другой более лучший инструмент для парсинга кривых html под bash тогда скажи что использовать? Буду признательна за подсказку.

AnastasiaM
() автор топика
Ответ на: комментарий от anonymous

Да один фиг, нет и не может быть простого универсального решения для 100% успешного парсинга «диких» данных. Тем более с ограничениями вида только на баш.

Закладывайте допустимый процент отказов в случае неопределенного набора данных. Или затачивайте индивидуально под конкретные сайты, если их набор ограничен. С периодическими переделками, если где поменяется форматирование

anonymous
()
Ответ на: комментарий от AnastasiaM

Каждый кривой html крив по-своему. Так что даже, если что-то найдется лучшее, оно все-равно будет лажать на каких-то сайтах. Смирись с каким-то процентом ошибок или точи парсер на конкретные сайты

anonymous
()
Ответ на: комментарий от anonymous

Из найденых вариантов есть только костыльный. Только для кривого сайта использовать такой подход:

cat  file.html | grep 'TableInfo12345' | sed -e 's/<[^>]*>//g'

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

AnastasiaM
() автор топика

libxml2 умеет невалидный хтмл. Посмотри, может подойдёт для твоей задачи.

anonymous
()
Ответ на: комментарий от AnastasiaM

Если есть другой более лучший инструмент для парсинга кривых html

Дай определение кривому html.

У html кривость - это непрописанные в нужном месте обязательные аттрибуты элементов, например. Парсеры на таком не могут ломаться. это называется валидация.

Кривостью о которой гвооришь ты могут обладать только xml документы, html в целом не может быть НЕ РАСПАРСЕН хтмл-парсеом, просто потому, что любая строка - это валидный html.

anonymous
()
Ответ на: комментарий от AnastasiaM

Ты попробуй всё-таки этот BeautifulSoup из Python. Если он справится с чудовищно невалидным html (по идее должен), сделай скрипт-утилитку подобную hxselect и вызывай её из своего Bash-скрипта в случаях когда дичь-сайты нужно распарсить.

EXL ★★★★★
()

Тебе же уже посоветовали xmllint. Он основан на libxml2 и должен хорошо уметь в html. Быстрый поиск по ману выдал ключ –html.

Stack77
()
Ответ на: комментарий от anonymous

Кривой html в моем случаи это не закрывающиеся теги, они в большом количестве в той странице что нужно распарсить. Используемый hxselect спотыкается и не понимает почему появляются новые теги если еще старый тег не закрыт.

AnastasiaM
() автор топика
Ответ на: комментарий от AnastasiaM

xmllint –html –xpath «//td[@id=‘TableInfo12345’]/text()» index.html

libxml - это библиотека, ты как её к башу подключать собралась? Или скрипт пишешь на пайтоне, например? Но зачем, если есть xmllint?

Stack77
()
Последнее исправление: Stack77 (всего исправлений: 1)
Ответ на: комментарий от AnastasiaM

Незакрывающиеся теги - это нормальный, правильный html. Почему ты его называешь кривым?

anonymous
()
Ответ на: комментарий от AnastasiaM

Используемый hxselect спотыкается и не понимает почему появляются новые теги если еще старый тег не закрыт.

Потому что он и не должен быть закрыт. Еще раз - ты пытаешься парсить HTML документ, XML парсером. Вчитайся в то, что я тебе пишу. HTML у тебя нормальный. Ты выбираешь не правильный инструмент, потому что у тебя кривые руки.

anonymous
()
Ответ на: комментарий от Stack77

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

P.S. Но если взять тестовую страницу где все теги правильные то xmllint работает и выдает нужные данные.

AnastasiaM
() автор топика
Ответ на: комментарий от AnastasiaM

Какая ошибка? Неоднократно парсил с libxml2 в Qt, нормально разбирает html даже с ошибками (в большинстве случаев). Xmllint по идее не хуже работать должен.

Проверил, всё работает. Просто ошибки показывает в stderr (если невалидный), перенаправь их в null.

xmllint --html --xpath "//td[@id='TableInfo12345']/text()" test.html 2>/dev/null

Stack77
()
Последнее исправление: Stack77 (всего исправлений: 2)
Ответ на: комментарий от ddidwyll

Это оригинальный файл

 xmllint --html --xpath "//td[@id='TableInfo12345']/text()" orign.html
orign.html:289: HTML parser error : Unexpected end tag : a
</a></td><td class="tableCenter">332</a>
                                                                               

Это исправленный после tidy файл

xmllint --html --xpath "//td[@id='TableInfo12345']/text()" fix.html
fix.html:694: element h4: validity error : ID changeAnnounceMes already defined
<h4 class="modal-title" id="changeAnnounceMes">

Что-то с разметкой не то.

Как вариант еще попробовала написать скрипт на python с использованием BeautifulSoup и о чудо он не матюкается и выдает нужные данные. Скрипт на python пока кривой и я не связала работу с основным скриптом на bash, но ошибок слава богу нет. Попробую переписать всю логику работы с bash на python.

Вот кусок кода на python, выдает данные но в месте с тегами, щас попробую избавится от тегов и все будет хорошо:

from bs4 import BeautifulSoup
import urllib3

url = 'https://www.site.ru/path/file.html'

http = urllib3.PoolManager()
r = http.request('GET', url)
htmlSource = r.data

soup = BeautifulSoup(str(htmlSource),"html.parser")
vardata = soup.findAll("td", class_="TableInfo12345")

print("My Data : " + str(vardata))

P.S. Python знаю очень плохо, поэтому может быть пишу немного неправильно и криво.

AnastasiaM
() автор топика
Ответ на: удаленный комментарий

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

AnastasiaM
() автор топика
Ответ на: комментарий от AnastasiaM

Да, он постоянно флудит и флеймит на этом форуме, создаёт шум.

Попробую переписать всю логику работы с bash на python.

Вообще надо было сразу так, bash это не тот язык, на котором пишутся подобные вещи.

щас попробую избавится от тегов и все будет хорошо

Посмотри в сторону vardata.get_text() или vardata.text().

EXL ★★★★★
()
Ответ на: комментарий от AnastasiaM

Шитпостер скорее, но это сути не меняет.

anonymous
()
Ответ на: комментарий от AnastasiaM

откуда эта ошибка про сертификат?

Так как питона не знаю вообще, я ради любопытства попробовал сделать запрос из pwsh. Вот такой InnerException и вылез.

И я не Владимир.

anonymous
()
Ответ на: комментарий от AnastasiaM

Странно, что у тебя так что-то находится (или ты html-код в топике «левый» указала, или находится совсем не то, что ты искала). У тебя в твоем коде python ищутся все совпадения тега td c классом TableInfo12345, хотя в топике у тебя это не класс, а ID. И соответственно тебе нужно искать по ID одну единственную запись (т.к. id должен быть уникален на html-странице).

Stack77
()
Ответ на: комментарий от EXL

Да все верно, спасибо за подсказку. Вывод данных вот так выглядит:

for index,row in enumerate(vardata):
    print("My Data: " + row.text)

Условно полагаем что проблема решена. Старый рабочий скрипт что дергал данные будет потихоньку переписан на python. Bash похоже плохо подходит для работы с парсингом html. Всем спасибо.

AnastasiaM
() автор топика
Ответ на: комментарий от Stack77

В коде опечатка, я плохо знакома с python поэтому там код кривоват, я смогла найти пример и повторила его, там был в примере class. класс потом поменяля на id. И так и так работает (для класса клас вставила «col123», для id подставила id «TableInfo12345»). Класс как и id почему-то тоже уникален и только в этом месте таблицы встречается. Вот конечный пример:

так работает с id:

from bs4 import BeautifulSoup
import urllib3

url = 'https://www.site.ru/path/file.html'

http = urllib3.PoolManager()
r = http.request('GET', url)
htmlSource = r.data

soup = BeautifulSoup(str(htmlSource),"html.parser")
vardata = soup.findAll("td", id="TableInfo12345")

# так можно с классом
# vardata = soup.findAll("td", class_="col123")

for index,row in enumerate(vardata):
    print("My Data: " + row.text)

AnastasiaM
() автор топика
Ответ на: комментарий от AnastasiaM

Класс может быть на странице как в единственном экземпляре, так и может быть несколько классов с одинаковым названием на одной html-странице. Идентификатор (id) должен быть уникален на странице. Конечно, какой-нибудь «рукожоп» может и несколько ID с одним названием сделать, но это по сути ошибка и такого быть не должно.

Поэтому, если ищешь по ID, то findAll тебе не нужен, а нужен find. Соответственно и for index,row… тоже не потребуется.

Stack77
()
Последнее исправление: Stack77 (всего исправлений: 1)
Ответ на: комментарий от Stack77

Учитывая что сайт писали рукожопы и там куча тегов не закрытых и я не сильно шарю в написании скриптов на питоне (пример нашла, работает - ура! вот и все мои познания питона). Поэтому от греха подальше я лучше найду всё что есть через findAll и потом обработаю. Ну в общем два рукожопа нашли друг друга ;-)

AnastasiaM
() автор топика
Ответ на: комментарий от AnastasiaM

Ну в общем два рукожопа нашли друг друга

Надо сказать симпатичный «рукожоп» ;) Только сильно не пользуйся этой привилегией на ЛОР’е, в плане «дайте готовый код» (иначе, могут разозлиться и послать… в Job). А питон он не сложный - достаточно быстро освоишь, думаю. В общем, удачи!

Stack77
()
Последнее исправление: Stack77 (всего исправлений: 1)
Ответ на: комментарий от Stack77

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

AnastasiaM
() автор топика
Ответ на: комментарий от AnastasiaM

Ты со своим наколенным скриптом сейчас не меньший рукожоп, не удивлюсь если верстка специально написано околовалидной чтобы затруднить подобный парсинг со страницы, а если еще и динамически подгружаемым сделать контент со сменой определенных тегов и ид рандомным мусором, ух как весело жить станет. Опять же, если по роду деятельности тебе приходится парсить html это уже набат в колокол, а не звоночек. Это может значит только то, что ты пытаешься выдрать информацию из страницы на которую никто не удосужился сделать нормального api, а это значит, что ею никто не собирался делиться. Ну и все эти говнопарсеры html будут каждый раз ломаться при малейших правках верстки, сизифов труд по дизайну. Такие дела Анастасий :3

Сайт знакомств «Владимир»

anonymous
()
Ответ на: комментарий от AnastasiaM

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

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

Stack77
()
Ответ на: комментарий от anonymous

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

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

AnastasiaM
() автор топика
$ echo '<td id="TableInfo12345" class="col123">Текст что нужно получить из таблицы</td>' | lynx --dump --stdin
    Текст что нужно получить из таблицы
pinus_nigra
()
Ответ на: комментарий от anonymous

Критикуешь - предлагай! Если сайт в открытом доступе и ТС не нарушает ничьих авторских прав, то какие проблемы? (Это насчет законности).

Далее… Бывают случаи, когда нужно автоматизировать процесс, но разработчики сервиса/сайта из-за одного тебя заморачиваться не хотят (делать отдельный API). И если тебе требуется парсить не 100500 сайтов, а скажем - дюжину, то никакого труда раз в полгода уделить 5 минут чтобы указать другие селекторы выборки не составляет (если верстка поменялась или т.п.).

Я бы еще добавил в скрипт обработку ошибок и отправку сообщений в телегу об оных (если скрипт предполагает запуск в автоматическом режиме).

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

Это нужно быть полным психом, чтобы специально не закрывать теги или писать их настолько криво, что даже tidy не справляется. Это хорошо, если современный браузер смог корректно отобразить DOM (и то не факт на самом деле, чисто мое предположение).

Адекватные люди просто настраивать iptables (fail2ban), etc. Например, ограничивают множество одновременных запросов с одного IP. Просто, с вероятность в 99,999% сайт писала криворукая макака.

Stack77
()
Последнее исправление: Stack77 (всего исправлений: 2)
Ответ на: комментарий от Stack77

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

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

так написано очень много сайтов и они работают годами. Есть ли в этом какой-то скрытый смысл или просто выживали как могли - вопрос оспариваемый.

anonymous
()
Ответ на: комментарий от anonymous

Да какое там противодействие, если сама ТС говорит, что у них этот парсер на баше еще при динозаврах писали и он с тех пор работает.

Сам не первый раз с такой задачей сталкиваюсь. И не первый раз слышу: мы отдельное API писать не будем (по разным причинам) - копируете данные сами. Из недавнего (ну, как недавнего, года пол назад) делал многопоточный парсер на Qt5 (все легально, сами добро дали). Протестировал, все норм. Запустил данные качать и ушел. Только через несколько часов заметил, что «уронил» сайт (причем сайт немаленький), и никакой-то фаервол сработал, а сайт тупо заDDoSил. Ни один админ не пошевелился. И такого не просто много, а дофига. Какая там борьба, тем более, если сайт парсить раз в час, например. Этого в жизни никто не заметит в 99,(9)% случаев.

Есть ли в этом какой-то скрытый смысл или просто выживали как могли - вопрос оспариваемый.

Я не сталкивался, чтобы специально писали невалидный жуткий html в целях защиты. Это какая-то глупость, из-за которой сайт будет неправильно работать, потеряет выдачу в ПС и соответственно потеряет часть аудитории. Зато видел массу сайтов с такими проблемами написанных «рукожопами».

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

Stack77
()

вторая страница, а никто ссылку на тот эпичный пост стаковерфлова не запостил, ЛОР не торт

Harald ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.