Commit 42391918 authored by Antoine BOSSY's avatar Antoine BOSSY

Add new CapHousing module for fnaim.fr

parent f813a626
Pipeline #1770 failed with stages
in 0 seconds
# -*- coding: utf-8 -*-
# Copyright(C) 2017 Antoine BOSSY
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from .module import FnaimModule
__all__ = ['FnaimModule']
# -*- coding: utf-8 -*-
# Copyright(C) 2017 Antoine BOSSY
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from weboob.browser import PagesBrowser, URL
from weboob.capabilities.housing import HOUSE_TYPES
from weboob.tools.json import json
from .pages import SearchCityPage, SearchPage, HousingPage
from .constants import QUERY_TYPES, QUERY_HOUSE_TYPES
class FnaimBrowser(PagesBrowser):
BASEURL = 'https://www.fnaim.fr'
cities = URL(r'/include/ajax/ajax.localite.autocomplete.php\?term=(?P<term>)', SearchCityPage)
search = URL(r'/18-louer.htm\?localites=(?P<localities>.+)(?P<types>.+)&SURFACE_MIN=(?P<surface_min>\d*)&SURFACE_MAX=(?P<surface_max>\d*)&PRIX_MAX=(?P<prix_max>\d*)&PRIX_MIN=(?P<prix_min>\d*)&idtf=18&TRANSACTION=(?P<transaction>\d)&submit=Rechercher.*',
r'/18-louer.htm\?.*', r'/17-acheter.htm\?.*', SearchPage)
housing = URL(r'/annonce-immobiliere/(?P<id>.+)/1[8|7]-.*.htm', HousingPage)
def search_city(self, term):
return self.cities.open(term=term).iter_cities()
def get_housing(self, housing_id):
return self.housing.open(id=housing_id).get_housing()
def search_housings(self, query):
types = []
for house_type in query.house_types:
if house_type == HOUSE_TYPES.UNKNOWN:
types = ['&TYPE[]=%s' % t for t in QUERY_HOUSE_TYPES[house_type]]
break
types.extend(['&TYPE[]=%s' % t for t in QUERY_HOUSE_TYPES[house_type]])
types_search = ''.join(types)
transaction = QUERY_TYPES[query.type]
def none_to_empty(field):
if field is None:
return ''
return field
cities = []
for city in query.cities:
label = city.name.capitalize().replace(' ', '+')
cities.append({
'label': label,
'value': label,
'id': city.id,
})
localities = json.dumps(cities)
return self.search.go(localities=localities,
types=types_search,
transaction=transaction,
surface_min=none_to_empty(query.area_min),
surface_max=none_to_empty(query.area_max),
prix_max=none_to_empty(query.cost_max),
prix_min=none_to_empty(query.cost_min)).iter_housings()
from weboob.capabilities.housing import HOUSE_TYPES, POSTS_TYPES
QUERY_TYPES = {
POSTS_TYPES.RENT: 2,
POSTS_TYPES.SALE: 1,
POSTS_TYPES.SHARING: 2 # There is no special search for shared appartments.
}
QUERY_HOUSE_TYPES = {
HOUSE_TYPES.APART: [1],
HOUSE_TYPES.HOUSE: [2],
HOUSE_TYPES.PARKING: [7],
HOUSE_TYPES.LAND: [8],
HOUSE_TYPES.OTHER: [3, 4, 5, 6, 9, 10, 11],
HOUSE_TYPES.UNKNOWN: range(1, 12)
}
# -*- coding: utf-8 -*-
# Copyright(C) 2017 Antoine BOSSY
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from weboob.tools.backend import Module
from weboob.capabilities.housing import CapHousing
from .browser import FnaimBrowser
__all__ = ['FnaimModule']
class FnaimModule(Module, CapHousing):
NAME = 'fnaim'
DESCRIPTION = 'www.fnaim.fr website'
MAINTAINER = 'Antoine BOSSY'
EMAIL = 'mail+github@abossy.fr'
LICENSE = 'AGPLv3+'
VERSION = '1.4'
BROWSER = FnaimBrowser
def get_housing(self, housing):
"""
Get an housing from an ID.
:param housing: ID of the housing
:type housing: str
:rtype: :class:`Housing` or None if not found.
"""
return self.browser.get_housing(housing)
def search_city(self, pattern):
"""
Search a city from a pattern.
:param pattern: pattern to search
:type pattern: str
:rtype: iter[:class:`City`]
"""
return self.browser.search_city(pattern)
def search_housings(self, query):
"""
Search housings.
:param query: search query
:type query: :class:`Query`
:rtype: iter[:class:`Housing`]
"""
return self.browser.search_housings(query)
# -*- coding: utf-8 -*-
# Copyright(C) 2017 Antoine BOSSY
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from weboob.browser.pages import HTMLPage, JsonPage, pagination
from weboob.browser.elements import ItemElement, ListElement, method, DictElement
from weboob.browser.filters.html import Attr, AbsoluteLink, Link
from weboob.browser.filters.json import Dict
from weboob.browser.filters.standard import CleanDecimal, CleanText, Regexp
from weboob.capabilities.base import NotAvailable, Currency
from weboob.capabilities.housing import City, Housing, UTILITIES, HousingPhoto
class SearchCityPage(JsonPage):
@method
class iter_cities(DictElement):
class item(ItemElement):
klass = City
obj_id = Dict('id')
obj_name = Dict('label')
class SearchPage(HTMLPage):
@pagination
@method
class iter_housings(ListElement):
item_xpath = '//li[has-class("item")]'
next_page = AbsoluteLink('//span[has-class("selected")]//following-sibling::span/a')
class item(ItemElement):
klass = Housing
obj_id = Attr('.//footer//ul[has-class("infos")]//li[has-class("suivre")]//a[@data-type="ep-suivre"]', 'data-ep-id')
obj_url = AbsoluteLink('.//h3[@id and contains(@id,"responsive-prix-mobile")]//a')
obj_cost = CleanDecimal('.//div[has-class("js-block-responsive")]//h4', default=NotAvailable)
obj_currency = Currency.get_currency(u'€')
obj_area = CleanDecimal(Regexp(CleanText('.//ul[has-class("infos")]//li[has-class("surface")]//b'), r'([\d\ ]+)m', default=NotAvailable), default=NotAvailable)
obj_title = CleanText('.//h3[contains(@id, "responsive-prix-mobile")]//a')
obj_text = CleanText('.//p[has-class("resume")]')
obj_rooms = CleanDecimal(Regexp(CleanText('.//ul[has-class("infos")]//li[has-class("rooms")]//b'), r'([\d\ ]+)m', default=NotAvailable), default=NotAvailable)
obj_phone = CleanText('.//span[has-class("tel")]//span[contains(@id, "agence_call")]', default=NotAvailable)
obj_utilities = UTILITIES.UNKNOWN
def obj_photos(self):
return [HousingPhoto(Attr('.//a[has-class("visuel")]//img', 'src')(self))]
class HousingPage(HTMLPage):
@method
class get_housing(ItemElement):
klass = Housing
obj_url = AbsoluteLink('.//a[has-class("actif")]')
obj_id = Attr('//a[has-class("masquer")]', 'data-tpl-id')
obj_cost = CleanDecimal('.//span[@itemprop="price"]', default=NotAvailable)
obj_currency = Attr('//meta[@itemprop="priceCurrency"]', 'content', default=NotAvailable)
def obj_utilities(self):
if CleanText('.//span[has-class("alur")]')(self) == 'Charges comprises':
return UTILITIES.INCLUDED
elif CleanText('.//span[has-class("alur")]')(self) == 'Hors charges':
return UTILITIES.EXCLUDED
else:
return UTILITIES.UNKNOWN
obj_rooms = CleanDecimal('//li[has-class("pieces")]//b', default=NotAvailable)
def obj_photos(self):
photos = []
for photo in self.xpath('.//a[has-class("imageAnnonce")]'):
photos.append(HousingPhoto(Link('.')(photo)))
return photos
obj_area = CleanDecimal('.//li[has-class("surface")]//b', default=NotAvailable)
obj_text = CleanText('.//p[@itemprop="description"]')
obj_phone = CleanText('.//span[@id="agence_call"]')
# -*- coding: utf-8 -*-
# Copyright(C) 2017 Antoine BOSSY
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from weboob.tools.test import BackendTest
class FnaimTest(BackendTest):
MODULE = 'www.fnaim.fr'
......@@ -62,6 +62,7 @@ esalia
explorimmo
feedly
figgo
fnaim
foncia
fortuneo
francetelevisions
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment