Overall, the code is readable and Pythonic. Some minor improvements:
you can switch from stringIO
to cStringIO
to gain some speed improvements:
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
I would define template
as a high-level proper constant outside of the function:
TEMPLATE = u"""
<h2 class='title'>{title}</h2>
<a class='link' href='{link}'>{title}</a>
<span class='description'>{description}</span>
"""
when you loop over the entries
, you don't have to define separate title
, link
and description
variables - you can unpack the entry
into the entry2html
arguments:
for entry in parsefeed(kwargs['url']).entries:
print >> out, entry2html(**entry)
similarly, you can pass the kwargs
directly to the .format()
call:
return template.format(**kwargs).encode('utf-8')
organize imports according to PEP8 guide
- define docstrings according to the PEP8 guide - triple quotes, starts with a capital letter, ends with a dot
dic
is probably not the best variable name
Here is the modified code with the above and other improvements applied:
# -*- coding: utf-8 -*-
"""Simple RSS to HTML converter."""
__version__ = "0.0.1"
__author__ = "Ricky L Wilson"
from unicodedata import normalize
from bs4 import BeautifulSoup
from feedparser import parse as parse_feed
TEMPLATE = u"""
<h2 class='title'>{title}</h2>
<a class='link' href='{link}'>{title}</a>
<span class='description'>{summary}</span>
"""
def flatten_unicode_keys(entry_properties):
"""Ensures passing unicode keywords to **kwargs."""
for key in entry_properties:
if isinstance(key, unicode):
value = entry_properties[key]
del entry_properties[key]
entry_properties[normalize('NFKD', key).encode('ascii', 'ignore')] = value
def entry_to_html(**kwargs):
"""Formats feedparser entry."""
flatten_unicode_keys(kwargs)
return TEMPLATE.format(**kwargs).encode('utf-8')
def convert_feed(url):
"""Main loop."""
html_fragments = [entry_to_html(**entry) for entry in parse_feed(url).entries]
return BeautifulSoup("\n".join(html_fragments), 'lxml').prettify()
def save_file(url, filename):
"""Saves data to disc."""
with open(filename, 'w') as file_object:
file_object.write(convert_feed(url).encode('utf-8'))
if __name__ == '__main__':
save_file('http://stackoverflow.com/feeds', 'index.html')
Note that I had to replace {description}
placeholder with {summary}
to test that it is working.
Note that I've avoided using an output buffer altogether - just collected feed HTML fragments into a list and then joined.
Overall, there are some other things I would also look into - for instance, it really looks like you can make use of a template engine like mako
or jinja2
- define a template with a loop over the feeds and then render it. Example using mako
:
from feedparser import parse as parse_feed
from mako.template import Template
def convert_feed(url, filename):
"""Convert feed to an HTML."""
with open(filename, 'w') as file_object:
feeds = parse_feed(url).entries
html_content = Template(filename='template.html', output_encoding='utf-8').render(feeds=feeds)
file_object.write(html_content)
if __name__ == '__main__':
convert_feed('http://stackoverflow.com/feeds', 'index.html')
where template.html
contains:
<html>
<body>
% for feed in feeds:
<div>
<h2 class='title'>${feed.title}</h2>
<a class='link' href='${feed.link}'>${feed.title}</a>
<span class='description'>${feed.summary}</span>
</div>
% endfor
</body>
</html>