fixed parser crash when overview contains footpath
[pywienerlinien] / parseHtml.py
1 from BeautifulSoup import BeautifulSoup, NavigableString
2 import urllib2
3 from datetime import time, datetime
4 from textwrap import wrap
5
6
7 class Parser:
8     _overview = None
9     _details = None
10
11     def __init__(self, html):
12         self.soup = BeautifulSoup(html)
13
14     def __iter__(self):
15         for detail in self.details():
16             yield detail
17         raise IndexError()
18
19     def _parse_details(self):
20         trips = map(lambda x: map(lambda x: {
21                         'time': map(lambda x: (time(*map(lambda x: int(x), x.split(':')))), wrap(x.find('td', {'class': 'col_time'}).text, 5)), # black magic appears
22                         'station': map(lambda x: x[2:].strip(),
23                                        filter(lambda x: type(x) == NavigableString, x.find('td', {'class': 'col_station'}).contents)), # filter non NaviStrings
24                         'info': map(lambda x: x.strip(),
25                                     filter(lambda x: type(x) == NavigableString, x.find('td', {'class': 'col_info'}).contents)),
26                     }, x.find('tbody').findAll('tr')),
27                     self.soup.findAll('div', {'class': 'data_table tourdetail'})) # all routes
28         return trips
29
30     @property
31     def details(self):
32         if not self._details:
33             self._details = self._parse_details()
34
35         return self._details
36
37     def _parse_overview(self):
38         """
39         Returns dict containing
40         date: datetime
41         time: [time, time]
42         duration: time
43         change: int
44         price: float
45         """
46         # get overview table
47         table = self.soup.find('table', {'id': 'tbl_fahrten'})
48         # get rows
49         rows = table.findAll('tr')[1:]
50         overview = map(lambda x: {
51                            'date': datetime.strptime(x.find('td', {'class': 'col_date'}).text, '%d.%m.%Y') # grab date
52                                        if x.find('td', {'class': 'col_date'}).text else None, # if date is empty set to None
53                            'time': map(lambda x: time(*map(lambda x: int(x), x.split(':'))) if x else None, # extract times or set to None if empty
54                                        x.find('td', {'class': 'col_time'}).text.split('  - ')),
55                            'duration': time(*map(lambda x: int(x), x.find('td', {'class': 'col_duration'}).text.split(':'))), # grab duration
56                            'change': int(x.find('td', {'class': 'col_change'}).text) # grab changes
57                                        if x.find('td', {'class': 'col_change'}).text else 0, # if change is empty set to 0
58                            'price': float(x.find('td', {'class': 'col_price'}).text.replace(',', '.')) # grab price
59                                        if x.find('td', {'class': 'col_price'}).text.find(',') >= 0 else 0.0, # if price is empty set to 0.0
60                        },
61                        rows)
62
63         return overview
64
65     @property
66     def overview(self):
67         if not self._overview:
68             self._overview = self._parse_overview()
69
70         return self._overview
71
72
73 class iTipParser:
74
75     def __init__(self):
76         raise NotImplementedError