Вглубь pyparsing: парсим единицы измерения на python

Эффективная обработка файлов

Одна из функций парсера – это хранение данных как в базе данных, так и в обычных файлах, таких как CSV/Text. Если собираете большой объем данных, это не означает, что операция ввода-вывода будет в цикле. Давайте рассмотрим, как это делается.


Пробуем:

Python

try: a_list_variable = [] a_list_variable.extend(a_func_return_record()) except requests.ConnectionError as e: print(«Упс!! Ошибка подключения к интернету.») print(str(e)) except requests.Timeout as e: print(«Упс!! Время ожидания истекло.») print(str(e)) except requests.RequestException as e: print(«Упс!! Возникла непредвиденная ошибка!») print(str(e)) except KeyboardInterrupt: print(«Кто-то закрыл принудительно программу.») finally: print(«Total Records = » + str(len(property_urls))) try: # файл для хранения URL record_file = open(‘records_file.txt’, ‘a+’) record_file.write(«\n».join(property_urls)) record_file.close() except Exception as ex: print(«Возникла ошибка при сохранении данных, текст ошибки:») print(str(e))

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

try

a_list_variable=

a_list_variable.extend(a_func_return_record())

exceptrequests.ConnectionError ase

print(«Упс!! Ошибка подключения к интернету.»)

print(str(e))

exceptrequests.Timeout ase

print(«Упс!! Время ожидания истекло.»)

print(str(e))

exceptrequests.RequestException ase

print(«Упс!! Возникла непредвиденная ошибка!»)

print(str(e))

exceptKeyboardInterrupt

print(«Кто-то закрыл принудительно программу.»)

finally

print(«Total Records  = «+str(len(property_urls)))

try

# файл для хранения URL

record_file=open(‘records_file.txt’,’a+’)

record_file.write(«\n».join(property_urls))

record_file.close()

exceptExceptionasex

print(«Возникла ошибка при сохранении данных, текст ошибки:»)

print(str(e))

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

Надеюсь, эта статья была для вас полезной. Пожалуйста, Поделитесь своим опытом о том, как сделать парсер более эффективным!

