views:

763

answers:

3
+4  Q: 

Python parsing

I'm trying to parse the title tag in an RSS 2.0 feed into three different variables for each entry in that feed. Using ElementTree I've already parsed the RSS so that I can print each title [minus the trailing )] with the code below:

feed = getfeed("http://www.tourfilter.com/dallas/rss/by_concert_date")

for item in feed:  
 print repr(item.title[0:-1])

I include that because, as you can see, the item.title is a repr() data type, which I don't know much about.

A particular repr(item.title[0:-1]) printed in the interactive window looks like this:

'randy travis (Billy Bobs 3/21'
'Michael Schenker Group (House of Blues Dallas 3/26'

The user selects a band and I hope to, after parsing each item.title into 3 variables (one each for band, venue, and date... or possibly an array or I don't know...) select only those related to the band selected. Then they are sent to Google for geocoding, but that's another story.

I've seen some examples of regex and I'm reading about them, but it seems very complicated. Is it? I thought maybe someone here would have some insight as to exactly how to do this in an intelligent way. Should I use the re module? Does it matter that the output is currently is repr()s? Is there a better way? I was thinking I'd use a loop like (and this is my pseudoPython, just kind of notes I'm writing):


     list = bandRaw,venue,date,latLong  
     for item in feed:  
      parse item.title for bandRaw, venue, date  
       if bandRaw == str(band)   
        send venue name + ", Dallas, TX" to google for geocoding  
        return lat,long  
      list = list + return character + bandRaw + "," + venue + "," + date + "," + lat + "," + long  
     else  

In the end, I need to have the chosen entries in a .csv (comma-delimited) file looking like this:

band,venue,date,lat,long  
randy travis,Billy Bobs,3/21,1234.5678,1234.5678  
Michael Schenker Group,House of Blues Dallas,3/26,4321.8765,4321.8765

I hope this isn't too much to ask. I'll be looking into it on my own, just thought I should post here to make sure it got answered.

So, the question is, how do I best parse each repr(item.title[0:-1]) in the feed into the 3 separate values that I can then concatenate into a .csv file?

+14  A: 

Don't let regex scare you off... it's well worth learning.

Given the examples above, you might try putting the trailing parenthesis back in, and then using this pattern:

import re
pat = re.compile('([\w\s]+)\(([\w\s]+)(\d+/\d+)\)')
info = pat.match(s)
print info.groups()

('Michael Schenker Group ', 'House of Blues Dallas ', '3/26')

To get at each group individual, just call them on the info object:

print info.group(1) # or info.groups()[0]

print '"%s","%s","%s"' % (info.group(1), info.group(2), info.group(3))
"Michael Schenker Group","House of Blues Dallas","3/26"

The hard thing about regex in this case is making sure you know all the known possible characters in the title. If there are non-alpha chars in the 'Michael Schenker Group' part, you'll have to adjust the regex for that part to allow them.

The pattern above breaks down as follows, which is parsed left to right:

([\w\s]+) : Match any word or space characters (the plus symbol indicates that there should be one or more such characters). The parentheses mean that the match will be captured as a group. This is the "Michael Schenker Group " part. If there can be numbers and dashes here, you'll want to modify the pieces between the square brackets, which are the possible characters for the set.

\( : A literal parenthesis. The backslash escapes the parenthesis, since otherwise it counts as a regex command. This is the "(" part of the string.

([\w\s]+) : Same as the one above, but this time matches the "House of Blues Dallas " part. In parentheses so they will be captured as the second group.

(\d+/\d+) : Matches the digits 3 and 26 with a slash in the middle. In parentheses so they will be captured as the third group.

\) : Closing parenthesis for the above.

The python intro to regex is quite good, and you might want to spend an evening going over it http://docs.python.org/library/re.html#module-re. Also, check Dive Into Python, which has a friendly introduction: http://diveintopython.org/regular_expressions/index.html.

EDIT: See zacherates below, who has some nice edits. Two heads are better than one!

