[PYTHON] Re: La vie Heroku à partir de zéro avec Flask ~ Selenium & PhantomJS & Beautifulsoup ~

introduction

Pour la première fois, j'ai utilisé Flask et Heroku of Python pour créer une API qui renvoie des informations récupérées avec json, je voudrais donc résumer la méthode utilisée à ce moment-là.

Ceux utilisés par heroku jusqu'à Hello World et la construction de l'environnement de Python sont la première partie. Re: Heroku life - Environment start with Flask from zero and the Hello World ~ À Ce sera la deuxième partie jusqu'à ce que le programme à créer cette fois soit déployé sur Heroku Re: Life in Heroku à partir de zéro avec Flask ~ PhantomJS à Heroku ~ Puisqu'il est écrit, veuillez également consulter

Que faire cette fois

Si vous étudiez, vous pouvez réinventer les roues

Cette fois, j'écrirai comment gratter en utilisant Selenium et PhantomJS avec SlideShare comme sujet.

*** Comme il est devenu long lorsque je l'ai rassemblé dans un article, le flux de déploiement vers Heroku est divisé en une deuxième partie. *** ***

Mouvement spécifique

[HerokuURL] / api / [Mot de recherche] / [Nombre de pages] Exemple: ~ herokuapp.com/api/python/2 Lorsque vous accédez

  1. PhantomJS fonctionne sur Heroku et ouvre la page de recherche Slideshare

  2. Entrez le [Mot de recherche] de l'URL dans le champ de recherche de la page de recherche à rechercher.

  3. Modifiez le paramètre de langue des résultats de recherche en japonais

  4. Extraire les informations des diapositives dans les pages Web en grattant スクリーンショット 2016-10-17 22.10.28.png

  5. Cliquez sur Suivant sur le pager sous le nombre de pages de l'URL et répétez le scraping. スクリーンショット 2016-10-17 22.22.28.png

  6. Après avoir gratté, mettez-le au format json et lancez-le!

Je voudrais créer une API qui fait cela.

Ce que j'ai utilisé cette fois

Préparation

Construction de l'environnement à faire avant cette heure

Re: Heroku life - Environment start with Flask from zero and the Hello World ~ Installé au moment où vous faites Hello World Veuillez inclure *** Flask *** et *** Gunicorn *** Veuillez préparer votre environnement préféré tel que pyenv-virtualenv.

Construire un environnement supplémentaire pour faire cette fois

PhantomJS Placez PhantomJS localement pour vérifier l'opération localement avant de l'exécuter sur Heroku. Je pense qu'il est normal de reconnaître que le navigateur n'a pas d'interface graphique pouvant être utilisée à partir du code. Référence: Essayez différentes choses avec PhantomJS

$  brew install phantomjs

Selenium Il semble que ce soit un outil de test d'interface utilisateur multi-navigateurs et multi-plateformes. Avec le grattage normal, vous ne pouvez faire que ce qui est affiché à l'URL spécifiée, mais en utilisant le sélénium, vous pouvez appuyer sur le bouton pour aller à la page suivante ou entrer des caractères et appuyer sur le bouton de recherche. sensationnel

C'est Ruby, mais c'est un article utile: Test d'automatisation de l'interface utilisateur Web - Essayez avec Selenium

$ pip install selenium

beautifulsoup Il est utilisé lors du traitement des données de page Web acquises. Référence: Scraping with Python and Beautiful Soup

$ pip install beautifulsoup4

lxml Utilisé en combinaison avec une belle soupe.

$ pip install lxml

Un gars qui évite les contraintes interdomaines avec les cors

Même si vous créez une API qui renvoie normalement Json, il est gênant d'utiliser une API qui n'a pas été traitée dans Chrome en raison de restrictions interdomaines, je prendrai donc des mesures. Je vais mettre un lien dans l'explication du code.

$ pip install -U flask-cors

Ce qui a été fait

https://github.com/ymgn/SlideShare-API

api.py


# -*- coding: utf-8 -*-

import json
#Grattage requis à partir d'ici
from bs4 import BeautifulSoup
#De là, vous devez utiliser le navigateur avec du sélénium
from selenium import webdriver 
from selenium.webdriver.common.keys import Keys #Utilisé lors de la saisie de caractères

#Quantité de flacon requise à partir d'ici
import os
from flask import Flask

