Pyparsing: Non-greedy matching?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Peter Fein

    #1

    Pyparsing: Non-greedy matching?

    I'm trying to use pyparsing write a screenscraper. I've got some
    arbitrary HTML text I define as opener & closer. In between is the HTML
    data I want to extract. However, the data may contain the same
    characters as used in the closer (but not the exact same text,
    obviously). I'd like to get the *minimal* amount of data between these.

    Here's an example (whitespace may differ):

    from pyparsing import *

    test=r"""<tr class="tableTop Space"><td></td></tr>
    <tr class="tableTit leDark"><td class="tableTit leDark">Job
    Information</td></tr><tr><td><tab le width="100%" border="0"
    cellspacing="3" ><tr>
    <td width="110" valign="top"><d iv align="right">< strong>Job Title:
    </strong></div></td>
    <td class="ccDispla yCell">Big Old <B
    STYLE="backgrou nd-color:#FFEF95"> Head Honcho</B> Boss Man</td></tr>
    <tr>
    <td width="110" valign="top"><d iv align="right">< strong>Employer :
    </strong></div></td>
    <td width="200" nowrap class="ccDispla yCell"><table>< tr><td colspan="2"
    valign="top">Gl obal Megacorp</td></tr></table></td><td>
    <script>
    function escapecomp(){
    }
    """

    data=Combine(On eOrMore(Word(pr intables)), adjacent=False,
    joinString=" ")

    title_open=Lite ral(r"""<td width="110" valign="top"><d iv
    align="right">< strong>Job Title: </strong></div></td>
    <td class="ccDispla yCell">""")
    title_open.supp ress()

    title_close=Lit eral(r"""</td>""")
    title_close.sup press()

    title=title_ope n + data + title_close
    title2=title_op en + (data | title_close)
    [color=blue][color=green][color=darkred]
    >>> title.scanStrin g(test).next()[/color][/color][/color]
    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    StopIteration
    [color=blue][color=green][color=darkred]
    >>> title2.scanStri ng(test).next()[/color][/color][/color]
    ((['<td width="110" valign="top"><d iv align="right">< strong>Job Title:\n
    </strong></div></td>\n<td class="ccDispla yCell">', 'Big Old <B
    STYLE="backgrou nd-color:#FFEF95"> Head Honcho</B> Boss Man</td> </tr>
    <tr> <td width="110" valign="top"><d iv align="right">< strong>Employer :
    </strong></div></td> <td width="200" nowrap
    class="ccDispla yCell"><table>< tr><td colspan="2" valign="top">Gl obal
    Megacorp</td></tr></table></td> <td> <script> function escapecomp(){
    }'], {}), 182, 656)[color=blue][color=green][color=darkred]
    >>>[/color][/color][/color]

    I'd expected title to work, but it doesn't match at all. ;( In other
    test variants, title2 gives extra stuff at the end though not
    necessarily to the end of the string (due to unprintable characters,
    perhaps).

    I want a ParseResult more like:
    ['<td width="110" valign="top"><d iv align="right">< strong>Job Title:\n
    </strong></div></td>\n<td class="ccDispla yCell">', 'Big Old <B
    STYLE="backgrou nd-color:#FFEF95"> Head Honcho</B> Boss Man, '</td>']

    I sort of understand why title2 works as it does (the OneOrMore just
    slurps up everything), but for the life of me I can't figure out how to
    fix it. ;) Is there a way of writing something similar to RE's ".*?" ?

    --Pete

    --
    Peter Fein pfein@pobox.com 773-575-0694

    Basically, if you're not a utopianist, you're a schmuck. -J. Feldman
  • Paul McGuire

    #2
    Re: Pyparsing: Non-greedy matching?

    "Peter Fein" <pfein@pobox.co m> wrote in message
    news:mailman.86 32.1104454752.5 135.python-list@python.org ...[color=blue]
    > I'm trying to use pyparsing write a screenscraper. I've got some
    > arbitrary HTML text I define as opener & closer. In between is the HTML
    > data I want to extract. However, the data may contain the same
    > characters as used in the closer (but not the exact same text,
    > obviously). I'd like to get the *minimal* amount of data between these.
    >
    > Here's an example (whitespace may differ):
    >
    > from pyparsing import *
    >
    > test=r"""<tr class="tableTop Space"><td></td></tr>
    > <tr class="tableTit leDark"><td class="tableTit leDark">Job
    > Information</td></tr><tr><td><tab le width="100%" border="0"
    > cellspacing="3" ><tr>
    > <td width="110" valign="top"><d iv align="right">< strong>Job Title:
    > </strong></div></td>
    > <td class="ccDispla yCell">Big Old <B
    > STYLE="backgrou nd-color:#FFEF95"> Head Honcho</B> Boss Man</td></tr>
    > <tr>
    > <td width="110" valign="top"><d iv align="right">< strong>Employer :
    > </strong></div></td>
    > <td width="200" nowrap class="ccDispla yCell"><table>< tr><td colspan="2"
    > valign="top">Gl obal Megacorp</td></tr></table></td><td>
    > <script>
    > function escapecomp(){
    > }
    > """
    >
    > data=Combine(On eOrMore(Word(pr intables)), adjacent=False,
    > joinString=" ")
    >
    > title_open=Lite ral(r"""<td width="110" valign="top"><d iv
    > align="right">< strong>Job Title: </strong></div></td>
    > <td class="ccDispla yCell">""")
    > title_open.supp ress()
    >
    > title_close=Lit eral(r"""</td>""")
    > title_close.sup press()
    >
    > title=title_ope n + data + title_close
    > title2=title_op en + (data | title_close)
    >[color=green][color=darkred]
    > >>> title.scanStrin g(test).next()[/color][/color]
    > Traceback (most recent call last):
    > File "<stdin>", line 1, in ?
    > StopIteration
    >[color=green][color=darkred]
    > >>> title2.scanStri ng(test).next()[/color][/color]
    > ((['<td width="110" valign="top"><d iv align="right">< strong>Job Title:\n
    > </strong></div></td>\n<td class="ccDispla yCell">', 'Big Old <B
    > STYLE="backgrou nd-color:#FFEF95"> Head Honcho</B> Boss Man</td> </tr>
    > <tr> <td width="110" valign="top"><d iv align="right">< strong>Employer :
    > </strong></div></td> <td width="200" nowrap
    > class="ccDispla yCell"><table>< tr><td colspan="2" valign="top">Gl obal
    > Megacorp</td></tr></table></td> <td> <script> function escapecomp(){
    > }'], {}), 182, 656)[color=green][color=darkred]
    > >>>[/color][/color]
    >
    > I'd expected title to work, but it doesn't match at all. ;( In other
    > test variants, title2 gives extra stuff at the end though not
    > necessarily to the end of the string (due to unprintable characters,
    > perhaps).
    >
    > I want a ParseResult more like:
    > ['<td width="110" valign="top"><d iv align="right">< strong>Job Title:\n
    > </strong></div></td>\n<td class="ccDispla yCell">', 'Big Old <B
    > STYLE="backgrou nd-color:#FFEF95"> Head Honcho</B> Boss Man, '</td>']
    >
    > I sort of understand why title2 works as it does (the OneOrMore just
    > slurps up everything), but for the life of me I can't figure out how to
    > fix it. ;) Is there a way of writing something similar to RE's ".*?" ?
    >
    > --Pete
    >
    > --
    > Peter Fein pfein@pobox.com 773-575-0694
    >
    > Basically, if you're not a utopianist, you're a schmuck. -J. Feldman[/color]

    Peter -

    Well you are correct, OneOrMore just keeps on slurping as long as it
    continues to find matching text. Unlike RE's, it does not look ahead in the
    RE to treat the next literal as a terminating expression.

    In the examples that come with pyparsing, there is an HTML extractor
    (getNTPservers. py) that uses a CharsNotIn("<") expression for the body of an
    HTML tag. That works for the given case, but wont work for you - the body
    of your tag also includes other HTML tags, such as <B>, so a CharsNotIn
    would terminate before the complete body were extracted.

    Assuming that your <td> tag wont contain any nested <td> tag, you could
    define your data content as "everything up until I find '</td>'". For this
    you can use pyparsing's SkipTo element. I think if you define data as:
    data = SkipTo("</td>")
    then your code should start working better.

    There are a couple of other points on your sample code. Note that
    suppress() is *not* a mutator, but actually a factory method - in
    expr.suppress() , expr is not modified by suppress, but returns a Suppress
    object wrapped around an expr. So in place of:
    title_close=Lit eral(r"""</td>""")
    title_close.sup press()
    you should do
    title_close=Lit eral(r"""</td>""").suppres s()
    or
    title_close=Lit eral(r"""</td>""")
    title_close = title_close.sup press()

    -- Paul


    Comment

    • Peter Fein

      #3
      Re: Pyparsing: Non-greedy matching?

      On 12/31/04 03:00 AM CST, "Paul McGuire" <ptmcg@austin.r r._bogus_.com>
      sayeth:[color=blue]
      > Assuming that your <td> tag wont contain any nested <td> tag, you
      > could define your data content as "everything up until I find
      > '</td>'". For this you can use pyparsing's SkipTo element. I think
      > if you define data as:
      > data = SkipTo("</td>")
      > then your code should start working better.[/color]

      Hey! It does! ;) I just worked up (note to googlers- don't do this):
      goodchars=print ables.replace(" <", "")
      good_ab="<" + (~Literal(r"""/td>""") + SkipTo(">", include=True))
      good_ab.setDebu g(True)
      simple=Word(goo dchars)
      complex=Combine (simple | good_ab, adjacent=False, joinString="")
      data=Combine(On eOrMore(complex ), adjacent=False, joinString=" ")
      title4=title_op en+data

      But that would break for closers with more than one ">". Need to stop
      thinking like these are regexps. Thanks - this is a great tool. ;)

      --
      Peter Fein pfein@pobox.com 773-575-0694

      Basically, if you're not a utopianist, you're a schmuck. -J. Feldman

      Comment

      Working...