rev |
line source |
paulo@39
|
1 import os
|
paulo@40
|
2 import sys
|
paulo@39
|
3 import re
|
paulo@40
|
4 import urllib2
|
paulo@40
|
5 import threading
|
paulo@40
|
6 import Queue
|
paulo@39
|
7
|
paulo@39
|
8 import html
|
paulo@39
|
9 import xml.etree.ElementTree
|
paulo@39
|
10
|
paulo@39
|
11
|
paulo@39
|
12 MAX_ITEMS = 30
|
paulo@39
|
13 MAX_LINK_Z = 4
|
paulo@40
|
14 MAX_THREADS = 20
|
paulo@39
|
15
|
paulo@39
|
16
|
paulo@39
|
17 _PARSE_ROOT_TAG_RE = re.compile(r"(\{(.+)\})?(.+)")
|
paulo@39
|
18
|
paulo@39
|
19 def _parse_root_tag(root_tag):
|
paulo@39
|
20 re_match = _PARSE_ROOT_TAG_RE.match(root_tag)
|
paulo@39
|
21
|
paulo@39
|
22 if re_match is None:
|
paulo@39
|
23 return (None, None)
|
paulo@39
|
24 else:
|
paulo@39
|
25 return re_match.group(2, 3)
|
paulo@39
|
26
|
paulo@39
|
27
|
paulo@39
|
28 def _go_rss(elementTree):
|
paulo@39
|
29 title = elementTree.find("channel/title").text.strip()
|
paulo@39
|
30 link = elementTree.find("channel/link").text
|
paulo@39
|
31
|
paulo@39
|
32 items = []
|
paulo@39
|
33
|
paulo@39
|
34 for i in elementTree.findall("channel/item")[:MAX_ITEMS]:
|
paulo@39
|
35 it_title = i.find("title").text.strip()
|
paulo@39
|
36 it_link = i.find("link").text
|
paulo@39
|
37
|
paulo@39
|
38 items.append((it_title, it_link))
|
paulo@39
|
39
|
paulo@39
|
40 return (title, link, items)
|
paulo@39
|
41
|
paulo@39
|
42
|
paulo@39
|
43 def _go_atom(elementTree):
|
paulo@39
|
44 ns = "http://www.w3.org/2005/Atom"
|
paulo@39
|
45
|
paulo@39
|
46 title = elementTree.find("{%s}title" % ns).text.strip()
|
paulo@39
|
47 link = ''
|
paulo@39
|
48
|
paulo@39
|
49 for i in elementTree.findall("{%s}link" % ns):
|
paulo@39
|
50 if i.get("type") == "text/html" and i.get("rel") == "alternate":
|
paulo@39
|
51 link = i.get("href")
|
paulo@39
|
52 break
|
paulo@39
|
53
|
paulo@39
|
54 items = []
|
paulo@39
|
55
|
paulo@39
|
56 for i in elementTree.findall("{%s}entry" % ns)[:MAX_ITEMS]:
|
paulo@39
|
57 it_title = i.find("{%s}title" % ns).text.strip()
|
paulo@39
|
58 it_link = ''
|
paulo@39
|
59
|
paulo@39
|
60 for j in i.findall("{%s}link" % ns):
|
paulo@39
|
61 if j.get("type") == "text/html" and j.get("rel") == "alternate":
|
paulo@39
|
62 it_link = j.get("href")
|
paulo@39
|
63 break
|
paulo@39
|
64
|
paulo@39
|
65 items.append((it_title, it_link))
|
paulo@39
|
66
|
paulo@39
|
67 return (title, link, items)
|
paulo@39
|
68
|
paulo@39
|
69
|
paulo@39
|
70 def _to_html(docstruct):
|
paulo@39
|
71 root = html.HTML()
|
paulo@39
|
72
|
paulo@39
|
73 header = root.header
|
paulo@39
|
74 header.title("myrss")
|
paulo@39
|
75 header.link(rel="stylesheet", type="text/css", href="index.css")
|
paulo@39
|
76
|
paulo@39
|
77 link_z = 0
|
paulo@39
|
78
|
paulo@39
|
79 for feed in docstruct:
|
paulo@40
|
80 if feed is None:
|
paulo@40
|
81 continue
|
paulo@40
|
82
|
paulo@39
|
83 (title, link, items) = feed
|
paulo@39
|
84
|
paulo@39
|
85 root.h1.a(title, href=link, klass="z%d" % (link_z % MAX_LINK_Z))
|
paulo@39
|
86 link_z += 1
|
paulo@39
|
87 p = root.p
|
paulo@39
|
88
|
paulo@39
|
89 for (i, (it_title, it_link)) in enumerate(items):
|
paulo@39
|
90 if i > 0:
|
paulo@39
|
91 p += " - "
|
paulo@39
|
92
|
paulo@39
|
93 p.a(it_title, href=it_link, klass="z%d" % (link_z % MAX_LINK_Z))
|
paulo@39
|
94 link_z += 1
|
paulo@39
|
95
|
paulo@39
|
96 return unicode(root).encode("utf-8")
|
paulo@39
|
97
|
paulo@39
|
98
|
paulo@40
|
99 def _process_url(url):
|
paulo@40
|
100 ret = None
|
paulo@40
|
101
|
paulo@40
|
102 try:
|
paulo@40
|
103 print >> sys.stderr, "--> processing %s" % url
|
paulo@40
|
104 feed = urllib2.urlopen(url)
|
paulo@40
|
105 except urllib2.HTTPError as e:
|
paulo@40
|
106 print >> sys.stderr, "--> (%s) %s" % (url, e)
|
paulo@40
|
107 return ret
|
paulo@40
|
108
|
paulo@40
|
109 elementTree = xml.etree.ElementTree.parse(feed)
|
paulo@40
|
110 root = elementTree.getroot()
|
paulo@40
|
111
|
paulo@40
|
112 parsed_root_tag = _parse_root_tag(root.tag)
|
paulo@40
|
113
|
paulo@40
|
114 if parsed_root_tag == (None, "rss"):
|
paulo@40
|
115 version = float(root.get("version", 0.0))
|
paulo@40
|
116 if version >= 2.0:
|
paulo@40
|
117 ret = _go_rss(elementTree)
|
paulo@40
|
118 else:
|
paulo@40
|
119 raise NotImplementedError("Unsupported rss version")
|
paulo@40
|
120 elif parsed_root_tag == ("http://www.w3.org/2005/Atom", "feed"):
|
paulo@40
|
121 ret = _go_atom(elementTree)
|
paulo@40
|
122 else:
|
paulo@40
|
123 raise NotImplementedError("Unknown root tag")
|
paulo@40
|
124
|
paulo@40
|
125 return ret
|
paulo@40
|
126
|
paulo@40
|
127
|
paulo@40
|
128 class WorkerThread(threading.Thread):
|
paulo@40
|
129 def __init__(self, *args, **kwargs):
|
paulo@40
|
130 self._input_queue = kwargs.pop("input_queue")
|
paulo@40
|
131 self._output_queue = kwargs.pop("output_queue")
|
paulo@40
|
132 threading.Thread.__init__(self, *args, **kwargs)
|
paulo@40
|
133 self.daemon = True
|
paulo@40
|
134
|
paulo@40
|
135 def run(self):
|
paulo@40
|
136 while True:
|
paulo@40
|
137 (idx, url) = self._input_queue.get()
|
paulo@40
|
138 docfeed = None
|
paulo@40
|
139 try:
|
paulo@40
|
140 docfeed = _process_url(url)
|
paulo@40
|
141 except Exception as e:
|
paulo@40
|
142 print >> sys.stderr, "--> (%s) exception: %s" % (url, e)
|
paulo@40
|
143 self._output_queue.put((idx, docfeed))
|
paulo@40
|
144 self._input_queue.task_done()
|
paulo@40
|
145
|
paulo@40
|
146
|
paulo@39
|
147 if __name__ == "__main__":
|
paulo@40
|
148 with open("FEEDS") as feeds_file:
|
paulo@40
|
149 feedlines = feeds_file.readlines()
|
paulo@39
|
150
|
paulo@40
|
151 docstruct = [None]*len(feedlines)
|
paulo@40
|
152 iq = Queue.Queue(feedlines)
|
paulo@40
|
153 oq = Queue.Queue(feedlines)
|
paulo@39
|
154
|
paulo@40
|
155 for _ in range(MAX_THREADS):
|
paulo@40
|
156 WorkerThread(input_queue=iq, output_queue=oq).start()
|
paulo@39
|
157
|
paulo@40
|
158 for (i, l) in enumerate(feedlines):
|
paulo@40
|
159 if l[0] != '#':
|
paulo@40
|
160 l = l.strip()
|
paulo@40
|
161 iq.put((i, l))
|
paulo@40
|
162
|
paulo@40
|
163 iq.join()
|
paulo@40
|
164
|
paulo@40
|
165 while True:
|
paulo@40
|
166 try:
|
paulo@40
|
167 (idx, docfeed) = oq.get_nowait()
|
paulo@40
|
168 docstruct[idx] = docfeed
|
paulo@40
|
169 except Queue.Empty:
|
paulo@40
|
170 break
|
paulo@40
|
171
|
paulo@40
|
172 print _to_html(docstruct)
|