views:

357

answers:

4

I'm using pyparsing to parse HTML. I'm grabbing all embed tags, but in some cases there's an a tag directly following that I also want to grab if it's available.

example:

import pyparsing
target = pyparsing.makeHTMLTags("embed")[0]
target.setParseAction(pyparsing.withAttribute(src=pyparsing.withAttribute.ANY_VALUE))
target.ignore(pyparsing.htmlComment)

result = target.searchString(""".....
   <object....><embed>.....</embed></object><br /><a href="blah">blah</a>
   """)

I haven't been able to find any character offset in the result objects, otherwise I could just grab a slice of the original input string and work from there.

EDIT:

Someone asked why I don't use BeautifulSoup. That's a good question, let me show you why I chose not to use it with a code sample:

import BeautifulSoup
import urllib
import re
import socket

socket.setdefaulttimeout(3)

# get some random blogs
xml = urllib.urlopen('http://rpc.weblogs.com/shortChanges.xml').read()

success, failure = 0.0, 0.0

for url in re.compile(r'\burl="([^"]+)"').findall(xml)[:30]:
    print url
    try:
        BeautifulSoup.BeautifulSoup(urllib.urlopen(url).read())
    except IOError:
        pass
    except Exception, e:
        print e
        failure += 1
    else:
        success += 1


print failure / (failure + success)

When I try this, BeautifulSoup fails with parse errors 20-30% of the time. These aren't rare edge cases. pyparsing is slow and cumbersome but it hasn't blown up no matter what I throw at it. If I can be enlightened as to a better way to use BeautifulSoup then I would be really interested in knowing that.

A: 

Why would you write your own HTML parser? The standard library includes HTMLParser, and BeautifulSoup can handle any job HTMLParser can't.

Ned Batchelder
http://pyparsing.wikispaces.com/
ʞɔıu
I know what pyparsing is, I just wonder why you would use it for the messy job of parsing HTML when existing specialized modules already exist.
Ned Batchelder
+1 for BeautifulSoup
John Keyes
I updated the question with the reason why I don't use BeautifulSoup. Short answer: because BeautifulSoup gets lots of parse errors, but I don't have the same problem with pyparsing. If there's a better way to use BeautifulSoup that I don't know about or there's something else I'm missing I would be really interested in learning about that, however.
ʞɔıu
+3  A: 

If there is an optional <a> tag that would be interesting if it follows an <embed> tag, then add it to your search pattern:

embedTag = pyparsing.makeHTMLTags("embed")[0]
aTag = pyparsing.makeHTMLTags("a")[0]
target = embedTag + pyparsing.Optional(aTag)
result = target.searchString(""".....   
    <object....><embed>.....</embed></object><br /><a href="blah">blah</a>
    """)

print result.dump()

If you want to capture the character location of an expression within your parser, insert one of these, with a results name:

loc = pyparsing.Empty().setParseAction(lambda s,locn,toks: locn)
target = loc("beforeEmbed") + embedTag + loc("afterEmbed") + 
                                                 pyparsing.Optional(aTag)
Paul McGuire
The loc thing worked but I couldn't seem to get the Optional thing to work. Are you sure that code sample works?
ʞɔıu
Well *that* example doesn't work, because the `<a>` tag *doesn't* immediately follow the `<embed>` tag. I didn't follow what you meant by *follow*. What do you mean by *follow*?
Paul McGuire
In the example, the embed tag is followed by some stuff, shown by ellipses, a close-embed tag, a close-object tag, an empty BR tag, and *then* the A tag.
Paul McGuire
could I do something like embedTag + skipTo(endEmbedTag) + Optional(endObjectTag + brTag + aTag) ?
ʞɔıu
`embedTag + pyparsing.SkipTo(endEmbedTag, include=True) + pyparsing.Optional(endObjectTag + brTag + aTag)` should work for *this specific case*. But I would not be surprised if your HTML had other tags in there in unpredictable places. If you want to match an `<embed>` tag that is followed by an `<a>` tag, this might be a little more robust: `embedTag + pyparsing.SkipTo(aTag, failOn=embedTag) + aTag | embedTag`. In this case, SkipTo advances directly to the next aTag, but fails if there is another embedTag found first. But I'm in Pure Speculation Land here, so you have to fill in the rest.
Paul McGuire
A: 

you don't prefer using normal regex? or because its bad habit to parse html? :D

re.findall("<object.*?</object>(?:<br /><a.*?</a>)?",a)
S.Mark
everyone on SO now knows that parsing HTML with regex is a crime against Man; cite: http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454
ʞɔıu
:D I see, thats my first impression within these 2 days when I joined too.
S.Mark
I'm actually not opposed to using regex per se, but I've used that approach in the past and I'm trying to learn a better way. I could do that but I would still need/want a parser to parse out the HTML attributes, etc, and I may end up using a hybrid approach using a little of both.
ʞɔıu
A: 

I was able to run your BeautifulSoup code and received no errors. I'm running BeautifulSoup 3.0.7a

Please use BeautifulSoup 3.0.7a; 3.1.0.1 has bugs that prevent it from working at all in some cases (such as yours).

gibson
Would have added this as a comment on the first question but I don't have enough rep.
gibson