#À partir de là pour la contrainte inter-domaines lors de l'utilisation de cors en définissant ajax avec flask
from flask_cors import CORS, cross_origin
app = Flask(__name__)
CORS(app)

@app.route('/')
def index():
    return "Comment utiliser: /api/Mot à rechercher/Nombre de pages acquises"

@app.route('/api/<string:word>/<int:page>') #Rechercher un mot/Recevoir le nombre de pages du chemin vers la variable
def slide(word,page):
 
    driver = webdriver.PhantomJS() #Utilisez PhantomJS
    driver.set_window_size(1124, 850) #Spécifiez la taille de PhantomJS
    driver.implicitly_wait(20) #Si l'élément spécifié n'existe pas, le pilote attendra automatiquement jusqu'à 20 secondes jusqu'à ce qu'il sorte.

    URL = "http://www.slideshare.net/search/"
    driver.get(URL) #Accéder à l'URL de partage de diapositives
    data_list = [] #Un tableau qui collecte des données pour toutes les pages

    search = driver.find_element_by_id("nav-search-query") #Obtenir un élément de champ de recherche
    search.send_keys(word) #Entrez un mot de recherche
    search.submit() #Soumettre la recherche

    lang = driver.find_element_by_xpath("//select[@id='slideshows_lang']/option[@value='ja']") #Extraire la partie japonaise de la liste de sélection des langues
    lang.click() #Sélectionnez le japonais comme sélection de langue

    for i in range(0,page): 
        print(str(i+1) + u"Page de la page")
        data = driver.page_source.encode('utf-8') #Utf les informations dans la page-Préparez-vous en 8
        soup = BeautifulSoup(data,"lxml") #Faites-le au format lxml pour un traitement facile
        slide_list = soup.find_all("div",class_="thumbnail-content") #Extrait par diapositive
        for slide in slide_list:
            slide_in = {} #Organiser les informations sur les diapositives au format dictionnaire
            
            #Obtenez le nom de l'affiche de la diapositive
            name = slide.find("div",class_="author").text
            slide_in["name"] = name.strip() # strip()Élimine les blancs et les sauts de ligne aux deux extrémités
            
            #Obtenez le titre de la diapositive
            title = slide.find("a",class_="title title-link antialiased j-slideshow-title").get("title") #Balise spécifiée&Émettre un titre dans la classe
            slide_in["title"] = title

            #Obtenez le lien de la diapositive
            link = slide.find("a",class_="title title-link antialiased j-slideshow-title").get("href") #Balise spécifiée&Émettre href en classe
            slide_in["link"] = "http://www.slideshare.net" + link
            
            #Obtenir le lien de la miniature de la diapositive
            imagetag = slide.find("a",class_="link-bg-img").get("style") #Balise spécifiée&Mettez le style dans la classe
            image = imagetag[imagetag.find("url(")+4:imagetag.find(");")] #Retirez les pièces inutiles
            slide_in["image"] = image
            
            #Obtenez des diapositives et des likes, qui sont le nombre de pages de diapositives
            info = slide.find("div",class_="small-info").string #Obtenez les chaînes de diapositives et de likes
            slides = info[7:info.find("slides")] #Extraire la partie diapositives
            slide_in["slides"] = slides.strip() # strip()Élimine les blancs et les sauts de ligne aux deux extrémités
            if "likes" in info:
                likes = info[info.find(", ")+2:info.find("likes")] #Extraire la partie likes
            else:
                likes = "0"
            slide_in["likes"] = likes.strip() # strip()Élimine les blancs et les sauts de ligne aux deux extrémités

            data_list.append(slide_in) # data_Résumer le contenu d'une page dans la liste

        driver.execute_script('window.scrollTo(0, 3000)') #Descendre avec téléavertisseur
        next = driver.find_element_by_xpath("//li[@class='arrow']/a[@rel='next']") #Extraire l'élément NEXT du pager
        next.click() #Cliquez sur le bouton Suivant

    driver.close() #Terminer le fonctionnement du navigateur
    jsonstring = json.dumps(data_list,ensure_ascii=False,indent=2) #Sortie du tableau créé au format json
    return jsonstring
 
#Déterminez si vous frappez avec bash ou insérez avec l'importation
if __name__ == '__main__':
    app.run()

Commentaire de code