Варианты разбора

  • Решать задачу в лоб, то есть анализировать посимвольно входящий поток и используя правила грамматики, строить АСД или сразу выполнять нужные нам операции над нужными нам компонентами. Из плюсов — этот вариант наиболее прост, если говорить об алгоритмике и наличии математической базы. Минусы — вероятность случайной ошибки близка к максимальной, поскольку у вас нет никаких формальных критериев того, все ли правила грамматики вы учли при построении парсера. Очень трудоёмкий. В общем случае, не слишком легко модифицируемый и не очень гибкий, особенно, если вы не имплементировали построение АСД. Даже при длительной работе парсера вы не можете быть уверены, что он работает абсолютно корректно. Из плюс-минусов. В этом варианте все зависит от прямоты ваших рук. Рассказывать об этом варианте подробно мы не будем.
  • Используем регулярные выражения! Я не буду сейчас шутить на тему количества проблем и регулярных выражений, но в целом, способ хотя и доступный, но не слишком хороший. В случае сложной грамматики работа с регулярками превратится в ад кромешный, особенно если вы попытаетесь оптимизировать правила для увеличения скорости работы. В общем, если вы выбрали этот способ, мне остается только пожелать вам удачи. Регулярные выражения не для парсинга! И пусть меня не уверяют в обратном. Они предназначены для поиска и замены. Попытка использовать их для других вещей неизбежно оборачивается потерями. С ними мы либо существенно замедляем разбор, проходя по строке много раз, либо теряем мозговые клеточки, пытаясь измыслить способ удалить гланды через задний проход. Возможно, ситуацию чуть улучшит попытка скрестить этот способ с предыдущим. Возможно, нет. В общем, плюсы почти аналогичны прошлому варианту. Только еще нужно знание регулярных выражений, причем желательно не только знать как ими пользоваться, но и иметь представление, насколько быстро работает вариант, который вы используете. Из минусов тоже примерно то же, что и в предыдущем варианте, разве что менее трудоёмко.
  • Воспользуемся кучей инструментов для парсинга BNF! Вот этот вариант уже более интересный. Во-первых, нам предлагается вариант типа lex-yacc или flex-bison, во вторых во многих языках можно найти нативные библиотеки для парсинга BNF. Ключевыми словами для поиска можно взять LL, LR, BNF. Смысл в том, что все они в какой-то форме принимают на вход вариацию BNF, а LL, LR, SLR и прочее — это конкретные алгоритмы, по которым работает парсер. Чаще всего конечному пользователю не особенно интересно, какой именно алгоритм использован, хотя они имеют определенные ограничения разбора грамматики (остановимся подробнее ниже) и могут иметь разное время работы (хотя большинство заявляют O(L), где L — длина потока символов). Из плюсов — стабильный инструментарий, внятная форма записи (БНФ), адекватные оценки времени работы и наличие записи БНФ для большинства современных языков (при желании можно найти для sql, python, json, cfg, yaml, html, csv и многих других). Из минусов — не всегда очевидный и удобный интерфейс инструментов, возможно, придется что-то написать на незнакомом вам ЯП, особенности понимания грамматики разными инструментами.
  • Воспользуемся инструментами для парсинга PEG! Это тоже интересный вариант, плюс, здесь несколько побогаче с библиотеками, хотя они, как правило, уже несколько другой эпохи (PEG предложен Брайаном Фордом в 2004, в то время как корни BNF тянутся в 1980-е), то есть заметно моложе и хуже выглажены и проживают в основном на github. Из плюсов — быстро, просто, часто — нативно. Из минусов — сильно зависите от реализации. Пессимистичная оценка для PEG по спецификации вроде бы O(exp(L)) (другое дело, для создания такой грамматики придется сильно постараться). Сильно зависите от наличия/отсутствия библиотеки. Почему-то многие создатели библиотек PEG считают достаточными операции токенизации и поиска/замены, и никакого вам AST и даже привязки функций к элементам грамматики. Но в целом, тема перспективная.

Обзор методов создания эмбедингов предложений, Часть1

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

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

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

Например две фразы ‘эпл лучше самсунг’ от ‘самсунг лучше эпл’, должны быть на противоположном конце по одному из значений вектора, но при этом совпадать по другим.

Можно привести аналогию с картинкой ниже. По шкале от кекса до собаки они находятся на разных концах, а по количеству чёрных точек и цвету объекта на одном.

Прежде чем начать…

Прежде чем мы перейдем к инструментам подумайте зачем вы будете это делать и будьте честными. Парсинг сайтов на Python может означать много чего. Не сканируйте веб-сайты чтобы их дублировать и представить чужую работу как свою. Помните об авторских правах и лицензировании, а также о том, документы, которые вы парсите могут быть защищены авторским правом. Учитывайте файлы robots.txt. А также не отправляйте сайту запросы чаще, чем реальные пользователи иначе рискуете получить проблемы с доступностью.

А теперь рассмотрим инструменты, которые можно использовать для парсинга данных с сайтов.

Конфуций и Маргарита

Из песочницы

Вступление


Соревнований по машинному обучению как и платформ, на которых они проводятся, существует немало и на любой вкус. Но не так часто темой контеста является человеческий язык и его обработка, еще реже такое соревнование связано с русским языком. Недавно я принимал участие в соревновании по машинному переводу с китайского на русский, прошедшего на платформе ML Boot Camp от Mail.ru. Не обладая большим опытом в соревновательном программировании, и проведя, благодаря карантину, все майские праздники дома, удалось занять первое место. Про это, а также про языки и подмену одной задачи другой я постараюсь рассказать в статье.

