[Python] Créez un robot d'exploration Web basé sur les événements à l'aide de l'architecture sans serveur d'AWS

Essayez de créer un robot d'exploration WEB sans serveur et basé sur les événements

Maintenant que j'étudie Elastic Search, je me suis demandé si je pouvais faire quelque chose avec ES, j'ai donc écrit un robot d'exploration Web basé sur les événements utilisant Kinesis + Lambda.

Flux de travail

serverless-crawler.png

Le flux général est le suivant.

  1. Extrayez l'URL avec Scrapy (Scraping Hub ou AWS Lambda) et placez-la dans le flux Kinesis
  2. Lancez AWS Lambda depuis le flux Kinesis
  3. Patrouille de la fonction Lambda à l'URL et envoi des données à Elastic Search Service

Créer un utilisateur IAM

Étant donné que vous avez besoin d'une autorisation pour utiliser Kinesis et Elastic Search, Avoir ** ID de clé d'accès ** et ** clé d'accès secrète ** pour chacun.

De plus, l'ARN de l'utilisateur est également requis, alors gardez-le à l'esprit (arn: aws: iam :: ********: user / ********)

Créer un flux avec AWS Kinesis

Commencez par créer un flux Kinesis.

  1. Créez un flux
  2. Entrez un nom de flux approprié (provisoirement scraping_url)
  3. Entrez le nombre de fragments (1 pour le moment)
  4. Créer un flux スクリーンショット 2017-04-06 12.45.52.png

Créer un service AWS ElasticSearch

Ensuite, créez un ES avec Amazon Elastic Search Service.

Fonctionnement sur AWS

  1. Create a new domain

  2. Entrez un nom de domaine approprié dans le champ Nom de domaine Elasticsearch (provisoirement archives Web)

  3. Sélectionnez [5.1] pour la version Elasticsearch. Appuyez sur [Suivant] スクリーンショット 2017-04-06 13.06.02.png

  4. Définissez le type d'instance sur [t2.small] dans Configurer le cluster. Appuyez sur Suivant (pour tester, donc avec une petite instance) スクリーンショット 2017-04-06 13.07.46.png

  5. Sélectionnez Autoriser ou refuser l'accès à un ou plusieurs comptes AWS ou utilisateurs IAM dans la stratégie d'accès Configurer スクリーンショット 2017-04-06 13.18.13.png

  6. Entrez l'ARN de l'utilisateur que vous souhaitez autoriser dans l'ID de compte ou l'ARN * スクリーンショット 2017-04-06 13.18.51.png

  7. Créer avec [Confirmer et créer]

  8. Après un certain temps, ES démarrera, vérifiez donc [Endpoint] et gardez-le à l'esprit car il sera utilisé dans Lambda. スクリーンショット 2017-04-06 13.30.18.png

Indexer et mapper vers Elastic Search

Créez des données de mappage pour enregistrer l'URL, le titre et le contenu de l'article pour le stockage des articles.

mapping.json


{
    "mappings": {                                            
        "article": {
            "properties" : {
                "url" : {
                    "type": "string",
                    "index" : "not_analyzed"
                },
                "title" : {
                    "type": "string",
                    "index" : "analyzed"
                },
                "contents" : {
                    "type": "string",
                    "index" : "analyzed"
                }
            }
        }   
    }
}

Ensuite, créez les données de mappage ci-dessus et un script pour créer un index.

Installez les packages suivants localement à l'avance
$ pip install requests_aws4auth elasticsearch

es-mapping.py


# -*- coding: utf-8 -*-
import elasticsearch
from requests_aws4auth import AWS4Auth
import json

if __name__ == '__main__':
    #Spécifiez le point de terminaison ES
    host='search-***************.ap-northeast-1.es.amazonaws.com'
    awsauth = AWS4Auth(
            #ID de clé d'accès utilisateur AWS et clé d'accès secrète
            'ACCESS_KRY_ID',
            'SECRET_ACCESS_KEY',
            'ap-northeast-1', 'es')

    es = elasticsearch.Elasticsearch(
            hosts=[{'host': host, 'port': 443}],
            http_auth=awsauth,
            use_ssl=True,
            verify_certs=True,
            connection_class=elasticsearch.connection.RequestsHttpConnection
            )

    f = open('mapping.json', 'r')
    mapping = json.load(f)

    es.indices.create(index='website')                    
    es.indices.put_mapping(index='website', doc_type='article', body=mapping['mappings'])