J'ai également écrit des commentaires dans le code, mais je voudrais expliquer les parties importantes du haut. L'importation est telle qu'elle est écrite, donc omise CORS Les programmes qui utilisent des API ne fonctionnent pas sur Chrome, etc.! Je pense que vous avez l'expérience. Puisque nous créons une API avec beaucoup d'efforts, prenons des mesures.

from flask_cors import CORS, cross_origin

app = Flask(__name__)
CORS(app)

Il semble que si vous écrivez, cela prendra des mesures. Benri Référence: https://flask-cors.readthedocs.io/en/latest/

Prendre un argument du chemin Flask

Si vous écrivez dans la route de Flask et écrivez le nom de la variable entre () de def, vous pouvez recevoir le contenu du chemin comme argument.

@app.route('/api/<string:word>/<int:page>') #Rechercher un mot/Recevoir le nombre de pages du chemin vers la variable
def slide(word,page):

Référence: Let's master Flask

Déterminez la taille du navigateur de PhantomJS

driver.set_window_size(1124, 850)

Si vous ne décidez pas de la taille du navigateur, vous ne pourrez pas prendre des éléments ou bien faire défiler. La raison est inconnue car la valeur numérique de la taille est telle qu'elle a été écrite lorsque je l'ai vérifiée.

En attente de la lecture de l'élément

driver.implicitly_wait(20)

En écrivant comme ceci, lorsque vous spécifiez l'ID et la classe de driver.find ~~ '' et que vous récupérez et utilisez l'élément, attendez jusqu'à 10 secondes et exécutez immédiatement lorsque la lecture est terminée. Ce sera dans un état pratique à faire. C'est très pratique lorsque vous utilisez du sélénium car vous n'avez pas à attendre explicitement le temps d'attente attendu tel que time.sleep (3) ''. Référence: Il est écrit dans la section Attentes implicites de ce site

Entrez le texte dans la zone de formulaire et soumettez

search = driver.find_element_by_id("nav-search-query") #Obtenir un élément de champ de recherche
search.send_keys(word) #Entrez un mot de recherche
search.submit() #Soumettre la recherche

Après avoir récupéré l'élément d'entrée, etc. par id depuis le navigateur, vous pouvez entrer la valeur avec `send_keys (" hoge ")` etc. Si l'élément est dans un formulaire, vous pouvez le soumettre en ajoutant `` .submit () ''.

Sélectionnez la liste déroulante

lang = driver.find_element_by_xpath("//select[@id='slideshows_lang']/option[@value='ja']") #Extraire la partie japonaise de la liste de sélection des langues
lang.click() #Sélectionnez le japonais comme sélection de langue

Cette fois, la méthode de spécification d'élément est spécifiée par XPATH au lieu de id ou de classe. La raison en est que lors de la sélection d'un élément enfant qui a plusieurs identifiants ou que seul le parent a un identifiant, il est nécessaire de le spécifier dans une partie autre que id et classe.

Au fait, si vous ne passez pas au japonais comme celui-ci, même si vous obtenez le japonais localement, le côté Heroku obtiendra les diapositives de toutes les langues.

Lorsqu'il y a plusieurs identifiants

lang = driver.find_elements_by_id("slideshows_lang")
lang[1].find_elements_by_tag_name("option")
#Lors de l'extraction de plusieurs, ce sera d'élément en éléments

Si seulement le parent a un identifiant

lang = driver.find_element_by_id("slideshows_lang")
lang.find_element_by_tag_name("option")

Veuillez vous référer à la référence pour savoir comment écrire XPATH et d'autres méthodes d'extraction. Référence: Locating Elements

Préparez-vous pour le grattage

data = driver.page_source.encode('utf-8') #Utf les informations dans la page-Préparez-vous en 8
soup = BeautifulSoup(data,"lxml") #Faites-le au format lxml pour un traitement facile

Après avoir encodé les données de page du site Web obtenues par webdriver avec utf-8, utilisez lxml qui est compatible avec Beautiful Soup pour le rendre facile à gratter. Je l'ai mis car il doit être chargé à chaque fois que la page change.

faire défiler

driver.execute_script('window.scrollTo(0, 3000)') #Descendre avec téléavertisseur

Vous pouvez maintenant faire défiler PhantomJS vers le bas de 3000 pixels avec JavaScript. Si PhantomJS n'a pas d'interface graphique, le défilement n'a pas de sens, non? Vous pourriez penser, mais si vous ne faites pas défiler, vous obtiendrez une erreur. Je voulais aller en bas car je l'ai réglé sur 3000, donc je l'ai réglé sur 3000 pour le moment.