Эксперименты в песочнице

  1. Найдите все натуральные числа (возможно, окружённые буквами);
  2. Найдите все «слова», написанные капсом (то есть строго заглавными), возможно внутри настоящих слов (аааБББввв);
  3. Найдите слова, в которых есть русская буква, а когда-нибудь за ней цифра;
  4. Найдите все слова, начинающиеся с русской или латинской большой буквы ( — граница слова);
  5. Найдите слова, которые начинаются на гласную ( — граница слова);;
  6. Найдите все натуральные числа, не находящиеся внутри или на границе слова;
  7. Найдите строчки, в которых есть символ ( — это точно не конец строки!);
  8. Найдите строчки, в которых есть открывающая и когда-нибудь потом закрывающая скобки;
  9. Выделите одним махом весь кусок оглавления (в конце примера, вместе с тегами);
  10. Выделите одним махом только текстовую часть оглавления, без тегов;
  11. Найдите пустые строчки;

Почему здравый смысл важнее паттернов, а Active Record не так уж и плох

Так уж вышло, что разработчики, особенно молодые, любят паттерны, любят спорить о том, какой паттерн нужно применять здесь или там. Спорить до хрипоты: это фасад или прокси, а может даже синглтон. А если у вас не чистая, гексагональная архитектура, то некоторые разработчики готовы сжечь на костре Святой Инквизиции. При этом они забывают, что паттерны — это лишь возможные решения

У паттернов, также как и у любых принципов, есть границы применимости, и важно их понимать. Дорога в ад вымощена слепым и религиозным следованием пусть даже и авторитетным словам

А наличие во фреймворке нужных паттернов никак не гарантирует их правильного и осознанного применения.

Как со всем этим работать

Расскажу как всё это запустить на примере Windows 10. В Linux и macOS всё делается аналогично.

  • Установите Python 3. Я использую ветку 3.5. Скачать можно с официального сайта. Ветку 3.6 пока не советую, её зарелизили всего несколько дней назад.
  • Установите Grab. Лучше через pip. Это менеджер пакетов для python. Идёт с ним в комплекте.

pip install grab

Установите tqdm. Так же ставится через pip.

pip install tqdm

  • Установите git. Скачать можно с официального сайта.
  • Скачайте исходники себе на компьютер.

git clone https://github.com/gumbert/ydirect

  • Поменяйте запросы на ваши в тексте программы. Для написания и редактирования кода я использую Sublime.
  • Запустите скрипт.

python ydirect.py

Если всё сделали правильно, через некоторое время у вас будет файлик firms.csv со всеми данными.

Мнение автора и редакции может не совпадать. Хотите написать колонку для «Нетологии»? Читайте наши условия публикации.

Что такое REST API

Из песочницы

Если говорить отдаленно, то REST API нужен для создания сайта. Но ведь для этого много чего нужно (скажете Вы) — в какой же части REST API?

Frontend и Backend

Начнем по порядку. Что такое frontend и backend? У сайта есть две стороны: лицевая и внутренняя соответственно. Первая обычно отвечает за визуальное расположение объектов на странице (где какие картинки, где какой текст и где какие кнопочки). Вторая отвечает за “действия”. Обычно это нажатия на те самые кнопочки или на другие “штуки” на сайте. Например, Вы заходите на страницу вашей любимой социальной сети и для начала Вам нужно войти в аккаунт. С лицевой стороны (frontend) Вы вводите логин и пароль и нажимаете кнопку “Войти”. В это время запрос отправляется в базу данных для проверки наличия такого пользователя, и в случае успеха, Вы попадаете в социальную сеть под своим аккаунтом, а в противном случае, Вы увидите сообщение об ошибке. В данном случае, за отправку запроса в базу данных по сути и отвечает backend сторона. Обычно ее разделяют на три подчасти:

  1. Web API для приема запросов
  2. Бизнес-логика для обработки запросов
  3. Взаимодействие с базой данных

