LINUX.ORG.RU

re.DOTALL и нежадные регулярные выражения

 ,


0

2

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

Поэтому вопрос: чем они отличаются от жадных, и почему не работает re.DOTALL?

Собственно задача. Есть много однотипных файлов, скачанных с перегруженного скриптами сайта (webnovel.com, если интересно). Требуется удалить все теги <script ... </script>. Однострочник ''.join(re.split('<script.*?</script>', filestring)) удаляет однострочные скрипты, но не удаляет многострочные. ''.join(re.split('<script.*?</script>', filestring, re.DOTALL)) вдобавок не удаляет половину однострочных. Если предварительно прогнать filestring = filestring.replace('\n', '<abzats>'), удалить скрипты и вернуть filestring = filestring.replace('<abzats>', '\n'), получается желаемый результат. Почему не работает с DOTALL?

Ответ: Следует вызывать эту функцию как re.split('<script.*?</script>', filestring, flags=re.DOTALL)
без flags re.DOTALL воспринимается как параметр для maxsplit, поэтому часть найденных результатов игнорируется. Так как у меня было 32 результата, игнорировалась ровно половина.
То же относится к re.sub и многим другим.

Если охота побухтеть «HTML невозможно парсить регекспами, существуют специализированные библиотеки», назовите специализированную питоновскую библиотеку для совершения этой операции в 1 строку.

★★★★★

Последнее исправление: question4 (всего исправлений: 3)

re.split(‘<script.*?’, filestring, re.DOTALL)

>>> import re
>>> help(re.split)
split(pattern, string, maxsplit=0, flags=0)
    ...
>>> re.split('<s.*?</s>', 'test is<s>\nnot\n</s>ok', flags=re.DOTALL)
['test is', 'ok']
anonymous
()

парсить теги регулярками - идиотизм. ими пользуются когда тупо лень, тупо на конкретном сайте. это НЕ УНИВЕРСАЛЬНОЕ решение, потому как HTML5 позволяет не использовать закрывающий тег, теги регистронезависимы, у закрывающего тега могут быть атрибуты, а атрибуты у тегов могут быть в кавычках, без кавычек…

для парсинга обычного сайта не нужны никакие регулярки хватит и сплитов:

>>> r=requests.get('https://www.linux.org.ru/forum/general/17454838?lastmod=17454857')
>>> s=r.text.split('<script')[1]
>>> s[s.find('>')+1:s.rfind('</script')]
'\n  /*!\n  * $script.js Async loader & dependency manager\n  * https://github.com/ded/script.js\n  * (c) Dustin Diaz, Jacob Thornton 2011\n  * License: MIT\n  */\n(function(e,d,f){typeof f.module!="undefined"&&f.module.exports?f.module.exports=d():typeof f.define!="undefined"&&f.define=="function"&&f.define.amd?define(e,d):f[e]=d()})("$script",function(){function y(f,e){for(var h=0,g=f.length;h<g;++h){if(!e(f[h])){return E}}return 1}function x(d,c){y(d,function(b){return !c(b)})}function w(p,k,h){function c(b){return b.call?b():K[b]}function q(){if(!--d){K[e]=1,f&&f();for(var b in I){y(b.split("|"),c)&&!x(I[b],c)&&(I[b]=[])}}}p=p[D]?p:[p];var g=k&&k.call,f=g?k:h,e=g?p.join(""):k,d=p.length;return setTimeout(function(){x(p,function(b){if(G[b]){return e&&(J[e]=1),G[b]==2&&q()}G[b]=1,e&&(J[e]=1),v(!L.test(b)&&H?H+b+".js":b,q)})},0),w}function v(h,g){var b=N.createElement("script"),a=E;b.onload=b.onerror=b[z]=function(){if(b[B]&&!/^c|loade/.test(b[B])||a){return}b.onload=b[z]=null,a=1,G[h]=2,g()},b.async=1,b.src=h,M.insertBefore(b,M.firstChild)}var N=document,M=N.getElementsByTagName("head")[0],L=/^https?:\\/\\//,K={},J={},I={},H,G={},F="string",E=!1,D="push",C="DOMContentLoaded",B="readyState",A="addEventListener",z="onreadystatechange";return !N[B]&&N[A]&&(N[A](C,function u(){N.removeEventListener(C,u,E),N[B]="complete"},E),N[B]="loading"),w.get=v,w.order=function(f,e,h){(function g(a){a=f.shift(),f.length?w(a,g):w(a,e,h)})()},w.path=function(b){H=b},w.ready=function(f,d,h){f=f[D]?f:[f];var g=[];return !x(f,function(b){K[b]||g[D](b)})&&y(f,function(b){return K[b]})?d():!function(b){I[b]=I[b]||[],I[b][D](d),h&&h(g)}(f.join("|")),w},w},this);'
>>>

Все это дело можно оформить функцией:

>>> def extract_text(contents: str, tag: str) -> list[str]:
...     return [x[x.find('>')+1:x.rfind('</' + tag)] for x in contents.split('<' + tag)[1:]]
... 
>>> extract_text(r.text, 'script')
...
rtxtxtrx ★★
()
Последнее исправление: rtxtxtrx (всего исправлений: 2)
Ответ на: комментарий от rtxtxtrx

HTML5 позволяет не использовать закрывающий тег

Не в случае с тегом <script>.

теги регистронезависимы

Есть re.I.

у закрывающего тега могут быть атрибуты

Не могут: https://dev.w3.org/html5/spec-LC/syntax.html#end-tags

атрибуты у тегов могут быть в кавычках, без кавычек

Не важно.

Хотя внутри скрипта могут быть строки типа "</script>". Но для конкретного случая уж лучше регулярки, чем то, что предлагаешь ты.

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

