Как ускорить регулярные выражения в python?
по html документу прогоняется примерно 3000 регулярных выражений
вида «str(?:/([\\d.]+))» но есть и более сложные.
скорость составляет около 1 секунды.
как увеличить раз в 10 или более ?
возможен вариант с реализацией на других языках, например C++
Оценить 1 комментарий
Вариант номер 0: искать возможность НЕ прогонять по html документу 3000 регулярок. Скорее всего, есть более удачное решение задачи.
>>> import re >>> re._MAXCACHE 100 >>> re._MAXCACHE = 3000 >>> re._MAXCACHE 3000
Регулярные выражения предварительно откомпилированы?
Если нет — сделайте это.
Если да — десятикратного выигрыша при переходе на C++ не будет, и не надейтесь.
Питоновский движок re написан на C и сопоставим по быстродействию с реализациями в библиотеках других языков.
Так что:
1. Препроцессинг html — наверняка можно отсечь ненужные куски и выкусить ненужные блоки, прежде чем натравливать на него 3000 паттернов
2. Еще более глубокий препроцессинг — разбиение html на атомарные фрагменты с тем, чтобы, единожды идентифицировав фрагмент, больше по нему не елозить.
компиляция регулярных выражений происходит в любом случае (даже если вы не воспользовались re.compile а напрямую вызвали re.search например)
Используйте асинхронное выполнение кода в 1*х потоков. Прирост производительности должен быть хорошим.
Асинхронное выполнение не поможет, задача CPU bound. Несколько процессов привязанных к разным процессорам, это другое дело.
Python. Производительность регулярных выражений. Как ускорить?
Есть тулза, генерит много разного кода по образцу. Основная операция при этом — регулярки, коих не так уж и много (порядка 100 замен).
Какие есть хитрости для ускорения работы с ними?
Профилирование показало такую таблицу:
3559219 function calls (3548088 primitive calls) in 5.055 seconds Ordered by: internal time List reduced from 278 to 20 due to restriction ncalls tottime percall cumtime percall filename:lineno(function) 483505 0.873 0.000 0.974 0.000 /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/re.py:230(_compile) 231461 0.765 0.000 0.910 0.000 246491 0.416 0.000 0.416 0.000 246491 0.294 0.000 1.256 0.000 /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/re.py:143(search) 1030 0.260 0.000 2.118 0.002 /Work/mlc-tools/mlc_tools/WriterPython.py:443(convert_function_to_python) 231461 0.221 0.000 1.515 0.000 /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/re.py:148(sub) 16465 0.209 0.000 0.209 0.000 /Work/mlc-tools/mlc_tools/Parser.py:301(find_class) 268 0.178 0.001 0.196 0.001 /Work/mlc-tools/mlc_tools/WriterCpp.py:867(_find_includes_in_function_operation) 536 0.159 0.000 0.337 0.001 /Work/mlc-tools/mlc_tools/WriterCpp.py:727(_find_includes) 1772 0.112 0.000 0.115 0.000 /Work/mlc-tools/mlc_tools/Parser.py:27(find_body) 636 0.086 0.000 0.152 0.000 /Work/mlc-tools/mlc_tools/Function.py:99(parse_body) 84115 0.068 0.000 0.138 0.000 /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/re.py:284(_subx) 178072 0.065 0.000 0.065 0.000 /Work/mlc-tools/mlc_tools/Function.py:105(counter) 388165 0.060 0.000 0.060 0.000 543 0.060 0.000 0.113 0.000 /Work/mlc-tools/mlc_tools/WriterCpp.py:887(prepare_file) 5306 0.047 0.000 0.047 0.000 258565 0.046 0.000 0.046 0.000 471 0.045 0.000 0.096 0.000 /Work/mlc-tools/mlc_tools/WriterPython.py:332(prepare_file) 84115 0.041 0.000 0.070 0.000 /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/re.py:264(_compile_repl) 94273 0.040 0.000 0.040 0.000
Принцип работы — парсится текст, разбивается на участки. По каждому участку прогоняются регулярки и делают соответствующие замены.
Ускорение регулярных выражений в Python
Мне нужно быстро извлечь текст из файлов HTML. Я использую следующие регулярные выражения вместо полноценного парсера, поскольку мне нужно быть быстрым, а не точным (у меня больше терабайта текста). Профилировщик показывает, что большую часть времени в моем скрипте тратится на процедуру re.sub. Каковы хорошие способы ускорить мой процесс? Я могу реализовать некоторые части в C, но мне интересно, поможет ли это, учитывая, что время тратится внутри re.sub, что, я думаю, будет эффективно реализовано.
# Remove scripts, styles, tags, entities, and extraneous spaces: scriptRx = re.compile("", re.I) styleRx = re.compile("", re.I) tagsRx = re.compile("<[!/]?[a-zA-Z-]+[^<>]*>") entitiesRx = re.compile("&[0-9a-zA-Z]+;") spacesRx = re.compile("\s") . text = scriptRx.sub(" ", text) text = styleRx.sub(" ", text) .
6 ответов
Во-первых, используйте HTML-парсер, созданный для этого, например BeautifulSoup:
Затем вы можете идентифицировать оставшиеся конкретные медленные точки с помощью профилировщика:
И для изучения регулярных выражений я нашел очень полезным освоение регулярных выражений, независимо от языка программирования:
Из-за повторного объяснения варианта использования, то для этого запроса я бы сказал, что вышесказанное не то, что вы хотите. Моя альтернативная рекомендация: ускорение регулярных выражений в Python.
Вы обрабатываете каждый файл пять раз, поэтому первое, что вы должны сделать (как сказал Пол Санвальд), это попытаться уменьшить это число, объединив ваши регулярные выражения. Я бы также не использовал квантификаторы с неохотой, которые созданы для удобства за счет эффективности. Рассмотрим это регулярное выражение:
Каждый раз . идет потреблять другого персонажа, сначала он должен убедиться, не будет совпадать в этом месте. Это почти то же самое, что негативно смотреть на каждую позицию:
Но мы знаем, что нет смысла заглядывать в будущее, если следующий персонаж < и мы можем адаптировать регулярное выражение соответственно:
Когда я проверяю их в RegexBuddy с этой целевой строкой:
. неохотное регулярное выражение делает 173 шага, чтобы сделать матч, в то время как специализированное регулярное выражение занимает всего 28.
Объединение ваших первых трех регулярных выражений в одно дает этого зверя:
Вы могли бы хотеть убить элемент, пока вы на это (то есть, (script|style|head) ).
Я не знаю, что вы делаете с четвертым регулярным выражением для сущностей персонажей — вы тоже просто удаляете их? Я предполагаю, что пятое регулярное выражение должно выполняться отдельно, так как некоторые из пробелов, которые он очищает, генерируются более ранними шагами. Но попробуйте это с первыми тремя объединенными регулярными выражениями и посмотрите, насколько это важно. Это должно сказать вам, стоит ли идти вперед с таким подходом.
Если ваш вариант использования действительно заключается в том, чтобы разобрать несколько вещей для каждого из миллионов документов, то мой ответ выше не поможет. Я рекомендую некоторые эвристические методы, например, сделать пару регулярных выражений для них — как просто /script/ а также /style/ бросить вещи быстро, если можете. На самом деле, вам действительно нужно делать проверку конечных тегов вообще? не |etc. / так что не нужно проходить столько текста один раз для каждого регулярного выражения.
Одно, что вы можете сделать, это объединить регулярные выражения сценария / стиля, используя обратные ссылки. Вот некоторые примеры данных:
$ cat sample whatever
это будет соответствовать или сценарию или стилю. Я рекомендую «освоить регулярные выражения», это отличная книга.
Предложение об использовании HTML-парсера является хорошим, поскольку, скорее всего, оно будет быстрее, чем регулярные выражения. Но я не уверен, что BeautifulSoup — правильный инструмент для работы, поскольку он создает дерево разбора из всего файла и сохраняет все это в памяти. Для терабайта HTML вам понадобится неприличное количество оперативной памяти, чтобы сделать это;-) Я бы посоветовал вам взглянуть на HTMLParser , который написан на более низком уровне, чем BeautifulSoup, но я считаю, что это потоковый парсер, поэтому он будет загружать только часть текста за раз.
Я бы использовал простую программу с обычным разделом Python примерно так, но это проверяется только с одним примером файла стиля:
## simple filtering when not hierarchical tags inside other discarded tags start_tags=('Читайте также: Devexpress memoedit html text