def get_soup_from_url(self, url, timeout=10, sleep_sec=0.5): """ Récupération du parsing soup d'une url pour la recherche des éléments :param url: url :param timeout: timeout pour le chargement :param sleep_sec: délai entre chargement des différentes pages :return: objet BeautifulSoup """ logger.debug("Récuperation Beautiful soup de la page {}".format(url)) # https://stackoverflow.com/questions/26566799/wait-until-page-is-loaded-with-selenium-webdriver-for-python # https://selenium-python.readthedocs.io/waits.html try: self.driver.set_page_load_timeout(timeout) self.driver.get(url) time.sleep(sleep_sec) html = self.driver.page_source if self.savehtml: filename = url.replace(C2CParser.baseurl, '') filename = filename.replace('/', '_').replace( '?', '_').replace('=', '_') + '.html' with open(filename, 'w', encoding='utf-8') as f: f.write(html) soup = BeautifulSoup(html, 'lxml') return soup except TimeoutException as ex: logger.warning("Exception Timeout : {} ".format(ex))
def get_commentaires(self): """ Récupération des commentaires d'une voie :return: string contenant les commentaires """ logger.debug("Récupération des commentaires") commentaires = list() for exp in self.rawsoup.find_all( 'div', {'class': 'content markdown-content'}): for comment in exp.find_all('p'): commentaires.append(comment.text) return " ".join(commentaires)
def get_details_longueurs(self): """ Récupération des détails des longueurs d'une voie :return: liste avec une entrée par longueur """ logger.debug("Récupération détails des longueurs.") details = list() for exp in self.rawsoup.find_all( 'div', {'class': 'content markdown-content'}): for detail in exp.find_all('tr'): details.append(str(detail.text).replace('\n', ' ')) return details
def __init__(self, url_connection=mongodburl, database='camptocamp'): """ Constructeur pour intialisation une connection MongoDB sur une collection :param url_connection: endpoint de connection vers MongoDb :param database: nom de la Database """ try: logger.debug( "Connection MongoDb en cours: {}".format(url_connection)) self.client = MongoClient(url_connection) # Docker IP self.db = self.client[database] except ServerSelectionTimeoutError as err: logger.error("Erreur Connection MongoDB {}".format(err))
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 check_exists(urlvoie, backends): """ Vérification si une voie existe dans le backend en question :param urlvoie: l'url de la voie (son identifiant unique) :param backends: liste des backends dans lesquels faire la vérification :return: true si la voie existe, false si elle n'existe pas """ logger.debug("Verification existance de la voie dans les backends: {}".format(backends)) if 'db' in backends: return VoieDb.exists(urlvoie) if 'pickle' in backends: pickle = PickleDAO() return pickle.exists(urlvoie) if 'mongodb' in backends: return mongodb.exists_voie(urlvoie)
def execute_parser(self): """ Execution du parser selon les inputs fournies par l'utilisateur :return: """ if isinstance(self.userinput, list): logger.debug(" Parsing d'une liste en input") self.process_list() # Il faut Gérer le cas de figure ou la chaine fournie est un waypoint ;-) elif isinstance(self.userinput, str): logger.debug(" Parsing d'une chaine en input => conversion en list") liste = list() liste.append(self.userinput) self.userinput = liste self.process_list()
def persistance_voie(voie, backends): """ Interface permettant d'insérer dans le backend la voie :param voie: Objet 'Voie' à insérer :param backends: liste des backends pour insertion :return: """ logger.debug("Insertion de la voie dans les backends {}".format(backends)) if 'db' in backends: voiedb = VoieDb.from_voie(voie) voiedb.insert() if 'pickle' in backends: pickle = PickleDAO() pickle.insert(voie) if 'mongodb' in backends: mongodb.insert(voie)
def __init__(self, url, titre, area, approche, longueurs, difficultes, commentaires, cotations, sorties): """ Initialisation de la classe Voie avec tous ses attributs :param url: l'url unique de la voie (version minifiée: ie 'baseurl/routes/54788') (String) :param titre: le titre de la voie (ie 'Presles - Buis : Point trop n'en faut') (String) :param area: localisation de la voie (liste) :param approche: le dictionnaire de l'approche de la voie :param longueurs: la description des longueurs de la voie (liste) :param difficultes: la description des difficultés de la voie (dictionnaire) :param commentaires: les commentaires (String) :param cotations: les cotations de la voie (dictionnaire) :param sorties: les sorties relatives (liste) """ logger.debug("Init de la classe {}".format(self.__class__)) self.url = url self.titre = titre self.area = area self.approche = approche self.longueurs = longueurs self.difficultes = difficultes self.commentaires = commentaires self.cotations = cotations self.sorties = sorties
def getouting_unit(self, unesortie): """ Récupération des informations d'une sortie :param unesortie: url de la sortie :return: """ # Chargement de la nouvelle page logger.debug(" => {}".format(unesortie)) soup_outing = self.get_soup_from_url(self.baseurl + unesortie) try: # Date de la sortie date_sortie = soup_outing.find('span', attrs={ 'class': 'outing-date is-size-5' }).text.strip() # Description de la sortie details = "-- " + date_sortie + " -- " for desc_sortie in soup_outing.find_all( 'div', {'class': 'content markdown-content'}): details += "\n" + desc_sortie.text except AttributeError as err: logger.warning("Exception: {}".format(err)) else: return details
def get_approche(self): """ Récupération des approches d'une voie :return: dictionnaire avec les clés 'approche', 'itineraire', 'descente' """ logger.debug("Récupération approche de la voie") soup = self.rawsoup # approche, itineraire et descente: # Ce sont les éléments qui suivent les balises <h3> avec id=[liste_id] approche = dict() liste_id = ['approche', 'itineraire', 'descente'] # https://stackoverflow.com/questions/42809252/beautifulsoup-get-tags-after-p-tag-after-h3-and-br-tag-between-p for iddesc in liste_id: try: header = soup.find('h3', {'id': iddesc}) nextnode = header.nextSibling.nextSibling approche[iddesc] = nextnode.get_text(strip=True) except AttributeError: logger.debug("pas de {} trouvé.".format(iddesc)) logger.debug(approche) return approche
def get_alt_difficultes(self): """ Récupération des hauteurs des difficultés :return: dictionnaire des difficultés """ logger.debug("Récupération des difficultés de la voie.") difficultes = dict() for diff in [ 'Altitude maximale', 'Dénivelé positif', 'Dénivelé des difficultés' ]: try: # Recherche du texte puis du parent et on avance à la balise suivante. address = self.rawsoup.find(text=diff) parent_tag = address.parent val = parent_tag.find_next().text # Remplacement car. spéciale par un espace difficultes[diff] = val.replace(u'\xa0', u' ') except AttributeError: logger.debug("Impossible de récupérer : {}".format(diff)) continue logger.debug(difficultes) return difficultes
def get_cotations(self): """ Récupération des différentes cotations relatives à une voie :return: dictionnaire des cotations """ logger.debug("Récupération des cotations de la voie") # Initialisation d'une liste des attributs à récupérer pour la cotation de la voie cotations = dict() liste = [ 'Cotation globale', 'Cotation libre', 'Cotation obligatoire', 'Cotation obligatoire', 'Engagement', 'Qualité de l\'équipement', 'Exposition rocher' ] for cote in liste: try: logger.debug("{} : {}".format( cote, self.rawsoup.find('span', title=cote).text.strip())) cotations[cote] = self.rawsoup.find('span', title=cote).text.strip() except AttributeError: pass logger.debug(cotations) return cotations