В этой статье мы поговорим в основном про API или Application Programming Interface и немного про бизнес-логику. Но для начала создадим сервер.

Парсинг данных с сайтов на Python3

1. Pyspider

Давайте начнем с Pyspider. Это веб-краулер с веб-интерфейсом, который позволяет выполнять парсинг сайтов python 3 в несколько потоков и сразу же наблюдать за процессом парсинга. Этот расширяемый инструмент с поддержкой нескольких баз данных и очередей сканирования, а также множеством дополнительных функций, начиная от приоритета и заканчивая возможностью повторной попытки открытия страницы. PySpider поддерживает как Python 2, так и 3. Вы можете настроить более быстрое сканирование запустив несколько экземпляров приложения.

Функции и методы PySpider хорошо документированы, даже есть фрагменты кода, которые мы можете использовать в своих проектах. Кроме того, есть онлайн демонстрация работы, которая поможет вам получить представление о пользовательском интерфейсе. Лицензия на ПО — Apache 2, но программа доступна на GitHub.

2. MechanicalSoup


MechanicalSoup — это библиотека краулинга, созданная на основе очень популярной и невероятно универсальной библиотеки, выполняющей парсинг html python — Beautiful Soup. Если у вас нет никаких особых требований к сканированию, но вам нужно собрать несколько полей или ввести какой-либо текст, но вы не хотите создавать собственный парсер для этой задачи — то эта библиотека может стать отличным решением.

3. Scrapy

Scrapy — это фреймворк для Python, с помощью которого вы сможете создать собственный инструмент парсинга. В дополнение к средствам парсинга и разбора, он включает в себя возможности экспорта в такие форматы, как JSON или CSV, а также отправку данных бэкенду. Также есть ряд расширений для таких задач, как обработка файлов cookie, подмена User Agent, ограничения глубины сканирования, а также API для создания своих расширений.

https://youtube.com/watch?v=UyjCOXxN9xU

Другие

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

  • Cola — это высокоуровневый распределенный инструмент для обхода и парсинга сайтов, но работает только на Python 2. Последний раз обновлялся два года назад;
  • Demiurge — поддерживает Python 2 и Python 3. Еще один неплохой вариант, хотя разрабатывается он все еще медленно;
  • Feedparser — небольшая библиотека, предназначенная для разбора RSS или Atom лент.
  • Lassie — упрощает получение базового контента страницы, такого как описание, название, ключевые слова или список изображений;
  • RoboBrowser — еще одна простая библиотека для Python 2 или 3 с базовыми функциями, поддерживается нажатие кнопок и заполнение форм. Уже давно не обновлялась, но по-прежнему отличный выбор.

Где и что парсить?

В прошлый раз мы смотрели объявления по ссылкам вида

Заголовок и текст объявления вы уже знаете как находить. Отсюда же можно взять и сайт компании. Остальная информация есть в карточке объявления. Нужно найти признаки этих ссылок:

  • Сайт компании находится внутри тега span с классом domain.
  • Ссылка на карточку имеет класс vcard.

Ссылка на карточку имеет класс vcard

Сайт компании находится внутри тега span с классом domain

На карточке объявления также ищем признаки нужных элементов. Нас будут интересовать:

  • Название компании. Находится в заголовке с тегом h1.
  • Телефон находится внутри элементов с классами contact-item call-button-container → large-text.
  • Почта имеет класс email.

Соберу всё в кучу:

1. Пройдитесь по всем ссылкам вида https://direct.yandex.ru/search?&rid=213&text=запрос&page=номер_страницы. 2. С этих страниц запишите в файл заголовок и текст объявления, а также домен сайта. 3. Найдите ссылку на карточку объявления, если она есть. 4. Соберите название компании, почту и номер телефона с карточки и запишите в файл.

Суммаризация текста: подходы, алгоритмы, рекомендации и перспективы

