Subversion Repositories blog

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
42 jldugger 1
#!/usr/bin/python
2
 
3
import sqlite3
4
from datetime import datetime
5
#!/usr/bin/env python
6
if not hasattr(__builtins__, 'True'): True, False = 1, 0
7
import re, sys, urllib, htmlentitydefs, codecs, StringIO, types
8
import sgmllib
9
import urlparse
10
sgmllib.charref = re.compile('&#([xX]?[0-9a-fA-F]+)[^0-9a-fA-F]')
11
 
12
try: from textwrap import wrap
13
except: pass
14
 
15
# Use Unicode characters instead of their ascii psuedo-replacements
16
UNICODE_SNOB = 0
17
 
18
# Put the links after each paragraph instead of at the end.
19
LINKS_EACH_PARAGRAPH = 0
20
 
21
# Wrap long lines at position. 0 for no wrapping. (Requires Python 2.3.)
22
BODY_WIDTH = 78
23
 
24
# Don't show internal links (href="#local-anchor") -- corresponding link targets
25
# won't be visible in the plain text file anyway.
26
SKIP_INTERNAL_LINKS = False
27
 
28
### Entity Nonsense ###
29
 
30
def name2cp(k):
31
    if k == 'apos': return ord("'")
32
    if hasattr(htmlentitydefs, "name2codepoint"): # requires Python 2.3
33
        return htmlentitydefs.name2codepoint[k]
34
    else:
35
        k = htmlentitydefs.entitydefs[k]
36
        if k.startswith("&#") and k.endswith(";"): return int(k[2:-1]) # not in latin-1
37
        return ord(codecs.latin_1_decode(k)[0])
38
 
39
unifiable = {'rsquo':"'", 'lsquo':"'", 'rdquo':'"', 'ldquo':'"',
40
'copy':'(C)', 'mdash':'--', 'nbsp':' ', 'rarr':'->', 'larr':'<-', 'middot':'*',
41
'ndash':'-', 'oelig':'oe', 'aelig':'ae',
42
'agrave':'a', 'aacute':'a', 'acirc':'a', 'atilde':'a', 'auml':'a', 'aring':'a',
43
'egrave':'e', 'eacute':'e', 'ecirc':'e', 'euml':'e',
44
'igrave':'i', 'iacute':'i', 'icirc':'i', 'iuml':'i',
45
'ograve':'o', 'oacute':'o', 'ocirc':'o', 'otilde':'o', 'ouml':'o',
46
'ugrave':'u', 'uacute':'u', 'ucirc':'u', 'uuml':'u'}
47
 
48
unifiable_n = {}
49
 
50
for k in unifiable.keys():
51
    unifiable_n[name2cp(k)] = unifiable[k]
52
 
53
def charref(name):
54
    if name[0] in ['x','X']:
55
        c = int(name[1:], 16)
56
    else:
57
        c = int(name)
58
 
59
    if not UNICODE_SNOB and c in unifiable_n.keys():
60
        return unifiable_n[c]
61
    else:
62
        return unichr(c)
63
 
64
def entityref(c):
65
    if not UNICODE_SNOB and c in unifiable.keys():
66
        return unifiable[c]
67
    else:
68
        try: name2cp(c)
69
        except KeyError: return "&" + c
70
        else: return unichr(name2cp(c))
71
 
72
def replaceEntities(s):
73
    s = s.group(1)
74
    if s[0] == "#":
75
        return charref(s[1:])
76
    else: return entityref(s)
77
 
78
r_unescape = re.compile(r"&(#?[xX]?(?:[0-9a-fA-F]+|\w{1,8}));")
79
def unescape(s):
80
    return r_unescape.sub(replaceEntities, s)
81
 
82
def fixattrs(attrs):
83
    # Fix bug in sgmllib.py
84
    if not attrs: return attrs
85
    newattrs = []
86
    for attr in attrs:
87
        newattrs.append((attr[0], unescape(attr[1])))
88
    return newattrs
89
 
90
### End Entity Nonsense ###
91
 
92
def onlywhite(line):
93
    """Return true if the line does only consist of whitespace characters."""