$ python es-mapping.py
Lorsque vous exécutez le script, il doit être indexé sur AWS ES. スクリーンショット 2017-04-06 15.32.52.png

Créer AWS Lambda

Maintenant que nous avons créé ElasticSearch, il est temps de créer la fonction Lambda.

Créer une fonction Lambda

Créez une fonction Lambda localement. $ mkdir web_crawler
$ cd web_crawler
$ vim lambda_function.py

lambda_function.py



# -*- coding: utf-8 -*-                    
import os
import base64
from readability import Document
import html2text
import requests
import elasticsearch
from elasticsearch import helpers
from requests_aws4auth import AWS4Auth

def lambda_handler(event, context):
    host = os.environ['ES_HOST']
    #Utiliser le rôle IAM pour s'authentifier auprès d'ElasticSearch Service
    awsauth = AWS4Auth(
            os.environ['ACCESS_ID'],
            os.environ['SECRET_KEY'], 'ap-northeast-1', 'es')

    es = elasticsearch.Elasticsearch(
            hosts=[{'host': host, 'port': 443}],
            http_auth=awsauth,
            use_ssl=True,
            verify_certs=True,
            connection_class=elasticsearch.connection.RequestsHttpConnection
    )

    articles = []

    #Obtenir des événements de Kinesis Stream
    for record in event['Records']:
        payload = base64.b64decode(record['kinesis']['data'])
        try:
            response = requests.get(payload)
            if response.ok:
                article = Document(response.content).summary()
                titleText = html2text.html2text(Document(response.content).title())
                contentsText = html2text.html2text(article)
                res = es.search(index="website", body={"query": {"match": {"url": payload}}})
                #L'URL est-elle déjà enregistrée dans ES?
                if res['hits']['total'] is 0:
                    doc = {
                        'url': payload,
                        'title': titleText.encode('utf-8'),
                        'contents': contentsText.encode('utf-8')
                    }
                    articles.append({'_index':'website', '_type':'scraper', '_source':doc})
        except requests.exceptions.HTTPError as err:
            print("HTTPError: " + err)                                                                                                                                                       
    # Bulk Insert
    helpers.bulk(es, articles)

Après avoir créé la fonction Lambda, installez les bibliothèques requises dans la même hiérarchie
$ pip install readability-lxml html2text elasticsearch requests_aws4auth requests -t /path/to/web_crawler
Durcir avec zip
$ zip -r web_crawler.zip .

