def get_list_from_waypoint(wp, typecourse='Escalade'): """ Récupération d'une liste de voie à partir d'un point de passage (waypoint ou wp) Attention on ne récupère que les voies correspondants au type de course souhaité Exemple de wp: https://www.camptocamp.org/waypoints/104212/fr/telepherique-des-grands-montets :param wp: url de waypoint :param typecourse: str du type de course ['Escalade', '?', ...] :return: liste des voies du waypoint """ logger.info("Waypoint fourni! récupération des courses '{}'.".format( typecourse)) listedevoies = list() parser = C2CParser(wp) for h3 in parser.rawsoup.findAll('h3'): try: if h3.text.strip() == typecourse: h3parent = h3.parent for ln in h3parent.find_all('a'): listedevoies.append( str(C2CParser.baseurl + ln.get('href'))) except TypeError as te: logger.error(te) continue logger.info("{} voie(s) récupérée(s) à partir du waypoint.".format( len(listedevoies))) return listedevoies
def test_filtre_doublon(self): logger.info("\nTest Filtrage des doublons...") liste_non_filtree = [ 'https://www.camptocamp.org/routes/53914', 'https://www.camptocamp.org/routes/53914' ] liste_filtree = C2CParser.filter_url(liste_non_filtree) self.assertCountEqual(liste_filtree, self.expected_liste)
def test_filtre_mauvais_baseurl(self): logger.info("\nTest Filtrage mauvais baseurl...") liste_non_filtree = [ 'https://www.camp.org/routes/53914', 'https://www.tocamp.org/routes/53914', 'https://www.camptocamp.org/routes/53914' ] liste_filtree = C2CParser.filter_url(liste_non_filtree) self.assertCountEqual(liste_filtree, self.expected_liste)
def process_list(self): """ Processing de tous les éléments d'une liste avec un thread par voie """ listaparser = C2CParser.init_with_list(self.userinput) with ThreadPoolExecutor(max_workers=nb_workers) as executor: # Start the load operations and mark each future with its URL future_to_url = {executor.submit(self.process_unit, url): url for url in listaparser} for future in as_completed(future_to_url): logger.info("Fin de traitement de {}".format(future_to_url[future]))
def parse(self): """ Wrapper pour l'execution du parser permettant de clore proprement le webdriver """ try: self.execute_parser() except AttributeError: pass finally: logger.info("Nombre de voie parsées : {}".format(self.count_parsed)) logger.info("End of Parsing Program.")
def __init__(self, userinput, backends, update=False): """ Initialisation de la classe de Processing pour le parsing des voies :param userinput: un waypoint, une url de voie ou une liste d'url de voies :param backends: liste des backend de persistence :param update: True pour reparser les voies existantes, False pour skip si dejà existante """ logger.info('Processing avec backends={}, maj={}'.format(backends, update)) self.userinput = userinput self.backends = backends self.update = update self.count_parsed = 0 # compteur de voies parsées
def insert(self, voie, collection='voies'): """ Insertion d'une voie dans MongoDB :param voie: la voie a enregistrer (transformée en dictionnaire pendant l'insertion) :param collection: la collection pour l'enregistrement :return ids : le numero d'enregistrement en base """ logger.info( "Insertion en dans la collection '{}' de la voie : {}".format( collection, voie.url)) ids = self.db[collection].insert_one(voie.to_dict()) return ids
def __init__(self, url, savehtml=False): """ Initialisation du parser. Appelé pour chaque nouvelle url. :param url: l'url à parser :param savehtml: True pour sauver le html des voies dans un fichier (debug) (1 fichier par voie) """ if url is not None: self.driver = init_driver() logger.debug("Init de la classe {}".format(self.__class__)) logger.info("Traitement de {}".format(url)) self.savehtml = savehtml self.urlvoie = url self.rawsoup = self.get_soup_from_url(url)
def get_titre(self): """ Récupération du Titre de la voie :return: le titre de la voie """ logger.debug("Récupération titre de la voie {}".format(self.urlvoie)) soup = self.rawsoup try: titre = str(soup.find('h1').text) logger.info("Titre: {}".format(titre)) return titre.strip() except: logger.error("Pas de titre trouvé")
def filter_url(liste_des_voies): """ Filtre des urls sur le baseurl et suppression des doublons :param liste_des_voies: input :return: dictionnaire de voies uniques et minifiées """ logger.info("Filtrage des voies fournies.") filteredlistvoies = list() for voie in set(liste_des_voies): if C2CParser.get_baseurl(voie) == C2CParser.baseurl: minurl = C2CParser.get_urlvoie(voie) filteredlistvoies.append(minurl) logger.debug(f"Liste voie AVANT filtrage: {liste_des_voies}") logger.debug(f"Liste voie APRES filtrage: {filteredlistvoies}") return dict.fromkeys(filteredlistvoies)
def init_with_list(listedevoies): """ Traitement à partir d'une liste de voies : - Suppression des doublons - Expansion des waypoints (contenant d'autres voies) - Filtrage des urls de route contenant 'routes' :param listedevoies: liste de voie en input :return: liste de voie après traitement (liste) """ liste = list() filteredlist = C2CParser.filter_url(listedevoies) for i, voie in enumerate(filteredlist): if 'waypoints' in voie: ldc = C2CParser.get_list_from_waypoint(voie) liste.extend(ldc) elif 'routes' in voie: liste.append(voie) logger.info("Liste de voie complete : {}".format(liste)) return liste
def get_outings(self): """ Récupération des informations de toutes les sorties d'une voie :return: une liste avec un élément par sortie """ logger.info("Récupération des sorties de {}".format(self.urlvoie)) urls_outing = self._get_urls_outings() # Boucle sur toutes les sorties page par page if urls_outing is None: return list(['aucune sortie']) else: logger.info("Il y a {} sortie(s) pour {}".format( len(urls_outing), self.urlvoie)) list_sorties = list() for out in tqdm(urls_outing): s = self.getouting_unit(out) list_sorties.append(s) return list_sorties
def insert(self): """ Insertion ou update (delete puis insert) d'une voie :return: l'id d'insertion en base """ logger.info("Insertion en Base de la voie : {}".format(self.url)) session = Session() idvoie = VoieDb.exists(self.url) if idvoie: logger.info("Suppression de l'id : {}".format(idvoie)) session.query(VoieDb.id).filter_by(id=idvoie).delete() # Ajout en base session.add(self) session.commit() # Récupération de l'id de l'insertion session.refresh(self) logger.info('Insertion en Base Id: {}'.format(self.id)) return self.id
def process_unit(self, urlvoie): """ Processing unitaire d'une voie :param urlvoie: url de la voie à Parser :return: 0 si pas de parsing, 1 si parsing. """ urlmin = C2CParser.get_urlvoie(urlvoie) exist = InterfaceDAO.check_exists(urlmin, self.backends) # Si la voie n'existe pas ou si nous sommes en mode update if not exist or self.update: logger.info("Voie: {} existante id: {}, mode Update: {}".format(urlvoie, exist, self.update)) parser = C2CParser(urlmin) voie = Voie.from_c2cparser(parser) InterfaceDAO.persistance_voie(voie, self.backends) logger.info(voie) self.count_parsed = self.count_parsed + 1 voie = None return 1 else: logger.info("Voie déjà existante: {} et aucun reparsing demandé".format(urlmin)) return 0
# -*- coding: utf-8 -*- """ ------------------------------------------------------------------------------------------------------ Création et Initialisation de la Database Sqlite Auteur : SDI Date : 29/09/2019 Objectif : educationnal purpose only. Merci de respecter les copyrights. Le script est executé par le __init__ du package Si et Seulement Si le chemin d'execution est le même que le chemin racine du package (pour éviter la création dans les dossiers test/ , DAO/ ...) ------------------------------------------------------------------------------------------------------ """ import os from camptocamp import Base, engine, sqlitedb_filename, logger from camptocamp import ROOT_DIR # Import nécessaire pour la création du modèle en database from camptocamp.DAO.db_model import VoieDb logger.info("Projet ROOT DIR: {}".format(ROOT_DIR)) logger.info("Execution DIR : {}".format(os.getcwd())) # Creation de la database SQLite uniquement dans le ROOT_DIR du projet if ROOT_DIR == os.getcwd(): logger.info("Check de l'existence de la database sqlite.") if os.path.isfile(sqlitedb_filename): logger.info('la database sqlite existe. Rien à faire') else: logger.warning("database sqlite n'existe pas: création...") Base.metadata.create_all(engine) logger.info("Fin de Creation de la db.")
:param collection: la collection à afficher :param limit: le nombre d'enregistrements max """ print('Total Record for the collection: ' + str(self.db.voies.count())) for record in self.db[collection].find().limit(limit): pprint(record) # -------------------------------------------------------------------------- # Execution du main # -------------------------------------------------------------------------- if __name__ == '__main__': mongo = MongoDbDAO() logger.info("Insertion en base d'une voie de test") voietest = { 'nom': 'nom de la voie de test', 'diff': '6a', 'commentaires': 'un commentaire 2', 'rating': 5 } res = mongo.db.voies_test.insert_one(voietest) logger.info("Affichage voie de test rating=5 :") fivestar = mongo.db.voies_test.find_one({'rating': 5}) print(fivestar) logger.info("Affichage de tous les éléments dans voies") mongo.query_all(collection='voies')
def test_get_baseurl(self): logger.info("\nTest fonction get_baseurl()...") baseurl = C2CParser.get_baseurl( 'https://www.camptocamp.org/routes/53914') self.assertEqual(baseurl, self.expected_baseurl)