94
    for c in line:
95
        if c is not ' ' and c is not '  ':
96
            return c is ' '
97
    return line
98
 
99
def optwrap(text):
100
    """Wrap all paragraphs in the provided text."""
101
    if not BODY_WIDTH:
102
        return text
103
 
104
    assert wrap, "Requires Python 2.3."
105
    result = ''
106
    newlines = 0
107
    for para in text.split("\n"):
108
        if len(para) > 0:
109
            if para[0] is not ' ' and para[0] is not '-' and para[0] is not '*':
110
                for line in wrap(para, BODY_WIDTH):
111
                    result += line + "\n"
112
                result += "\n"
113
                newlines = 2
114
            else:
115
                if not onlywhite(para):
116
                    result += para + "\n"
117
                    newlines = 1
118
        else:
119
            if newlines < 2:
120
                result += "\n"
121
                newlines += 1
122
    return result
123
 
124
def hn(tag):
125
    if tag[0] == 'h' and len(tag) == 2:
126
        try:
127
            n = int(tag[1])
128
            if n in range(1, 10): return n
129
        except ValueError: return 0
130
 
131
class _html2text(sgmllib.SGMLParser):
132
    def __init__(self, out=None, baseurl=''):
133
        sgmllib.SGMLParser.__init__(self)
134
 
135
        if out is None: self.out = self.outtextf
136
        else: self.out = out
137
        self.outtext = u''
138
        self.quiet = 0
139
        self.p_p = 0
140
        self.outcount = 0
141
        self.start = 1
142
        self.space = 0
143
        self.a = []
144
        self.astack = []
145
        self.acount = 0
146
        self.list = []
147
        self.blockquote = 0
148
        self.pre = 0
149
        self.startpre = 0
150
        self.lastWasNL = 0
151
        self.abbr_title = None # current abbreviation definition
152
        self.abbr_data = None # last inner HTML (for abbr being defined)
153
        self.abbr_list = {} # stack of abbreviations to write later
154
        self.baseurl = baseurl
155
 
156
    def outtextf(self, s):
157
        self.outtext += s
158
 
159
    def close(self):
160
        sgmllib.SGMLParser.close(self)
161
 
162
        self.pbr()
163
        self.o('', 0, 'end')
164
 
165
        return self.outtext
166
 
167
    def handle_charref(self, c):
168
        self.o(charref(c))
169
 
170
    def handle_entityref(self, c):
171
        self.o(entityref(c))
172
 
173
    def unknown_starttag(self, tag, attrs):
174
        self.handle_tag(tag, attrs, 1)
175
 
176
    def unknown_endtag(self, tag):
177
        self.handle_tag(tag, None, 0)
178
 
179
    def previousIndex(self, attrs):
180
        """ returns the index of certain set of attributes (of a link) in the
181
            self.a list
182
 
183
            If the set of attributes is not found, returns None
184
        """
185
        if not attrs.has_key('href'): return None
186
 
187
        i = -1
188
        for a in self.a:
189
            i += 1
190
            match = 0
191
 
192
            if a.has_key('href') and a['href'] == attrs['href']:
193
                if a.has_key('title') or attrs.has_key('title'):
194
                        if (a.has_key('title') and attrs.has_key('title') and
195
                            a['title'] == attrs['title']):
196
                            match = True
197
                else:
198
                    match = True
199
 
200
            if match: return i
201
 
202
    def handle_tag(self, tag, attrs, start):
203
        attrs = fixattrs(attrs)
204
 
205
        if hn(tag):
206
            self.p()
207
            if start: self.o(hn(tag)*"#" + ' ')
208
 
209
        if tag in ['p', 'div']: self.p()
210
 
211
        if tag == "br" and start: self.o("  \n")
212
 
213
        if tag == "hr" and start:
214
            self.p()
215
            self.o("* * *")
216
            self.p()
217
 
218
        if tag in ["head", "style", 'script']:
219
            if start: self.quiet += 1
220
            else: self.quiet -= 1
221
 
222
        if tag in ["body"]:
223
            self.quiet = 0 # sites like 9rules.com never close <head>
224
 
225
        if tag == "blockquote":
226
            if start:
227
                self.p(); self.o('> ', 0, 1); self.start = 1
228
                self.blockquote += 1
229
            else:
230
                self.blockquote -= 1
231
                self.p()
232
 
233
        if tag in ['em', 'i', 'u']: self.o("_")
234
        if tag in ['strong', 'b']: self.o("**")
235
        if tag == "code" and not self.pre: self.o('`') #TODO: `` `this` ``
236
        if tag == "abbr":
237
            if start:
238
                attrsD = {}
239
                for (x, y) in attrs: attrsD[x] = y
240
                attrs = attrsD
241
 
242
                self.abbr_title = None
243
                self.abbr_data = ''
244
                if attrs.has_key('title'):
245
                    self.abbr_title = attrs['title']
246
            else:
247
                if self.abbr_title != None:
248
                    self.abbr_list[self.abbr_data] = self.abbr_title
249
                    self.abbr_title = None
250
                self.abbr_data = ''
251
 
252
        if tag == "a":
253
            if start:
254
                attrsD = {}
255
                for (x, y) in attrs: attrsD[x] = y
256
                attrs = attrsD
257
                if attrs.has_key('href') and not (SKIP_INTERNAL_LINKS and attrs['href'].startswith('#')):
258
                    self.astack.append(attrs)
259
                    self.o("[")
260
                else:
261
                    self.astack.append(None)
262
            else:
263
                if self.astack:
264
                    a = self.astack.pop()
265
                    if a:
266
                        i = self.previousIndex(a)
267
                        if i is not None:
268
                            a = self.a[i]
269
                        else:
270
                            self.acount += 1
271
                            a['count'] = self.acount
272
                            a['outcount'] = self.outcount
273
                            self.a.append(a)
274
                        self.o("][" + `a['count']` + "]")
275
 
276
        if tag == "img" and start:
277
            attrsD = {}
278
            for (x, y) in attrs: attrsD[x] = y
279
            attrs = attrsD
280
            if attrs.has_key('src'):
281
                attrs['href'] = attrs['src']
282
                alt = attrs.get('alt', '')
283
                i = self.previousIndex(attrs)
284
                if i is not None:
285
                    attrs = self.a[i]
286
                else:
287
                    self.acount += 1
288
                    attrs['count'] = self.acount
289
                    attrs['outcount'] = self.outcount
290
                    self.a.append(attrs)
291
                self.o("![")
292
                self.o(alt)
293
                self.o("]["+`attrs['count']`+"]")
294
 
295
        if tag == 'dl' and start: self.p()
296
        if tag == 'dt' and not start: self.pbr()
297
        if tag == 'dd' and start: self.o('    ')
298
        if tag == 'dd' and not start: self.pbr()
299
 
300
        if tag in ["ol", "ul"]:
301
            if start:
302
                self.list.append({'name':tag, 'num':0})
303
            else:
304
                if self.list: self.list.pop()
305
 
306
            self.p()
307
 
308
        if tag == 'li':
309
            if start:
310
                self.pbr()
311
                if self.list: li = self.list[-1]
312
                else: li = {'name':'ul', 'num':0}
313
                self.o("  "*len(self.list)) #TODO: line up <ol><li>s > 9 correctly.
314
                if li['name'] == "ul": self.o("* ")
315
                elif li['name'] == "ol":
316
                    li['num'] += 1
317
                    self.o(`li['num']`+". ")
318
                self.start = 1
319
            else:
320
                self.pbr()
321
 
322
        if tag in ["table", "tr"] and start: self.p()
323
        if tag == 'td': self.pbr()
324
 
325
        if tag == "pre":
326
            if start:
327
                self.startpre = 1
328
                self.pre = 1
329
            else:
330
                self.pre = 0
331
            self.p()
332
 
333
    def pbr(self):
334
        if self.p_p == 0: self.p_p = 1
335
 
336
    def p(self): self.p_p = 2
337
 
338
    def o(self, data, puredata=0, force=0):
339
        if self.abbr_data is not None: self.abbr_data += data
340
 
341
        if not self.quiet:
342
            if puredata and not self.pre:
343
                data = re.sub('\s+', ' ', data)
344
                if data and data[0] == ' ':
345
                    self.space = 1
346
                    data = data[1:]
347
            if not data and not force: return
348
 
349
            if self.startpre:
350
                #self.out(" :") #TODO: not output when already one there
351
                self.startpre = 0
352
 
353
            bq = (">" * self.blockquote)
354
            if not (force and data and data[0] == ">") and self.blockquote: bq += " "
355
 
356
            if self.pre:
357
                bq += "    "
358
                data = data.replace("\n", "\n"+bq)
359
 
360
            if self.start:
361
                self.space = 0
362
                self.p_p = 0
363
                self.start = 0
364
 
365
            if force == 'end':
366
                # It's the end.
367
                self.p_p = 0
368
                self.out("\n")
369
                self.space = 0
370
 
371
 
372
            if self.p_p:
373
                self.out(('\n'+bq)*self.p_p)
374
                self.space = 0
375
 
376
            if self.space:
377
                if not self.lastWasNL: self.out(' ')
378
                self.space = 0
379
 
380
            if self.a and ((self.p_p == 2 and LINKS_EACH_PARAGRAPH) or force == "end"):
381
                if force == "end": self.out("\n")
382
 
383
                newa = []
384
                for link in self.a:
385
                    if self.outcount > link['outcount']:
386
                        self.out("   ["+`link['count']`+"]: " + urlparse.urljoin(self.baseurl, link['href']))
387
                        if link.has_key('title'): self.out(" ("+link['title']+")")
388
                        self.out("\n")
389
                    else:
390
                        newa.append(link)
391
 
392
                if self.a != newa: self.out("\n") # Don't need an extra line when nothing was done.
393
 
394
                self.a = newa
395
 
396
            if self.abbr_list and force == "end":
397
                for abbr, definition in self.abbr_list.items():
398
                    self.out("  *[" + abbr + "]: " + definition + "\n")
399
 
400
            self.p_p = 0
401
            self.out(data)
402
            self.lastWasNL = data and data[-1] == '\n'
403
            self.outcount += 1
404
 
405
    def handle_data(self, data):
406
        if r'\/script>' in data: self.quiet -= 1
407
        self.o(data, 1)
408
 
409
    def unknown_decl(self, data): pass
410
 
411
def wrapwrite(text): sys.stdout.write(text.encode('utf8'))
412
 
413
def html2text_file(html, out=wrapwrite, baseurl=''):
414
    h = _html2text(out, baseurl)
415
    h.feed(html)
416
    h.feed("")
417
    return h.close()
418
 
419
def html2text(html, baseurl=''):
420
    return optwrap(html2text_file(html, None, baseurl))
421
 
422
conn = sqlite3.connect('/home/jldugger/Desktop/test/liferea.db')
423
 
424
curs = conn.cursor()
425
query = 'SELECT title, date, description FROM items WHERE source like "%jldugger.livejournal.com%"'
426
curs.execute(query)
427
 
428
field_desc = [f[0] for f in curs.description]
429
 
430
errCount = 1
431
 
432
for row in curs.fetchall():
433
   try:
434
      with codecs.open("%s.md" % '-'.join(row[0].split(' ')), mode='w',encoding='utf-8') as f:
435
         f.write(u'Title: ')
436
         f.write(row[0])
437
         f.write(u'\nDate: ')
438
         f.write(str(datetime.fromtimestamp(row[1])))
439
         f.write(u'\n\n')
440
         f.write(html2text(row[2]))
441
   except:
442
      errCount+=1
443
      with codecs.open("%s.md" % str(errCount), mode='w',encoding='utf-8') as f:
444
         f.write(u'Title: ')
445
         f.write(row[0])
446
         f.write(u'\nDate: ')
447
         f.write(str(datetime.fromtimestamp(row[1])))
448
         f.write(u'\n\n')
449
         f.write(html2text(row[2]))
450
 
451
conn.close