Déployer des fonctions Lambda sur AWS

  1. [Créer une fonction Lambda]

  2. Sélectionnez [Fonction vierge]

  3. Sélectionnez le flux Kinesis créé précédemment dans [Paramètres de déclenchement].

  4. [Taille du lot] est d'environ 10

  5. [Position de départ] est une coupe horizontale

  6. Vérifiez l'activation de la gâchette スクリーンショット 2017-04-06 13.51.27.png

  7. Entrez [Nom] dans [Paramètres de fonction](provisoirement WebCrawler ici)

  8. Sélectionnez Python 2.7 pour Runtime

  9. Sous Type d'entrée de code, sélectionnez Télécharger un fichier .ZIP

  10. Spécifiez le fichier zip créé précédemment à partir de [Function Package]

  11. Définissez [Variables d'environnement] sur 3 pour accéder à Elastic Search.

  12. Accédez à l'ID de clé dans ACCESS_ID

  13. Clé d'accès secrète à SECRET_KEY

  14. Point de terminaison Elastic Search sur ES_HOST スクリーンショット 2017-04-06 16.24.30.png

  15. [Handler] reste lambda_function.lambda_handler

  16. Créez les rôles appropriés

  17. Réglez [Délai] dans [Paramètres détaillés] sur environ 2 minutes.

  18. [Créer une fonction]

Extraire l'URL avec Scrapy et l'envoyer au flux Kinesis

Ensuite, dans la dernière étape, utilisez Scrapy pour extraire l'URL de la page de liste et envoyer les données au flux Kinesis.

La page de liste utilise l'entrée chaude de Hatena Bookmark. RSS semble être plus facile à obtenir des données si vous utilisez Scrapy, mais j'ai osé gratter la page Web. Scrapy est un framework utile et puissant pour créer des robots d'exploration avancés, donc si vous êtes intéressé, n'hésitez pas à le toucher.

Créer un projet

Installez d'abord Scrapy
$ pip install scrapy
$ scrapy startproject hotentry
$ vim hotentry/hotentry/spiders/hotentry.py
Entrez le code ci-dessous.

hotentry.py


# -*- coding: utf-8 -*-
import scrapy
from scrapy.conf import settings
import boto3
import json

kinesis = boto3.client(
        'kinesis',                                                                                                                                                                           
        aws_access_key_id=settings['AWS_ACCESS_KEY_ID'],
        aws_secret_access_key=settings['AWS_SECRET_ACCESS_KEY'],
        region_name='ap-northeast-1')

class HotEntrySpider(scrapy.Spider):
    name = "hotentry"
    allowed_domains = ["b.hatena.ne.jp"]
    start_urls = ['http://b.hatena.ne.jp/hotentry/general']

    def parse(self, response):
        for sel in response.css("li.hb-entry-unit-with-favorites"):
            url = sel.css("a.entry-link::attr('href')").extract_first()
            if url is None:
                continue
            kinesis.put_record(
                    StreamName = "scraping_url",
                    Data = sel.css("a.entry-link::attr('href')").extract_first(),
                    PartitionKey = "scraper"
            )

$ vim hotentry/hotentry/settings.py
Ajoutez l'ID de la clé d'accès et la clé d'accès secrète à settings.py

AWS_ACCESS_KEY_ID = 'AKI******************'
AWS_SECRET_ACCESS_KEY = '************************************'

Vous pouvez maintenant METTRE dans le flux Kinesis. Essayons d'exécuter ce code.
$ scrapy crawl hotenty

Vous devriez maintenant être en mesure de remplir les données avec "Scrapy-> Kinesis-> AWS Lambda-> Elastic Search".

Déployer Scrapy vers Scraping Hub

J'ai pu extraire l'URL avec Scrapy et l'envoyer à Kinesis, mais tel quel, ce sera un lot local, alors déployez le code Scrapy sur un service cloud appelé Scrapinghub.

Veuillez consulter l'article suivant pour savoir comment l'installer.

C'est facile à faire de l'enregistrement des utilisateurs au déploiement, je vais donc le décomposer.

finalement

Au départ, j'ai utilisé SQS et DynamoDB et divisé la fonction Lambda en plusieurs parties, mais cela est devenu compliqué et j'étais frustré car je ne pouvais pas suivre l'erreur. Après tout, c'est simple, c'est mieux. Je souhaite que davantage de déclencheurs Lambda prennent en charge plus de services.

** * Étant donné que ce code a été écrit dans un test, la gestion des erreurs, etc. n'est pas strictement effectuée. Si vous rencontrez des inconvénients avec ce code, veuillez le faire à vos propres risques. ** **

Recommended Posts

[Python] Créez un robot d'exploration Web basé sur les événements à l'aide de l'architecture sans serveur d'AWS
[Hyperledger Iroha] Créez un compte à l'aide de la bibliothèque Python
Créer une carte Web en utilisant Python et GDAL
Créez un fichier image à l'aide de PIL (Python Imaging Library).
Créer des tickets JIRA en utilisant Python
Web scraping avec Selenium (Python)
Faisons une discussion WEB en utilisant WebSocket avec AWS sans serveur (Python)!
Créer une interface graphique python à l'aide de tkinter
Créez une application avec une architecture propre tout en utilisant DI + mock en Python
[Débutant] Scrapage Web Python facile à comprendre à l'aide de Google Colaboratory
Créez rapidement un fichier Excel avec Python #python
Créer une application à l'aide de l'API Spotify
Créer un environnement OpenCV3 + python3 sur OSX
[Python] Créez rapidement une API avec Flask
Créez une application de mots anglais avec python
Créer un diagramme de dispersion elliptique en Python sans utiliser une distribution normale multivariée