Mesures par rapport à une balise qui n'a ni id ni classe sous plusieurs noms de classe

next = driver.find_element_by_xpath("//li[@class='arrow']/a[@rel='next']") #Extraire l'élément NEXT du pager
next.click() #Cliquez sur le bouton Suivant

Quand j'ai essayé d'appuyer sur Suivant sur la partie pager de Slideshare, Précédent et Suivant avaient class = "arrow" , et la balise a ne contenait ni id ni classe. .. Depuis que j'ai écrit rel = "next" dans la balise a de l'élément enfant, cette partie est définie sur XPATH qui peut être spécifiée, y compris le parent et rel.

Convertir le tableau créé au format json

jsonstring = json.dumps(data_list,ensure_ascii=False,indent=2) #Sortie du dictionnaire créé au format json
return jsonstring
json.dumps(Tableau,Données du dictionnaire,Faux si le japonais est inclus,Organiser par retrait)

Si vous passez un tableau ou un dictionnaire, il sera au format json. Le retrait est facultatif, et si vous définissez indent = 2, il sera mis en retrait avec deux espaces demi-largeur pour le rendre plus facile à voir. Référence: [Python] Handle JSON

Flux de la confirmation du mouvement au déploiement sur Heroku

Lancez Flask localement et vérifiez

Supposons que vous ayez tout ce dont vous avez besoin installé.

Préparation du dossier et du flacon

$ mkdir slide
$ cd slide

$ touch api.py Procfile
#Créer un fichier pour écrire le fichier et les paramètres du flacon

Fichiers requis lors du lancement de Flask

Procfile

web: gunicorn hello:app --log-file=-

api.py


Voir au dessus

Tout d'abord, vérifions le mouvement avec Flask

$ python api.py
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

http://127.0.0.1:5000/api/python/2 Accédez avec ce chemin et obtenez deux pages recherchées par python sur slideshare. résultat スクリーンショット 2016-10-16 17.52.34.png Dans Safari, json ressemble à une liste, mais dans Chrome, vous pouvez le voir magnifiquement en insérant JSON View, etc.

référence

Diverses bibliothèques pouvant être utilisées pour l'exploration et le scraping en Python

Déployer sur Heroku

Le flux de déploiement vers Heroku est la deuxième partie Re: Life in Heroku à partir de zéro avec Flask ~ PhantomJS à Heroku ~ Puisqu'il est écrit, merci.

Épilogue

Pour le moment, opérations de base du navigateur Selenium (saisie de caractères, soumission, sélection de liste déroulante, clic d'élément, spécification XPATH) et scraping (texte, image, URL, traitement de chaîne de caractères et jsonisation) Je pense que j'ai été capable d'écrire comment le faire, alors j'espère que cela aidera quelqu'un.

Nous vous serions reconnaissants de bien vouloir signaler des améliorations ou des erreurs dans la section des commentaires. Twitter:@ymgn_ll

Recommended Posts

Re: La vie Heroku à partir de zéro avec Flask ~ Selenium & PhantomJS & Beautifulsoup ~
Re: La vie d'Heroku à partir de zéro avec Flask ~ PhantomJS à Heroku ~
Re: La vie Heroku commence avec Flask from zero - Environnement et Hello world -
Efficacité commerciale à partir de zéro avec Python
Sélénium, Phantomjs et BeautifulSoup4
Micro service avec GCP sur RoR à partir de zéro
Apprentissage automatique à partir de zéro (apprentissage automatique appris avec Kaggle)
[Tweepy] Re: Développement de Twitter Bot à partir de zéro # 1 [python]
De la construction de l'environnement au déploiement pour flask + Heroku avec Docker
Re: Vie de programmation compétitive à partir de zéro Pour que les débutants puissent obtenir des performances encore un peu plus élevées ~ ABC154 ~ 156 avec impressions ~
Re: Durée de vie de la programmation compétitive à partir de zéro Chapitre 1.3 "Side tea"
Django à partir de zéro (partie: 2)
Django à partir de zéro (partie: 1)
Grattage au sélénium en Python
Touch Flask + courir avec Heroku
Grattage avec du sélénium en Python
La vie PySpark à partir de Docker
Re: Vie de programmation compétitive à partir de zéro Chapitre 1.2 "Python of tears"