Jarret Hardie
Thanks for your answer! That helps a lot! I'm a little confused though... I need to identify fields individually to send to Google and concatenate. How do I call each value? Like, for example, how would I concatenate the values?
Alan
Your regex leaves trailing spaces on the band and venue names, but that's easy to fix.
Aaron Maenpaa
Yeah, I noticed that too, but figured I'd just pull the `[0:-1]` trick on the first two values in each `item.title`.
Alan
zacherates has good suggestions
Jarret Hardie
And his search for (.*) solves the character problem, so long as you don't set the regex to be greedy
Jarret Hardie
I edited the post with some info on concatenating... hope I understood your intentions correctly.
Jarret Hardie
Yes. That makes it clear. I thought that might be the method. Thanks so much! I'm always amazed how much people know when I ask a question here. Do you check "bilingual" on job applications? ;-P
Alan
With both sets of code (and various mixes of the two) I get the error "AttributeError: 'NoneType' object has no attribute 'groups'." I've tried it in many different ways. What am I doing wrong?
Alan
If the pattern doesn't match your input, the re.match() command will return None. You should probably check for None just in case the regex failed. What input is it failing on?
Jarret Hardie
It fails on print info.groups().
Alan
Sorry.. I meant, what is the title from the RSS feed that you're feeding to re.match()?
Jarret Hardie
Aha! Left that trailing ')' off... it's always something... :)
Alan
LOL... sounds like you've got a good handle on regex already if you figured that part out!
Jarret Hardie
+7  A: 

Regular expressions are a great solution to this problem:

>>> import re
>>> s  = 'Michael Schenker Group (House of Blues Dallas 3/26'
>>> re.match(r'(.*) \((.*) (\d+/\d+)', s).groups()
('Michael Schenker Group', 'House of Blues Dallas', '3/26')

As a side note, you might want to look at the Universal Feed Parser for handling the RSS parsing as feeds have a bad habit of being malformed.

Edit

In regards to your comment... The strings occasionally being wrapped in "s rather than 's has to do with the fact that you're using repr. The repr of a string is usually delimited with 's, unless that string contains one or more 's, where instead it uses "s so that the 's don't have to be escaped:

>>> "Hello there"
'Hello there'
>>> "it's not its"
"it's not its"

Notice the different quote styles.

Aaron Maenpaa
Thanks for your answer! As to your side note, I have noticed that some of the entries come out with "" at the beginning and end rather than ''. I wonder if this will be a problem. I used the RSS parser available at http://effbot.org/zone/element-rss-wrapper.htm.
Alan
A: 

Regarding the repr(item.title[0:-1]) part, not sure where you got that from but I'm pretty sure you can simply use item.title. All you're doing is removing the last char from the string and then calling repr() on it, which does nothing.

Your code should look something like this:

import geocoders # from GeoPy
us = geocoders.GeocoderDotUS()

import feedparser # from www.feedparser.org
feedurl = "http://www.tourfilter.com/dallas/rss/by_concert_date"
feed = feedparser.parse(feedurl)

lines = []
for entry in feed.entries:
    m = re.search(r'(.*) \((.*) (\d+/\d+)\)', entry.title)  
    if m:
        bandRaw, venue, date = m.groups()

        if band == bandRaw:
            place, (lat, lng) = us.geocode(venue + ", Dallas, TX")
            lines.append(",".join([band, venue, date, lat, lng]))

result = "\n".join(lines)

EDIT: replaced list with lines as the var name. list is a builtin and should not be used as a variable name. Sorry.

itsadok
:::sigh::: looks like you wrote the whole thing in less lines than I have imports... what modules are you using? especially for the get_geo and list.append? list is a __builtin__, right? get_geo? is that from GeoPy?
Alan
And the last line adds the newline? That's helpful, too. Thanks for taking the time.
Alan
Sorry if it wasn't clear, but I made up get_geo. I just used it as a placeholder for whatever function you decide to implement.
itsadok