Эмуляция лексоподобной функциональности в Perl или Python - PullRequest
4 голосов
/ 02 октября 2008

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

Один пример:

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

Я на самом деле сделал это, используя flex (не путать с Adobe), который является реализацией старого доброго lex. Лекс обеспечивает элегантный способ сделать это, выполнив «действия» на основе выражений. Можно контролировать путь Lex также читает файл (чтение на основе блоков / строк).

Проблема в том, что flex на самом деле создает код C / C ++, который фактически выполняет работу по токенизации. у меня есть сделать файл, который оборачивает все эти вещи. Мне было интересно, может ли Perl / Python каким-то образом сделать то же самое. Просто я хотел бы сделать все Мне нравится в одном языке программирования.

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

Кроме perl или python может ли это сделать любой язык (также функциональный)?

Я читал здесь о PLY и ANTLR ( Парсинг, где я могу узнать об этом ).

Но есть ли способ сделать это естественным образом в самом Python? простите за мое невежество, но используются ли эти инструменты в каких-либо популярных продуктах / услугах?

Спасибо.

Ответы [ 8 ]

8 голосов
/ 02 октября 2008

Посмотрите документацию для следующих модулей на CPAN

HTML :: TreeBuilder

HTML :: TableExtract

и

Разобрать :: RecDescent

Я использовал эти модули для обработки довольно больших и сложных веб-страниц.

7 голосов
/ 02 октября 2008

Если вы специально разбираете ссылки на веб-страницах, то модуль Perl WWW :: Mechanize очень элегантно разберется с вами. Вот пример программы, которая захватывает первую страницу переполнения стека и анализирует все ссылки, печатая их текст и соответствующие URL-адреса:

#!/usr/bin/perl
use strict;
use warnings;
use WWW::Mechanize;

my $mech = WWW::Mechanize->new;

$mech->get("http://stackoverflow.com/");

$mech->success or die "Oh no! Couldn't fetch stackoverflow.com";

foreach my $link ($mech->links) {
    print "* [",$link->text, "] points to ", $link->url, "\n";
}

В основном цикле каждый $link является WWW :: Mechanize :: Link объектом, так что вы не просто обязаны получать текст и URL.

Всего наилучшего,

Пол

5 голосов
/ 02 октября 2008

Звучит так, будто вы действительно хотите разобрать HTML, для этого я рекомендую взглянуть на любой из замечательных пакетов:

Или! Вы можете использовать парсер, например, один из следующих:

  • Pyparsing
  • DParser - анализатор GLR с хорошими привязками Python.
  • ANTLR - Рекурсивный генератор приличного синтаксического анализатора, который может генерировать код Python.

Этот пример взят из документации BeautifulSoup :

from BeautifulSoup import BeautifulSoup, SoupStrainer
import re

links = SoupStrainer('a')
[tag for tag in BeautifulSoup(doc, parseOnlyThese=links)]
# [<a href="http://www.bob.com/">success</a>, 
#  <a href="http://www.bob.com/plasma">experiments</a>, 
#  <a href="http://www.boogabooga.net/">BoogaBooga</a>]

linksToBob = SoupStrainer('a', href=re.compile('bob.com/'))
[tag for tag in BeautifulSoup(doc, parseOnlyThese=linksToBob)]
# [<a href="http://www.bob.com/">success</a>, 
#  <a href="http://www.bob.com/plasma">experiments</a>]
3 голосов
/ 02 октября 2008

Вы смотрели на PyParsing ?

С их домашней страницы:

Вот программа для разбора "Hello, World!" (или любое приветствие формы ",!"):

from pyparsing import Word, alphas
greet = Word( alphas ) + "," + Word( alphas ) + "!" # <-- grammar defined here
hello = "Hello, World!"
print hello, "->", greet.parseString( hello )

Программа выводит следующее:

Hello, World! -> ['Hello', ',', 'World', '!']
2 голосов
/ 02 октября 2008

Если ваша проблема имеет какое-либо отношение к веб-очистке, я рекомендую взглянуть на Web :: Scraper , который обеспечивает легкий выбор элементов с помощью XPath и CSS-селекторов. У меня (Немецкий) доклад на Web :: Scraper , но если вы запустите его через babelfish или просто посмотрите на примеры кода, это поможет вам получить краткий обзор синтаксиса.

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

1 голос
/ 02 октября 2008

С perlop :

Полезная идиома для лексоподобных сканеров это /\G.../gc. Вы можете объединить несколько регулярных выражений, подобных этому, чтобы обработать строка по частям, делая разные действия в зависимости от того, какое регулярное выражение соответствует. Каждое регулярное выражение пытается соответствовать где предыдущий заканчивается.

 LOOP:
    {
      print(" digits"),       redo LOOP if /\G\d+\b[,.;]?\s*/gc;
      print(" lowercase"),    redo LOOP if /\G[a-z]+\b[,.;]?\s*/gc;
      print(" UPPERCASE"),    redo LOOP if /\G[A-Z]+\b[,.;]?\s*/gc;
      print(" Capitalized"),  redo LOOP if /\G[A-Z][a-z]+\b[,.;]?\s*/gc;
      print(" MiXeD"),        redo LOOP if /\G[A-Za-z]+\b[,.;]?\s*/gc;
      print(" alphanumeric"), redo LOOP if /\G[A-Za-z0-9]+\b[,.;]?\s*/gc;
      print(" line-noise"),   redo LOOP if /\G[^A-Za-z0-9]+/gc;
      print ". That's all!\n";
    }
1 голос
/ 02 октября 2008

Также посмотрите pQuery это действительно хороший способ Perlish для такого рода вещей ....

use pQuery;

pQuery( 'http://www.perl.com' )->find( 'a' )->each( 
    sub {
        my $pQ = pQuery( $_ ); 
        say $pQ->text, ' -> ', $pQ->toHtml;
    }
);

# prints all HTML anchors on www.perl.com
# =>  link text -> anchor HTML

Однако, если ваше требование выходит за рамки HTML / Web, то здесь есть более раннее «Hello World!» пример в Parse :: RecDescent ...

use strict;
use warnings;
use Parse::RecDescent;

my $grammar = q{
    alpha : /\w+/
    sep   : /,|\s/
    end   : '!'
    greet : alpha sep alpha end { shift @item; return \@item }
};

my $parse = Parse::RecDescent->new( $grammar );
my $hello = "Hello, World!";
print "$hello -> @{ $parse->greet( $hello ) }";

# => Hello, World! -> Hello , World !

Возможно, слишком большой молоток, чтобы расколоть эту гайку; -)

0 голосов
/ 05 октября 2008

Изменение примера Бруно для включения проверки ошибок:

my $input = "...";
while (1) {
    if ($input =~ /\G(\w+)/gc) { print "word: '$1'\n"; next }
    if ($input =~ /\G(\s+)/gc) { print "whitespace: '$1'\n"; next }

    if ($input !~ /\G\z/gc)  { print "tokenizing error at character " . pos($input) . "\n" }
    print "done!\n"; last;
}

(Обратите внимание, что использование скаляра // g, к сожалению, единственное место, где вы действительно не можете избежать использования переменных $ 1 и т. Д.)

...