Хотя внутри скрипта могут быть строки типа «». Но для конкретного случая уж лучше регулярки, чем то, что предлагаешь ты.

и регулярки так же их распарсят неправильно. в каком-то из старых браузеров был баг из-за которого в js вместо '<script нужно было писать '<scr' + 'ipt

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

Я перепроверил:

<script>alert("</script>")</script>

Последний хромиум (а равно и хром) отображает ") вместо алерта. Но это скорее из-за того, что хтмл движок отдельно от v8

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

<script><!--

В script <![CDATA[ и ]]> можно использовать. Это тоже комментарии, но для XML. По ходу и хтмл комментарии игнорируются. Воспринимает как пустой скрипт без закрывающего. Оно малоприменимо. Ну всякие XSS не заслуживают внимания

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

Вот, ты потерял >.

Где? Если про тег <script>, то в нём есть параметры.

назовите специализированную питоновскую библиотеку для совершения этой операции в 1 строку

Вы это серьезно?

Ниже по теме почти сразу нашёлся человек, который:

  1. повторил стандартную мантру, что нельзя
  2. (причём с капсом, но без заглавных букв в начале предложений),
  3. сам же себе ответил, что можно, если для конкретного сайта и быстро,
  4. предложил «универсальное» решение, которое решает совсем другую задачу,
  5. и ему тут же напридумывали патологических примеров, когда оно не сработает.

Но мне интересно только почему не срабатывает нежадное регулярное выражение с DOTALL.

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

для парсинга обычного сайта не нужны никакие регулярки хватит и сплитов:

Данный пример возвращает скрипт. Но:
Во-первых, мне нужны были не скрипты, а статичный HTML без скриптов.
Во-вторых, эту задачу я решил, и спрашивал, почему регулярные выражения не срабатывали, если в текст вклинивались концы строк.
В-третьих, какой смысл не ставить большие буквы в начале предложений, если всё равно пользуешься запятыми и скобками и капсишь?

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

Ничего не понял. Есть две основные библиотеки для разбора html, в одной есть cleaner, вот второй есть extract, вычищают все что надо.

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

Потому что. Правильная регулярка:

<script.*?</script>

Без вопроса движок регулярок </script> будет от конца строки искать (аналог rfind ака жадный поиск). так быстрее. На заглавные мне лень переключать. За меня это делает телефон

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

Правильная регулярка: <script.*?</script>

Чем она отличается от моей в стартовом посте?

На заглавные мне лень переключать.

Хочешь сказать, что rtxtxtrx — это ты же, и ты всю тему споришь сам с собой?

Без вопроса движок регулярок будет от конца строки искать (аналог rfind ака жадный поиск).

А я спрашиваю про нежадный и DOTALL.

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

Есть две основные библиотеки для разбора html

Какие? В гугле их гораздо больше. В официальной документации — html и xml, но в них нет ни cleaner, ни extract.

И я вообще-то про DOTALL спрашивал.

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

Тебе нужен не сплит, а re.sub.

Результат идентичен с join+split. Отличия в скорости — в пределах погрешности. Использовал split по инерции, так как он же был нужен в прошлый раз.

Сплит вообще никем и никогда не используется. Он бесполезен.

Не абсолютизируй своё невежество.

я не знаю чего ты ожидаешь, и чего выходит.

Заметно :)

Я хочу, чтобы регулярное выражение ловило многострочные подстринги. Например, в строке 'раз два три четыре раз два три четыре' выражение 'раз.*три' поймает 'раз два три четыре раз два три'. 'раз.*?три' поймает два раза 'раз два три'.

Проблема была в том, что в строке 'раз два\n три четыре раз два\n три четыре' не ловилось вообще ничего. Но я уже нашёл, почему.

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

join+split

своё невежество

Проблема была в том, что в строке 'раз два\n три четыре раз два\n три четыре' не ловилось вообще ничего. Но я уже нашёл, почему.

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

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

Любой хтмл можно в маркдаун обратить

И как сделать это быстро и просто? Для определённости — HTML, сохраняемый из LibreOffice.

P.S. И зачем ты спорил сам с собой?

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

Элементарно: я не спорил сам с собой.

using Playwright

  await page.emulateMedia({ media: "print" });
  await page.goto("https://robstarbuck.uk/cv");

  await page.pdf({ path: "./cv.pdf", format: "A4" });

https://stackoverflow.com/questions/72324669/playwright-download-via-print-to-pdf

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

Если на сайте авторизация нужна, то любой браузер можно запустить в режиме зомби чтоб пароли не вводить и тп и слать ему команды, включив режим отладки:

❯ cat ~/.config/brave-flags.conf
--remote-debugging-port=9222

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

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

rtxtxtrx ★★
()
Ответ на: комментарий от question4
extra/python-html2text 2020.1.16-9 (42.7 KiB 128.1 KiB) (Installed)
    A HTML to markdown-structured text converter

https://pypi.org/project/html2text/

>>> import html2text
>>>
>>> print(html2text.html2text("<p><strong>Zed's</strong> dead baby, <em>Zed's</em> dead.</p>"))
**Zed's** dead baby, _Zed's_ dead.
rtxtxtrx ★★
()
Ответ на: комментарий от rtxtxtrx

Лучше через безмозглый браузер сформированный хтмл получать, а потом конвертировать чтобы убрать лишнее. Я думаю часть контента нельзя формируется через яваскрипт. Если же он статичный и requests хватит. Короче два варианта: как напрямую в pdf перегнать и в md.

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

https://pypi.org/project/html2text/

Добавляет пробелы, если форматированием выделена только часть слова.

Но всё равно спасибо.

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