[Python] Erstellen Sie einen ereignisgesteuerten Webcrawler mithilfe der serverlosen Architektur von AWS

Versuchen Sie, einen serverlosen und ereignisgesteuerten WEB-Crawler zu erstellen

Nachdem ich Elastic Search studiert habe, habe ich mich gefragt, ob ich etwas mit ES anfangen kann. Deshalb habe ich mit Kinesis + Lambda einen ereignisgesteuerten Webcrawler geschrieben.

Arbeitsablauf

serverless-crawler.png

Der allgemeine Fluss ist wie folgt.

  1. Extrahieren Sie die URL mit Scrapy (Scraping Hub oder AWS Lambda) und fügen Sie sie in den Kinesis-Stream ein
  2. Feuern Sie AWS Lambda aus dem Kinesis-Stream ab
  3. Patrouillieren Sie von der Lambda-Funktion zur URL und senden Sie Daten an den Elastic Search Service

Erstellen eines IAM-Benutzers

Da Sie die Berechtigung zur Verwendung von Kinesis und Elastic Search benötigen, Haben Sie jeweils ** Zugriffsschlüssel-ID ** und ** geheimen Zugriffsschlüssel **.

Darüber hinaus ist auch die ARN des Benutzers erforderlich. Denken Sie also daran (arn: aws: iam :: ********: user / ********).

Erstellen Sie einen Stream mit AWS Kinesis

Erstellen Sie zunächst einen Kinesis-Stream.

  1. Erstellen Sie einen Stream
  2. Geben Sie einen geeigneten Streamnamen ein (vorläufig Scraping_url).
  3. Geben Sie die Anzahl der Shards ein (vorerst 1).
  4. Stream erstellen スクリーンショット 2017-04-06 12.45.52.png

Erstellen Sie den AWS ElasticSearch Service

Erstellen Sie als Nächstes eine ES mit Amazon Elastic Search Service.

Betrieb unter AWS

  1. Create a new domain

  2. Geben Sie einen geeigneten Domainnamen in den Elasticsearch-Domainnamen ein (vorläufig Webarchive).

  3. Wählen Sie [5.1] für die Elasticsearch-Version. Drücken Sie [Weiter] スクリーンショット 2017-04-06 13.06.02.png

  4. Setzen Sie den Instanztyp unter Cluster konfigurieren auf [t2.small]. Klicken Sie auf Weiter (zum Testen, also mit einer kleinen Instanz). スクリーンショット 2017-04-06 13.07.46.png

  5. Wählen Sie in der Richtlinie Zugriff einrichten die Option Zugriff auf ein oder mehrere AWS-Konten oder IAM-Benutzer zulassen oder verweigern aus スクリーンショット 2017-04-06 13.18.13.png

  6. Geben Sie die ARN des Benutzers, den Sie zulassen möchten, in die Konto-ID oder die ARN * ein スクリーンショット 2017-04-06 13.18.51.png

  7. Erstellen Sie mit [Bestätigen und erstellen]

  8. Nach einer Weile wird ES gestartet. Überprüfen Sie daher [Endpunkt] und denken Sie daran, wie es in Lambda verwendet wird. スクリーンショット 2017-04-06 13.30.18.png

Index und Karte zur elastischen Suche

Erstellen Sie Zuordnungsdaten, um URL, Titel und Artikelinhalt für die Artikelspeicherung zu speichern.

mapping.json


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

Erstellen Sie als Nächstes die obigen Zuordnungsdaten und ein Skript, um einen Index zu erstellen.

Installieren Sie die folgenden Pakete vorab lokal
$ pip install requests_aws4auth elasticsearch

es-mapping.py


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

if __name__ == '__main__':
    #Geben Sie den ES-Endpunkt an
    host='search-***************.ap-northeast-1.es.amazonaws.com'
    awsauth = AWS4Auth(
            #AWS-Benutzerzugriffsschlüssel-ID und geheimer Zugriffsschlüssel
            '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
Wenn Sie das Skript ausführen, sollte es in AWS ES indiziert sein. スクリーンショット 2017-04-06 15.32.52.png

Erstellen Sie AWS Lambda

Nachdem wir ElasticSearch erstellt haben, ist es Zeit, die Lambda-Funktion zu erstellen.

Erstellen einer Lambda-Funktion

Erstellen Sie lokal eine Lambda-Funktion. $ 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']
    #Verwenden Sie die IAM-Rolle, um sich beim ElasticSearch-Dienst zu authentifizieren
    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 = []

    #Holen Sie sich Events von 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}}})
                #Ist die URL bereits in ES registriert?
                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)

Installieren Sie nach dem Erstellen der Lambda-Funktion die erforderlichen Bibliotheken in derselben Hierarchie
$ pip install readability-lxml html2text elasticsearch requests_aws4auth requests -t /path/to/web_crawler
Mit Reißverschluss aushärten
$ zip -r web_crawler.zip .

