class item(ItemElement): klass = Account def condition(self): return self.page.accounts_list_condition(self.el) class Type(Filter): def filter(self, label): for pattern, actype in AccountsPage.TYPES.items(): if pattern in label: return actype return Account.TYPE_UNKNOWN obj__title = CleanText('td[@class="ColonneLibelle"][2]') obj__nature = CleanText('td[@class="ColonneLibelle"][3]') obj_label = Format('%s %s', Field('_title'), Field('_nature')) obj_currency = FrenchTransaction.Currency( './td[@class="ColonneCode"]') obj_id = CleanText('td[@class="ColonneLibelle"][1]') obj__link = Link('td[@class="ColonneLibelle"][1]/a', default=NotAvailable) obj__rib_link = Link('.//a[contains(@href, "rib.jsp")]') obj_type = Type(Field('label')) obj_balance = CleanDecimal('td[@class="ColonneNumerique"]/nobr', replace_dots=True)
class item(ItemElement): klass = Recipe obj_id = Regexp(Link('.//a[has-class("bu_cuisine_recette_img")]'), r'.*\/(.*)$') obj_url = Link('.//a[has-class("bu_cuisine_recette_img")]') obj_title = CleanText( './/p[has-class("bu_cuisine_recette_title")]') obj_short_description = NotAvailable obj_author = NotAvailable obj_ingredients = NotAvailable class obj_picture(ItemElement): klass = BaseImage obj_url = NotAvailable def obj_thumbnail(self): style = Attr( './/a[has-class("bu_cuisine_recette_img")]/span', 'style')(self) return Thumbnail( style.replace("background-image:url(", "").rstrip(");")) obj_instructions = NotAvailable obj_preparation_time = CleanDecimal( '(.//span[has-class("bu_cuisine_recette_carnet_duree")])[1]') obj_cooking_time = CleanDecimal( '(.//span[has-class("bu_cuisine_recette_carnet_duree")])[2]', default=0) obj_nb_person = NotAvailable
def obj_coming(self): if Field('type')(self) == Account.TYPE_CHECKING and Field('balance')(self) != 0: # When the balance is 0, we get a website unavailable on the history page # and the following navigation is broken has_coming = False coming = 0 details_page = self.page.browser.open(Field('url')(self)) coming_op_link = Link('//a[contains(text(), "Opérations à venir")]', default=NotAvailable)(details_page.page.doc) if coming_op_link: coming_op_link = Regexp(Link('//a[contains(text(), "Opérations à venir")]'), r'../(.*)')(details_page.page.doc) coming_operations = self.page.browser.open(self.page.browser.BASEURL + '/voscomptes/canalXHTML/CCP/' + coming_op_link) else: coming_op_link = Link('//a[contains(text(), "Opérations en cours")]')(details_page.page.doc) coming_operations = self.page.browser.open(coming_op_link) if CleanText('//span[@id="amount_total"]')(coming_operations.page.doc): has_coming = True coming += CleanDecimal('//span[@id="amount_total"]', replace_dots=True)(coming_operations.page.doc) if CleanText(u'.//dt[contains(., "Débit différé à débiter")]')(self): has_coming = True coming += CleanDecimal(u'.//dt[contains(., "Débit différé à débiter")]/following-sibling::dd[1]', replace_dots=True)(self) return coming if has_coming else NotAvailable return NotAvailable
class item(ItemElement): klass = Message obj_id = Regexp(Link('./div/div/small/a', default=''), '/.+/status/(.+)', default=None) obj_title = Regexp( CleanText( './div[@class="content"]/div/p[has-class("tweet-text")]', replace=[('@ ', '@'), ('# ', '#'), ('http:// ', 'http://')]), '(.{50}|.+).+') obj_content = CleanText( './div[@class="content"]/div/p[has-class("tweet-text")]', replace=[('@ ', '@'), ('# ', '#'), ('http:// ', 'http://')]) obj_sender = Regexp(Link('./div/div/small/a', default=''), '/(.+)/status/.+', default=None) obj_date = DatetimeFromTimestamp( Attr( './div/div[@class="stream-item-header"]/small/a/span | ./div/div[@class="ProfileTweet-authorDetails"]/span/a/span', 'data-time')) def validate(self, obj): return obj.id is not None
class item(ItemElement): klass = Suggestion obj_id = Regexp(Link('./td[2]/a'), '/([0-9]+)') obj_name = CleanText('./td[2]/a/text()') obj_description = Format('Genre: %s - Country: %s', CleanText('./td[3]/text()'), CleanText('./td[4]/text()')) obj_url = Link('./td[2]/a')
def obj_url(self): url = Link(u'./a', default=NotAvailable)(self) if not url: url = Regexp(Attr(u'.//span', 'onclick', default=''), r'\'(https.*)\'', default=NotAvailable)(self) if url: if 'CreditRenouvelable' in url: url = Link(u'.//a[contains(text(), "espace de gestion crédit renouvelable")]')(self.el) return urljoin(self.page.url, url) return url
class item(ItemElement): klass = BaseCalendarEvent obj_url = Link('./a[1]') obj_id = Regexp(Link('./a[1]'), r'\?(\d+)') obj_summary = CleanText('./a[1]') obj_start_date = Date(CleanText('./span[1]')) obj_category = CATEGORIES.CONCERT obj_status = STATUS.CONFIRMED
def next_page(self): if Link(u'//a[text()="▶"]')(self) is not None: form = self.page.get_form(xpath='//*[@id="history_form"]') form['page'] = CleanDecimal(Link(u'//a[text()="▶"]'))(self) return requests.Request("POST", self.page.url, data=form)
def obj_url(self): async_page = Async('details').loaded_page(self) url = Link( '//a[contains(@href, "download")]|//a[contains(@href, "generated_invoices")]', default=NotAvailable)(async_page.doc) if not url: url = Link( '//a[contains(text(), "Imprimer un récapitulatif de commande")]' )(async_page.doc) return url
class item(ItemElement): klass = BaseCalendarEvent obj_url = Link('./div[@class="bbox"]/h1/a') obj_id = Regexp(Link('./div[@class="bbox"]/h1/a'), r'aspx\?(.+)') obj_location = CleanText('./div[@class="bbox"]/span/a') obj_start_date = DateTime(Attr('.//time', 'datetime')) obj_summary = Regexp(Attr('./div[@class="bbox"]/h1/a', 'title'), r'details of (.+)') obj_category = CATEGORIES.CONCERT obj_status = STATUS.CONFIRMED
class item(ItemElement): klass = Subscription obj_id = Regexp(Link('.'), r'\bidContrat=(\d+)', default='') obj__page = Regexp(Link('.'), r'\bpage=([^&]+)', default='') obj_label = CleanText('.') def validate(self, obj): # unsubscripted contracts may still be there, skip them else # facture-historique could yield wrong bills return bool(obj.id) and obj._page != 'nec-tdb-ouvert'
class item(ItemElement): klass = Housing def condition(self): return Regexp(Link('./div[has-class("box-header")]/a[@class="title-item"]'), '/annonces/(.*)', default=None)(self) obj_id = Regexp(Link('./div[has-class("box-header")]/a[@class="title-item"]'), '/annonces/(.*)') obj_title = CleanText('./div[has-class("box-header")]/a[@class="title-item"]') obj_area = CleanDecimal(Regexp(CleanText('./div[has-class("box-header")]/a/span[@class="h1"]'), '(.*?)(\d*) m\xb2(.*?)', '\\2'), default=NotAvailable) obj_cost = CleanDecimal(CleanText('./div[has-class("box-header")]/a/span[@class="price"]'), replace_dots=True, default=Decimal(0)) obj_currency = Regexp(CleanText('./div[has-class("box-header")]/a/span[@class="price"]'), '.*([%s%s%s])' % (u'€', u'$', u'£'), default=u'€') obj_utilities = UTILITIES.UNKNOWN def obj_date(self): _date = Regexp(CleanText('./div[has-class("box-header")]/p[@class="date"]'), '.* / (.*)')(self) return parse_french_date(_date) obj_station = CleanText('./div[@class="box-body"]/div/div/p[@class="item-transports"]', default=NotAvailable) obj_location = CleanText('./div[@class="box-body"]/div/div/p[@class="item-description"]/strong') obj_text = CleanText('./div[@class="box-body"]/div/div/p[@class="item-description"]') obj_rooms = CleanDecimal( './div[@class="box-body"]/div/div/div[@class="clearfix"]/ul[has-class("item-summary")]/li[1]/strong', default=NotAvailable ) obj_price_per_meter = PricePerMeterFilter() def obj_bedrooms(self): rooms_bedrooms_area = XPath( './div[@class="box-body"]/div/div/div[@class="clearfix"]/ul[has-class("item-summary")]/li' )(self) if len(rooms_bedrooms_area) > 2: return CleanDecimal( './div[@class="box-body"]/div/div/div[@class="clearfix"]/ul[has-class("item-summary")]/li[2]/strong', default=NotAvailable )(self) else: return NotAvailable obj_url = Format( u'http://www.pap.fr%s', Link( './div[@class="box-body"]/div/div/div[@class="clearfix"]/div[@class="float-right"]/a' ) ) def obj_photos(self): photos = [] for img in XPath('./div[@class="box-body"]/div/div/a/img/@src')(self): photos.append(HousingPhoto(u'%s' % img)) return photos
class item(ItemElement): klass = Bill load_details = Link('.//a[contains(text(), "détails")]') & MyAsyncLoad obj_id = Format('%s_%s', Env('subid'), Field('label')) obj_url = Async('details') & Link('//a[span[contains(text(), "Télécharger la facture")]]', default=NotAvailable) obj_date = Date(CleanText('./div[contains(@class, "cell-date")]'), dayfirst=True) obj_format = 'pdf' obj_label = Regexp(CleanText('./div[contains(@class, "cell-nb-order")]'), r' (.*)') obj_type = DocumentTypes.BILL obj_price = CleanDecimal(CleanText('./div[contains(@class, "cell-value")]'), replace_dots=(' ', '€')) obj_currency = 'EUR'
class item(ItemElement): klass = Account load_details = Attr('.//a', 'href') & AsyncLoad obj__link_id = Async( 'details', Link('//li/a[contains(text(), "Mouvements")]')) obj__link_inv = Link('./td[1]/a') obj_id = CleanText('./td[2]', replace=[(' ', '')]) obj_label = CleanText('./td[1]') obj_balance = CleanDecimal('./td[3]', replace_dots=True) obj_currency = FrenchTransaction.Currency('./td[4]') obj__card_links = [] obj_type = Account.TYPE_LIFE_INSURANCE obj__is_inv = True
def populate(self, accounts): cards = [] for account in accounts: for li in self.doc.xpath('//li[@class="nav-category"]'): title = CleanText().filter(li.xpath('./h3')) for a in li.xpath('./ul/li//a'): label = CleanText().filter( a.xpath('.//span[@class="nav-category__name"]')) balance_el = a.xpath( './/span[@class="nav-category__value"]') balance = CleanDecimal( replace_dots=True, default=NotAvailable).filter(balance_el) if 'CARTE' in label and balance: acc = Account() acc.balance = balance acc.label = label acc.currency = FrenchTransaction.Currency().filter( balance_el) acc._link = Link().filter(a.xpath('.')) acc._history_page = acc._link acc.id = acc._webid = Regexp( pattern='([^=]+)$').filter(Link().filter( a.xpath('.'))) acc.type = Account.TYPE_CARD if not acc in cards: cards.append(acc) elif account.label == label and account.balance == balance: if not account.type: account.type = AccountsPage.ACCOUNT_TYPES.get( title, Account.TYPE_UNKNOWN) if account.type == Account.TYPE_LOAN: account._history_page = None elif account.type in (Account.TYPE_LIFE_INSURANCE, Account.TYPE_MARKET): account._history_page = re.sub( '/$', '', Link().filter(a.xpath('.'))) elif '/compte/cav' in a.attrib[ 'href'] or not 'titulaire' in self.url: account._history_page = self.browser.other_transactions else: account._history_page = self.browser.budget_transactions account._webid = Attr( None, 'data-account-label').filter( a.xpath( './/span[@class="nav-category__name"]')) accounts.extend(cards)
class get_history(ListElement): item_xpath = '//table[@id="ContentTable_datas"]//tr[@class]' next_page = Link('//table[@id="tgDecorationFoot"]//b/following-sibling::a[1]') class item(ItemElement): klass = FrenchTransaction obj_rdate = FrenchTransaction.Date(CleanText('./td[1]')) obj_date = FrenchTransaction.Date(CleanText('./td[3]')) obj_raw = FrenchTransaction.Raw(CleanText('./td[2]')) _obj_amnt = FrenchTransaction.Amount(CleanText('./td[5]'), replace_dots=False) obj_original_amount = FrenchTransaction.Amount(CleanText('./td[4]'), replace_dots=False) obj_original_currency = FrenchTransaction.Currency(CleanText('./td[4]')) obj_commission = FrenchTransaction.Amount(CleanText('./td[6]'), replace_dots=False) def obj__coming(self): if Field('date')(self) >= date.today(): return True return def obj_amount(self): if not Field('obj_commission'): return Field('_obj_amnt') else: return CleanDecimal(replace_dots=False).filter(self.el.xpath('./td[5]')) - CleanDecimal(replace_dots=False).filter(self.el.xpath('./td[6]'))
class iter_history(TableElement): item_xpath = '//table[@class="Tableau"]/tr[td[not(has-class("enteteTableau"))]]' head_xpath = '//table[@class="Tableau"]/tr[td[has-class("enteteTableau")]]/td' col_date = u'Date d\'effet' col_raw = u'Nature du mouvement' col_amount = u'Montant brut' next_page = Link('//a[contains(@href, "PageSuivante")]', default=None) class item(ItemElement): klass = Transaction load_details = Link('./td/a', default=None) & AsyncLoad obj_date = Date(CleanText(TableCell('date')), dayfirst=True) obj_raw = Transaction.Raw(TableCell('raw')) obj_amount = CleanDecimal(TableCell('amount'), replace_dots=True) obj_investments = Env('investments') def parse(self, el): try: page = Async('details').loaded_page(self) except AttributeError: page = None self.env['investments'] = list(page.get_investments( )) if page and 'numMvt' in page.url else []
class iter_history(TableElement): item_xpath = '//table/tbody/tr' head_xpath = '//table/thead/tr/th' col_label = 'Nature' col_amount = 'Montant' col_date = 'Date d\'effet' next_page = Link('//li[@class="pagination__next"]/a') class item(ItemElement): klass = Transaction obj_date = Date(CleanText(TableCell('date')), dayfirst=True) obj_raw = Transaction.Raw(CleanText(TableCell('label'))) obj_amount = CleanDecimal(TableCell('amount'), replace_dots=True, default=NotAvailable) obj__is_coming = False def parse(self, el): if el.xpath('./td[2]/a'): m = re.search( '(\d+)', el.xpath('./td[2]/a')[0].get( 'data-modal-alert-behavior', '')) if m: self.env['account']._history_pages.append((Field('raw')(self),\ self.page.browser.open('%s%s%s' % (self.page.url.split('mouvements')[0], 'mouvement/', m.group(1))).page)) raise SkipItem()
class iter_prices(ListElement): item_xpath = '//div[@class="adContainer "]' next_page = Link( '//section[@class="pagination"]/ul/li[@class="last"]/a') class item(ItemElement): klass = Price obj_id = CleanText('./p/a/@data-annid') obj_cost = CleanDecimal('./a/div/div/div/div[@class="fieldPrice"]') obj_currency = Regexp( CleanText('./a/div/div/div/div[@class="fieldPrice"]'), '.*([%s%s%s])' % (u'€', u'$', u'£'), default=u'€') obj_message = Format( '%s / %s / %s', CleanText('./a/div/div/h3'), CleanText('./a/div/div/div/div[@class="fieldYear"]'), CleanText('./a/div/div/div/div[@class="fieldMileage"]')) obj_url = Format('http://www.lacentrale.fr%s', CleanText('./a/@href')) obj_product = LaCentraleProduct() def obj_shop(self): shop = Shop(CleanText('./p/a/@data-annid')(self)) return shop
def obj_code(self): link = Link(TableCell('label')(self)[0].xpath('./a'), default=NotAvailable)(self) if not link: return NotAvailable return Regexp(pattern='isin=([A-Z\d]+)&?', default=NotAvailable).filter(link)
def companies_link(self): companies_link = [] for tr in self.doc.xpath( '//table[@summary="Liste des titulaires de contrats cartes"]//tr' ): companies_link.append(Link(tr.xpath('.//a'))(self)) return companies_link
class iter_history(ListElement): item_xpath = '''//table[@class="ca-table"][caption[span[b[contains(text(), "HISTORIQUE DES OPERATIONS")]]]] //tr[contains(@class, "ligne-")]''' next_page = Link( '//a[@class="liennavigationcorpspage"][img[@alt="Page suivante"]]', default=None) class item(ItemElement): klass = Transaction def fill_env(self, page, parent=None): # This *Element's parent has only the dateguesser in its env and we want to # use the same object, not copy it. self.env = parent.env def obj_date(self): # Dates in the first column may appear as '12/01/2019' or '12/01' date = CleanText('./td[1]/font//text()')(self) if len(date) == 10: return Date(CleanText('./td[1]/font//text()'), dayfirst=True)(self) elif len(date) == 5: # Date has no indicated year. return DateGuesser(CleanText('./td[1]//text()'), Env('date_guesser'))(self) obj_raw = Transaction.Raw(CleanText('./td[2]/font//text()')) obj_amount = CleanDecimal.French('./td[3]/font//text()') obj_rdate = Field('date')
class item(ItemElement): klass = Account obj__owner = Regexp( CleanText('./td[1]/text()', replace=[(' ', '')]), 'Titulaire:(.*)') obj_id = Format( '%s%s', Regexp(CleanText('./td[1]/a', replace=[(' ', '')]), '([\d]+)'), Field('_owner')) obj_label = Field('_owner') obj_balance = NotAvailable obj_currency = FrenchTransaction.Currency('./td[2]') obj__link_id = Link('./td[1]/a') obj_type = Account.TYPE_CARD obj__card_links = [] obj__is_inv = False obj__is_webid = False def parse(self, el): account = [ acc for acc in self.env['accounts'] if acc.id == Field('id')(self) ] if account: account[0]._card_links.append(Field('_link_id')(self)) raise SkipItem()
class get_operations(ListElement): item_xpath = '//table[has-class("style-operations")]/tbody//tr' next_page = Link( '//div[@class="m-table-paginator full-width-xs"]//a[@id="next-page"]' ) class item(ItemElement): klass = Transaction def condition(self): if 'tr-section' in self.el.attrib.get('class', ''): return False elif 'tr-trigger' in self.el.attrib.get('class', ''): return True return False def obj_date(self): return Transaction.Date( Regexp( CleanText( './preceding::tr[has-class("tr-section")][1]/th'), r'(\d+/\d+/\d+)'))(self) obj_raw = Transaction.Raw( Format( '%s %s', CleanText('./td[1]'), CleanText( './following-sibling::tr[contains(@class, "tr-more")]/td/p[1]/span' ))) obj_amount = MyDecimal('./td[2]', replace_dots=True)
class item(ItemElement): def klass(self): return BiplanCalendarEventConcert() if self.env['is_concert'] else BiplanCalendarEventTheatre() def condition(self): return (self.el.xpath('./div') and CleanText('./div/a/img/@src')(self)[-1] != '/') def validate(self, obj): return (self.is_valid_event(obj, self.env['city'], self.env['categories']) and self.is_event_in_valid_period(obj.start_date, self.env['date_from'], self.env['date_to'])) def is_valid_event(self, event, city, categories): if city and city != '' and city.upper() != event.city.upper(): return False if categories and len(categories) > 0 and event.category not in categories: return False return True def is_event_in_valid_period(self, event_date, date_from, date_to): if event_date >= date_from: if not date_to: return True else: if event_date <= date_to: return True return False obj_id = Regexp(Link('./div/a'), '/(.*?).html') obj_start_date = CombineDate(BiplanDate('div/div/b'), StartTime('div/div/b')) obj_end_date = CombineDate(BiplanDate('div/div/b'), EndTime('.')) obj_price = BiplanPrice('div/div/b') obj_summary = CleanText("div/div/div/a/strong")
class item(ItemElement): klass = Account def obj_id(self): area_id = Regexp(CleanText('//span[@class="CelMnTiersT1"]'), r'\((\d+)\)', default='')(self) acc_id = Regexp(CleanText('./td[1]'), r'(\d+)\s*(\d+)', r'\1\2')(self) if area_id: return '%s.%s' % (area_id, acc_id) return acc_id def obj__formdata(self): js = Attr('./td/a[1]', 'onclick', default=None)(self) if js is None: return args = re.search(r'\((.*)\)', js).group(1).split(',') form = args[0].strip().split('.')[1] idx = args[2].strip() idroot = args[4].strip().replace("'", "") return (form, idx, idroot) obj_url = Link('./td/a[1]', default=None)
class iter_history(TableElement): item_xpath = '//table[@class="Tableau"]/tr[td[not(has-class("enteteTableau"))]]' head_xpath = '//table[@class="Tableau"]/tr[td[has-class("enteteTableau")]]/td' col_date = u'Date d\'effet' col_raw = u'Nature du mouvement' col_amount = u'Montant brut' next_page = Link('//a[contains(@href, "PageSuivante")]', default=None) class item(ItemElement): klass = Transaction obj_date = Date(CleanText(TableCell('date')), dayfirst=True) obj_raw = Transaction.Raw(TableCell('raw')) obj_amount = CleanDecimal(TableCell('amount'), replace_dots=True) obj__detail = Env('detail') def obj_id(self): try: return Regexp(Link('./td/a', default=None), 'numMvt=(\d+)', default=None)(self) except TypeError: return NotAvailable def parse(self, el): link = Link('./td/a', default=None)(self) page = self.page.browser.async_open(link) if link else None self.env['detail'] = page
def get_account_details(self, account_id): balance = CleanDecimal.French( '//a[div[div[span[span[contains(text(), "%s")]]]]]/div[1]/div[2]/span/span' % account_id, default=NotAvailable)(self.doc) currency = Currency( '//a[div[div[span[span[contains(text(), "%s")]]]]]/div[1]/div[2]/span/span' % account_id, default=NotAvailable)(self.doc) label = CleanText( '//a[div[div[span[span[contains(text(), "%s")]]]]]/div[1]/div[1]/span/span' % account_id, default=NotAvailable)(self.doc) url = Link('//a[div[div[span[span[contains(text(), "%s")]]]]]' % account_id, default=None)(self.doc) if url: account_url = 'https://bgpi-gestionprivee.credit-agricole.fr' + url else: account_url = None return balance, currency, label, account_url
class item(ItemElement): klass = Housing def condition(self): return Regexp(Link('./div[@class="header-annonce"]/a'), '/annonces/(.*)', default=None)(self) obj_id = Regexp(Link('./div[@class="header-annonce"]/a'), '/annonces/(.*)') obj_title = CleanText('./div[@class="header-annonce"]/a') obj_area = CleanDecimal(Regexp(CleanText('./div[@class="header-annonce"]/a/span[@class="desc"]'), '(.*?)(\d*) m\xb2(.*?)', '\\2'), default=NotAvailable) obj_cost = CleanDecimal(CleanText('./div[@class="header-annonce"]/a/span[@class="prix"]'), replace_dots=(',', '.'), default=Decimal(0)) obj_currency = Regexp(CleanText('./div[@class="header-annonce"]/a/span[@class="prix"]'), '.*([%s%s%s])' % (u'€', u'$', u'£'), default=u'€') def obj_date(self): _date = CleanText('./div[@class="header-annonce"]/span[@class="date"]')(self) return parse_french_date(_date) obj_station = CleanText('./div/div/div[@cladd=metro]', default=NotAvailable) obj_location = CleanText('./div[@class="clearfix"]/div/a/span/img/@alt') obj_text = CleanText('./div[@class="clearfix"]/div[@class="description clearfix"]/p') def obj_photos(self): photos = [] for img in XPath('//div[@class="vignette-annonce"]/a/span/img/@src')(self): photos.append(HousingPhoto(u'%s' % img)) return photos
def populate(self, accounts): cards = [] for account in accounts: for li in self.doc.xpath('//li[@class="nav-category"]'): title = CleanText().filter(li.xpath('./h3')) for a in li.xpath('./ul/li//a'): label = CleanText().filter(a.xpath('.//span[@class="nav-category__name"]')) balance_el = a.xpath('.//span[@class="nav-category__value"]') balance = CleanDecimal(replace_dots=True, default=NotAvailable).filter(balance_el) if 'CARTE' in label and not empty(balance): acc = Account() acc.balance = balance acc.label = label acc.currency = FrenchTransaction.Currency().filter(balance_el) acc.url = urljoin(self.url, Link().filter(a.xpath('.'))) acc._history_page = acc.url try: acc.id = acc._webid = Regexp(pattern='carte/(.*)$').filter(Link().filter(a.xpath('.'))) except RegexpError: # Those are external cards, ie: amex cards continue acc.type = Account.TYPE_CARD if not acc in cards: cards.append(acc) elif account.label == label and account.balance == balance: if not account.type: account.type = AccountsPage.ACCOUNT_TYPES.get(title, Account.TYPE_UNKNOWN) account._webid = Attr(None, 'data-account-label').filter(a.xpath('.//span[@class="nav-category__name"]')) if cards: self.browser.go_cards_number(cards[0].url) if self.browser.cards.is_here(): self.browser.page.populate_cards_number(cards) accounts.extend(cards)