Ежедневно каждый из нас сталкивается с огромным информационным потоком. Нам часто необходимо изучить множество объемных текстов (статей, документов) в ограниченное время. Поэтому в области машинного обучения естественным образом родилась задача автоматического составления аннотации текста. У нас в компании мы активно работаем над автореферированием документов, в эту статью не стал включать все подробности и код, но описал основные подходы и результаты на примере нейтрального датасета: 30 000 футбольных спортивных новостных статей, собранных с информационного портала «Спорт-Экспресс».

Процесс парсинга

Чтобы понять, как развивался Хабр, нужно было обойти по все его статьи и выделить из них метаинформацию (например, даты). Обход дался легко, потому что ссылки на все статьи имеют вид «habrahabr.ru/post/337722/», причём номера задаются строго по порядку. Зная, что последний пост имеет номер чуть меньше 350 тысяч, я просто прошёлся по всем возможным id документов циклом (код на Python):

Функция пытается загружает страницу с соответствующим id и пытается вытащить из структуры html содержательную информацию.

В процессе парсинга открыл для себя несколько новых моментов.

Во-первых, говорят, что создавать больше процессов, чем ядер в процессоре, бесполезно. Но в моём случае оказалось, что лимитирующий ресурс — не процессор, а сеть, и 100 процессов отрабатывают быстрее, чем 4 или, скажем, 20.


Во-вторых, в некоторых постах встречались сочетания спецсимволов — например, эвфемизмы типа «%&#@». Оказалось, что , который я использовал сначала, реагирует на комбинацию болезненно, считая её началом html-сущности. Я уж было собирался творить чёрную магию, но на форуме подсказали, что можно просто поменять парсер.

«Живых» статей оказалась только половина от потенциального максимума — 166307 штук. Про остальные Хабр даёт варианты «страница устарела, была удалена или не существовала вовсе». Что ж, всякое бывает.

За выгрузкой статей последовала техническая работа: например, даты публикации нужно было перевести из формата «’21 декабря 2006 в 10:47» в стандартный , а «12,8k» просмотров — в 12800. На этом этапе вылезло ещё несколько казусов. Самый весёлый связан с подсчётом голосов и типами данных: в некоторых старых постах произошло переполнение инта, и они получили по 65535 голосов.

В результате тексты статей (без картинок) заняли у меня 1.5 гигабайта, комментарии с метаинформацией — ещё 3, и около сотни мегабайт — метаинформация о статьях. Такое можно полностью держать в оперативной памяти, что было для меня приятной неожиданностью.

Начал анализ статей я не с самих текстов, а с метаинформации: дат, тегов, хабов, просмотров и «лайков». Оказалось, что и она может многое поведать.

Scrapy vs. Beautiful Soup

В этом разделе будет дан обзор одного из самых популярных инструментов для парсинга, BeautifulSoup, и проведено его сравнение со Scrapy.

Scrapy — это Python-фреймворк, предлагающий полноценный набор инструментов и позволяющий разработчикам не думать о настройке кода.

BeautifulSoup также широко используется для веб-скрапинга. Это пакет Python для парсинга документов в форматах HTML и XML и извлечения данных из них. Он доступен для Python 2.6+ и Python 3.

Вот основные отличия между ними:

Scrapy BeautifulSoup
Функциональность
Scrapy — это самый полный набор инструментов для загрузки веб-страниц, их обработки и сохранения в файлы и базы данных BeautifulSoup — это в принципе просто парсер HTML и XML, требующий дополнительных библиотек, таких как requests и urlib2 для открытия ссылок и сохранения результатов.
Кривая обучения
Scrapy — это движущая сила веб-сканирования, предлагающая массу способов парсинга страниц. Обучение тому, как он работает, требует много времени, но после освоения процесс сканирования превращается в одну строку кода. Потребуется время, чтобы стать экспертом в Scrapy и изучить все его особенности BeautifulSoup относительно прост для понимания новичкам в программировании и позволяет решать маленькие задачи за короткий срок.
Скорость и нагрузка
Scrapy с легкостью выполняет крупную по объему работу. Он может сканировать несколько ссылок одновременно менее чем за минуту в зависимости от общего количества. Это происходит плавно благодаря Twister, который работает асинхронно (без блокировки) BeautifulSoup используется для простого и эффективного парсинга. Он работает медленнее Scrapy, если не использовать .
Расширяемость
Scrapy предоставляет функциональность , с помощью которой можно писать функции для веб-сканера. Они будут включать инструкции о том, как робот должен проверять, удалять и сохранять данные в базу данных. используются для проверки парсеров, благодаря чему можно создавать как базовые, так и глубокие парсеры. Он же позволяет настраивать множество переменных: повторные попытки, перенаправление и т. д. Если проект не предполагает большого количества логики, BeautifulSoup отлично для этого подходит, но если нужна настраиваемость, например прокси, управление куки и распределение данных, то Scrapy справляется лучше.

«Петя любит Дашу».replace(/Дашу|Машу|Сашу/, «Катю») ¶

Не трудно догадаться, что результатом работы js-выражения выше будет текст . Даже, если Петя неровно дышит к Маше или Саше, то результат всё равно не изменится.

Рассмотрим базовые спец. символы, которые можно использовать в шаблонах:

Символ Описание Пример использования Результат
\ Символ экранирования или начала мета-символа /путь\/к\/папке/ Надёт текст
^ Признак начала строки /^Дом/ Найдёт все строки, которые начинаются на
$ Признак конца строки /родной$/ Найдёт все строки, которые заканчиваются на
. Точка означает любой символ, кроме перевода строки /Петя ..бит Машу/ Найдёт как , так и
| Означает ИЛИ /Вася|Петя/ Найдёт как Васю, так и Петю
? Означает НОЛЬ или ОДИН раз /Вжу?х/ Найдёт и
* Означает НОЛЬ или МНОГО раз /Вжу*х/ Найдёт , , , и т.д.
+ Означает ОДИН или МНОГО раз /Вжу+х/ Найдёт , , и т.д.

Помимо базовых спец. символов есть мета-символы (или мета-последовательности), которые заменяют группы символов:

Символ Описание Пример использования Результат
\w Буква, цифра или _ (подчёркивание) /^\w+$/ Соответствует целому слову без пробелов, например
\W НЕ буква, цифра или _ (подчёркивание) /\W\w+\W/ Найдёт полное слово, которое обрамлено любыми символами, например
\d Любая цифра /^\d+$/ Соответствует целому числу без знака, например
\D Любой символ НЕ цифра /^\D+$/ Соответствует любому выражению, где нет цифр, например
\s Пробел или табуляция (кроме перевода строки) /\s+/ Найдёт последовательность пробелов от одного и до бесконечности
\S Любой символ, кроме пробела или табуляции /\s+\S/ Найдёт последовательность пробелов, после которой есть хотя бы один другой символ
\b Граница слова /\bдом\b/ Найдёт только отдельные слова , но проигнорирует
\B НЕ граница слова /\Bдом\b/ Найдёт только окночние слов, которые заканчиваются на
\R Любой перевод строки (Unix, Mac, Windows) /.*\R/ Найдёт строки, которые заканчиваются переводом строки

Нужно отметить, что спец. символы \w, \W, \b и \B не работают по умолчанию с юникодом (включая кириллицу). Для их правильной работы нужно указывать модификатор . К сожалению, на окончание 2019 года JavaScript не поддерживает регулярные выражения для юникода даже с модификатором, поэтому в js эти мета-символы работают только для латиницы.

Ещё регулярные выражения поддерживают разные виды скобочек:

Выражение Описание Пример использования Результат
(…) Круглые скобки означают под-шаблон, который идёт в результат поиска /(Петя|Вася|Саша) любит Машу/ Найдёт всю строку и запишет воздыхателя Маши в результат поиска под номером 1
(?:…) Круглые скобки с вопросом и двоеточием означают под-шаблон, который НЕ идёт в результат поиска /(?:Петя|Вася|Саша) любит Машу/ Найдёт только полную строку, воздыхатель останется инкогнито
(?P<name>…) Задаёт имя под-шаблона /(?P<воздыхатель>Петя|Вася|Саша) любит Машу/ Найдёт полную строку, а воздыхателя запишет в результат под индексом 1 и ‘воздыхатель’
Квадратные скобки задают ЛЮБОЙ СИМВОЛ из последовательности (включая спец. символы \w, \d, \s и т.д.) /^+$/ Соответствует любому выражению , но не
Если внутри квадратных скобок указать минус, то это считается диапазоном /+/ Аналог /\w/ui для JavaScript
Если минус является первым или последним символом диапазона, то это просто минус /+/ Найдёт любое целое числое с плюсом или минусом (причём не обязательно, чтобы минус или плюс были спереди)
Квадратные скобки с «крышечекой» означают любой символ НЕ входящий в диапазон //i Найдёт любой символ, который не является буквой, числом или пробелом
] Квадратные скобки в квадратных скобках задают класс символов (alnum, alpha, ascii, digit, print, space, punct и другие) /]+/ Найдёт последовательность непечатаемых символов
{n} Фигурные скобки с одним числом задают точное количество символов /\w+н{2}\w+/u Найдёт слово, в котором две буквы н
{n,k} Фигурные скобки с двумя числами задают количество символов от n до k /\w+н{1,2}\w+/u Найдёт слово, в котором есть одна или две буквы н
{n,} Фигурные скобки с одним числом и запятой задают количество символов от n до бесконечности /\w+н{3,}\w+/u Найдёт слово, в котором н встречается от трёх и более раз подряд

Общие выводы

  • Не так страшен чёрт, как его малюют. Создание парсера с помощью инструмента, дело, в общем, посильное. Достаточно изучить общие принципы и потратить полдня на изучение конкретного инструмента, после чего в дальнейшем все уже будет намного проще. А вот велосипеды изобретать — не надо. Особенно, если вам не особенно важна скорость парсинга и оптимизации.
  • Грамматики имеют собственную ценность. Имея перед глазами грамматику, гораздо проще оценить, будут ли при использовании составленного по ней парсера возникать ошибки.
  • Инструмент можно найти всегда. Возможно, не на самом привычном языке, но почти на всех они есть. Если не повезло, и его все-таки нет, можно взять что-нибудь легко используемое (что-то на js, python, lua или ruby — тут уж кому что больше нравится). Да, получится “почти stand-alone в рамках проекта”, но в большинстве случаев этого достаточно.
  • Все инструменты (немного) различаются. Иногда это “:” вместо “=” в BNF, иногда различия более обширны. Не надо этого пугаться. В крайнем случае, переделка грамматики под другой инструмент займет у вас минут 20. Так что если есть возможность достать где-то грамматику, а не писать её самому, лучше это сделать. Но перед использованием все равно лучше её проверьте. Все мы люди, всем нам свойственно ошибаться…
  • При прочих равных, лучше используйте более “разговорчивый” инструмент. Это поможет избежать ошибок составления грамматики и оценить, что и как будет происходить.
  • Если для вас в первую очередь важна скорость разбора, боюсь, вам придется либо пользоваться инструментом для C (например, Bison), либо решать проблему “в лоб”. Так же, следует задуматься о том, нужен ли вам именно парсинг (об этом стоит задуматься в любом случае, но в случае скоростных ограничений — особенно). В частности, для многих задач подходит токенизация — разбиение строки на подстроки с использованием заданного разделителя или их набора. Возможно, это ваш случай.

С этим читают