Stellen Sie Lambda-Funktionen in AWS bereit

  1. [Lambda-Funktion erstellen]

  2. Wählen Sie [Leere Funktion]

  3. Wählen Sie den zuvor unter [Triggereinstellungen] erstellten Kinesis-Stream aus.

  4. [Stapelgröße] beträgt ungefähr 10

  5. [Startposition] ist horizontale Trimmung

  6. Überprüfen Sie die Triggeraktivierung スクリーンショット 2017-04-06 13.51.27.png

  7. Geben Sie [Name] in [Funktionseinstellungen] ein (vorläufig WebCrawler hier).

  8. Wählen Sie Python 2.7 für Runtime

  9. Wählen Sie unter Codeeingabetyp die Option ZIP-Datei hochladen aus

  10. Geben Sie die zuvor aus [Funktionspaket] erstellte Zip-Datei an.

  11. Setzen Sie [Umgebungsvariablen] auf 3, um auf Elastic Search zuzugreifen.

  12. Greifen Sie auf die Schlüssel-ID in ACCESS_ID zu

  13. Geheimer Zugriffsschlüssel auf SECRET_KEY

  14. Elastic Search-Endpunkt auf ES_HOST スクリーンショット 2017-04-06 16.24.30.png

  15. [Handler] bleibt lambda_function.lambda_handler

  16. Erstellen Sie die entsprechenden Rollen

  17. Stellen Sie [Timeout] unter [Detaillierte Einstellungen] auf ca. 2 Minuten ein.

  18. [Funktion erstellen]

Extrahieren Sie die URL mit Scrapy und senden Sie sie an den Kinesis-Stream

Verwenden Sie als Nächstes Scrapy, um die URL von der Listenseite zu extrahieren und die Daten an den Kinesis-Stream zu senden.

Die Listenseite verwendet den Hot-Eintrag von Hatena Bookmark. RSS scheint einfacher zu sein, Daten zu erhalten, wenn Sie Scrapy verwenden, aber ich habe es gewagt, von der Webseite zu kratzen. Scrapy ist ein nützliches und leistungsstarkes Framework zum Erstellen erweiterter Webcrawler. Wenn Sie interessiert sind, können Sie es jederzeit berühren.

Ein Projekt erstellen

Installieren Sie zuerst Scrapy
$ pip install scrapy
$ scrapy startproject hotentry
$ vim hotentry/hotentry/spiders/hotentry.py
Geben sie den untenstehenden Code ein.

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
Fügen Sie der settings.py die Zugriffsschlüssel-ID und den geheimen Zugriffsschlüssel hinzu

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

Sie können jetzt in den Kinesis-Stream einfügen. Versuchen wir, diesen Code auszuführen.
$ scrapy crawl hotenty

Sie sollten nun in der Lage sein, die Daten mit "Scrapy-> Kinesis-> AWS Lambda-> Elastic Search" zu füllen.

Stellen Sie Scrapy auf dem Scraping-Hub bereit

Ich konnte die URL mit Scrapy extrahieren und an Kinesis senden, aber da es sich um einen lokalen Stapel handelt, stellen Sie den Scrapy-Code für einen Cloud-Dienst namens Scrapinghub bereit.

Weitere Informationen zur Installation finden Sie im folgenden Artikel.

Es ist einfach, von der Benutzerregistrierung bis zur Bereitstellung, also werde ich es aufschlüsseln.

Schließlich

Anfangs habe ich SQS und DynamoDB verwendet und die Lambda-Funktion in mehrere Teile unterteilt, aber es wurde kompliziert und ich war frustriert, weil ich dem Fehler nicht folgen konnte. Immerhin ist einfach am besten. Ich möchte, dass mehr Lambda-Trigger mehr Dienste unterstützen.

** * Da dieser Code in einem Test geschrieben wurde, wird die Fehlerbehandlung usw. nicht streng durchgeführt. Wenn Sie mit diesem Code Nachteile haben, tun Sie dies bitte auf eigenes Risiko. ** **.

Recommended Posts

[Python] Erstellen Sie einen ereignisgesteuerten Webcrawler mithilfe der serverlosen Architektur von AWS
[Hyperledger Iroha] Erstellen Sie ein Konto mit der Python-Bibliothek
Erstellen Sie eine Webmap mit Python und GDAL
Erstellen Sie eine Bilddatei mit PIL (Python Imaging Library).
Erstellen Sie JIRA-Tickets mit Python
Web Scraping mit Selenium (Python)
Lassen Sie uns einen Web-Chat mit WebSocket mit AWS serverless (Python) durchführen!
Erstellen Sie mit tkinter eine Python-GUI
Erstellen Sie eine Anwendung mit Clean Architecture, während Sie DI + mock in Python verwenden
[Anfänger] Leicht verständliches Python-Web-Scraping mit Google Colaboratory
Erstellen Sie schnell eine Excel-Datei mit Python #python
Erstellen Sie eine Anwendung mit der Spotify-API
Erstellen Sie eine OpenCV3 + python3-Umgebung unter OSX
[Python] Erstellen Sie schnell eine API mit Flask
Erstellen Sie eine englische Wort-App mit Python
Erstellen Sie in Python ein elliptisches Streudiagramm, ohne eine multivariate Normalverteilung zu verwenden