AWSのLambdaでScrapyを動かす設定です。
クローリングの大規模化などを行うのにこちらができると非常に柔軟な設計ができるため実施を行います。
★Scrapy関連記事
・CentOS環境の python3.6(pyenv環境)で Scrapy を利用してみる(Scrapy その1)
・Scrapyでデータ取得でクローリングで取得したURLから検索する(Scrapy その2)
・CentOS7でscrapy-splashを使ってJavaScriptの解析(Scrapy その3)
・Scrapyでデータの保存をmongodbにして見る(Scrapy その4)
・CentOS7でscrapy-splashを使ってJavaScriptの解析 その2(Scrapy その5)
今回はAWSのLambdaをpython3.8のランタイムで動かします。(現在の推奨設定)
python3.8での環境構築はこちらをご確認ください。
Lambdaの設定
まずAWSのコンソールからLambdaで「関数の作成」を行います。
続いて関数名を「lambdaTest」、ランタイムを「Python3.8」、アクセス権限を「既存のロール」の「lambda_basic_execution」で「関数の作成」を行います。
初期では以下の画面になります。
次に「トリガーを追加」を行います。
トリガーを「S3」、バケットを特定のバケットを選択します。
イベントタイプを「すべてのオブジェクト作成イベント」を選択します。
こちらを設定すると以下の状態になります。
真ん中の「lambdaTest」をクリックするとLambdaの関数が確認できます。
Lambda側からこちらで一旦離れます。
「送信先を追加」は成功や失敗のメッセージを送ることができますが今回は結果はCloudWatchで確認するため今回は必要ありません。
Pythonの関数の作成
「コードをインラインで編集」を行うと外部のモジュールのインポートができません。
外部モジュールのインポートを行うにはそのモジュールを含むzipファイルをアップする必要があります。
ここで、一番上にリンクのあったPython3.8のDocker環境にログインします。
アプリのディレクトリに移動して「Scrapy」のライブラリーをインストールします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
$ pip install Scrapy -t . Collecting Scrapy Downloading Scrapy-1.8.0-py2.py3-none-any.whl (238 kB) |████████████████████████████████| 238 kB 5.3 MB/s Collecting w3lib>=1.17.0 Downloading w3lib-1.21.0-py2.py3-none-any.whl (20 kB) Collecting cryptography>=2.0 Downloading cryptography-2.8-cp34-abi3-manylinux2010_x86_64.whl (2.3 MB) |████████████████████████████████| 2.3 MB 6.4 MB/s Collecting PyDispatcher>=2.0.5 Downloading PyDispatcher-2.0.5.tar.gz (34 kB) Collecting lxml>=3.5.0 Downloading lxml-4.5.0-cp38-cp38-manylinux1_x86_64.whl (5.6 MB) |████████████████████████████████| 5.6 MB 4.4 MB/s Collecting pyOpenSSL>=16.2.0 Downloading pyOpenSSL-19.1.0-py2.py3-none-any.whl (53 kB) |████████████████████████████████| 53 kB 2.2 MB/s Collecting zope.interface>=4.1.3 Downloading zope.interface-4.7.1-cp38-cp38-manylinux2010_x86_64.whl (174 kB) |████████████████████████████████| 174 kB 8.9 MB/s Collecting service-identity>=16.0.0 Downloading service_identity-18.1.0-py2.py3-none-any.whl (11 kB) Collecting cssselect>=0.9.1 Downloading cssselect-1.1.0-py2.py3-none-any.whl (16 kB) Collecting six>=1.10.0 Downloading six-1.14.0-py2.py3-none-any.whl (10 kB) Collecting parsel>=1.5.0 Downloading parsel-1.5.2-py2.py3-none-any.whl (12 kB) Collecting queuelib>=1.4.2 Downloading queuelib-1.5.0-py2.py3-none-any.whl (13 kB) Collecting Twisted>=17.9.0; python_version >= "3.5" Downloading Twisted-19.10.0.tar.bz2 (3.1 MB) |████████████████████████████████| 3.1 MB 9.1 MB/s Collecting protego>=0.1.15 Downloading Protego-0.1.16.tar.gz (3.2 MB) |████████████████████████████████| 3.2 MB 7.5 MB/s Collecting cffi!=1.11.3,>=1.8 Downloading cffi-1.14.0-cp38-cp38-manylinux1_x86_64.whl (409 kB) |████████████████████████████████| 409 kB 12.9 MB/s Collecting setuptools Downloading setuptools-45.2.0-py3-none-any.whl (584 kB) |████████████████████████████████| 584 kB 4.1 MB/s Collecting attrs>=16.0.0 Downloading attrs-19.3.0-py2.py3-none-any.whl (39 kB) Collecting pyasn1 Downloading pyasn1-0.4.8-py2.py3-none-any.whl (77 kB) |████████████████████████████████| 77 kB 5.0 MB/s Collecting pyasn1-modules Downloading pyasn1_modules-0.2.8-py2.py3-none-any.whl (155 kB) |████████████████████████████████| 155 kB 5.8 MB/s Collecting constantly>=15.1 Downloading constantly-15.1.0-py2.py3-none-any.whl (7.9 kB) Collecting incremental>=16.10.1 Using cached incremental-17.5.0-py2.py3-none-any.whl (16 kB) Collecting Automat>=0.3.0 Downloading Automat-20.2.0-py2.py3-none-any.whl (31 kB) Collecting hyperlink>=17.1.1 Downloading hyperlink-19.0.0-py2.py3-none-any.whl (38 kB) Collecting PyHamcrest>=1.9.0 Downloading PyHamcrest-2.0.0-py3-none-any.whl (51 kB) |████████████████████████████████| 51 kB 1.1 MB/s Collecting pycparser Downloading pycparser-2.19.tar.gz (158 kB) |████████████████████████████████| 158 kB 7.2 MB/s Collecting idna>=2.5 Downloading idna-2.9-py2.py3-none-any.whl (58 kB) |████████████████████████████████| 58 kB 4.7 MB/s Building wheels for collected packages: PyDispatcher, Twisted, protego, pycparser Building wheel for PyDispatcher (setup.py) ... done Created wheel for PyDispatcher: filename=PyDispatcher-2.0.5-py3-none-any.whl size=11515 sha256=86be41d40f19f9ac47c76eb982328b0e84983f56a788c7ec8922cbfba04d4e76 Stored in directory: /root/.cache/pip/wheels/d1/d7/61/11b5b370ee487d38b5408ecb7e0257db9107fa622412cbe2ff Building wheel for Twisted (setup.py) ... done Created wheel for Twisted: filename=Twisted-19.10.0-cp38-cp38-linux_x86_64.whl size=3080045 sha256=55b353fb773d54bce08f6cc8b3bfc939c895ac76886f735bc88d450ccdc65d39 Stored in directory: /root/.cache/pip/wheels/6c/7b/65/db0c3de94e8e87bd1600c617da06cbfcf18f449640bbe053e0 Building wheel for protego (setup.py) ... done Created wheel for protego: filename=Protego-0.1.16-py3-none-any.whl size=7765 sha256=adf55c1610bca6ec139a17017227e7b3a11f175160eb5918325a5991c53fe292 Stored in directory: /root/.cache/pip/wheels/91/64/36/bd0d11306cb22a78c7f53d603c7eb74ebb6c211703bc40b686 Building wheel for pycparser (setup.py) ... done Created wheel for pycparser: filename=pycparser-2.19-py2.py3-none-any.whl size=111031 sha256=7c58c2cb6631c581f314e6e57c73df7e1a1bed91e0690a2c4c8a4edd829aea48 Stored in directory: /root/.cache/pip/wheels/6d/fb/fb/c752da1378a60304d18004cc5c58e73519a798ee2809db7562 Successfully built PyDispatcher Twisted protego pycparser Installing collected packages: six, w3lib, pycparser, cffi, cryptography, PyDispatcher, lxml, pyOpenSSL, setuptools, zope.interface, attrs, pyasn1, pyasn1-modules, service-identity, cssselect, parsel, queuelib, constantly, incremental, Automat, idna, hyperlink, PyHamcrest, Twisted, protego, Scrapy Successfully installed Automat-20.2.0 PyDispatcher-2.0.5 PyHamcrest-2.0.0 Scrapy-1.8.0 Twisted-19.10.0 attrs-19.3.0 cffi-1.14.0 constantly-15.1.0 cryptography-2.8 cssselect-1.1.0 hyperlink-19.0.0 idna-2.9 incremental-17.5.0 lxml-4.5.0 parsel-1.5.2 protego-0.1.16 pyOpenSSL-19.1.0 pyasn1-0.4.8 pyasn1-modules-0.2.8 pycparser-2.19 queuelib-1.5.0 service-identity-18.1.0 setuptools-45.2.0 six-1.14.0 w3lib-1.21.0 zope.interface-4.7.1 # du -h . 48K ./.libs_cffi_backend 44K ./__pycache__ 108K ./attr/__pycache__ 268K ./attr 32K ./attrs-19.3.0.dist-info 48K ./automat/__pycache__ 80K ./automat/_test/__pycache__ 148K ./automat/_test 244K ./automat . . . . 48K ./zope.interface-4.7.1.dist-info 63M . |
pipでのインストール時に「-t」オプションをつけるとライブラリの場所を指定することができ、今回はカレントディレクトリにインストールしています。
また、ローカルのフォルダのサイズを確認しています。
これは圧縮前のフォルダが250MB以下、圧縮後のファイルが50MB以下にする必要があるためです。
・AWS Lambda の制限
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/limits.html
その後に上記フォルダに以下のファイルを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import boto3 import scrapy from scrapy.crawler import CrawlerProcess class TestSpider(scrapy.Spider): name = 'testspider' start_urls = [ 'https://developer-collaboration.com/' ] def parse(self, response): for quote in response.css('article.post-list'): title = quote.css('h1.entry-title::text').extract_first() url = quote.css('a::attr(href)').extract_first() yield { 'title': title, 'url': url } def lambda_handler(event, context): process = CrawlerProcess({ 'USER_AGENT': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)', 'FEED_FORMAT': 'json', 'FEED_URI': '/tmp/result.json' }) process.crawl(TestSpider) process.start() |
ポイントはScrapyをプロセスで動かしているところです。
・Scrapy latest - Common Practices - Run Scrapy from a script
https://docs.scrapy.org/en/latest/topics/practices.html
非常に簡単なScrapyの設定を行います。
Labmdaでの起動のため「lambda_handler」からの呼び出しを行なっています。
process.startの時に「stop_after_crawl=False」のオプションをつけているのは同じプロセスで複数のspiderを動かすためです。
https://doc.scrapy.org/en/latest/topics/practices.html#running-multiple-spiders-in-the-same-process
こちらのファイルを圧縮しサイズの確認を行います。
1 2 3 4 5 |
$ zip -r ../lambda_function.zip * .[^.]* $ ls -lah ../lambda_function.zip -rw-r--r-- 1 staff staff 19M 2 18 18:23 ../lambda_function.zip |
こちらでLambdaのデプロイパッケージをアップする準備は整いました。
Lambdaでデプロイパッケージのアップロードと実行
それではデプロイパッケージのアップロードを行います。
コードエントリタイプからzipファイルのアップロードができます。
先ほどの「lambda_function.zip」を選択し画面右上の「保存」を押してアップロードします。
この状態で準備は整いました。
S3のアップロードがトリガーになるので以下でファイルのアップを行います。
1 2 3 4 |
$ aws s3 sync ./input/ s3://lambda-test01.colabmix.co.jp/input/ --profile=lambda-test upload: input/sample.csv to s3://lambda-test01.colabmix.co.jp/input/sample.csv |
こちらで成功していると結果が確認できるはずです。
結果の確認
起動の状況は「CloudWatch」から確認できます。
該当のトリガーでのログを確認します。
成功しているためかなり長めのログが確認できました。
ログのさらに下の方では「title」と「url」のScrapyのクロールが成功している結果がわかります。
状況としてLambdaを使ったクローリングが成功しているようです。
またサーバー側では以下のログが出ていました。
1 2 3 4 5 |
13.112.43.37 - - [18/Feb/2020:18:48:56 +0900] "GET / HTTP/1.1" 200 9216 "-" "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)" 13.112.43.37 - - [18/Feb/2020:18:49:00 +0900] "GET / HTTP/1.1" 200 9216 "-" "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)" 3.112.66.65 - - [18/Feb/2020:19:11:02 +0900] "GET / HTTP/1.1" 200 9216 "-" "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)" |
ブラウザなどは指定のものです。
上記の2つのログが短期間に再度トリガーを発火したログです。
この際、接続元のIPは変わりません。
しばらくして(20分ほど)起動した際にはIPが変わっていることを確認できます。
トリガーファイルの中身のURLでクローリングを行ったり、クローリング結果の保存先など課題はまだありますが基本的なところができましたのでこちらまでで。
★関連記事
・AWSのLambdaでScrapyを動かす その2 - S3からの値の取得 -
・AWSのLambdaでScrapyを動かす その3 - dynamodbからの取得・保存 -
このブログは株式会社CoLabMixによる技術ブログです。
GCP、AWSなどでのインフラ構築・運用や、クローリング・分析・検索などを主体とした開発を行なっています。
Ruby on RailsやDjango、Pythonなどの開発依頼などお気軽にお声がけください。
開発パートナーを増やしたいという企業と積極的に繋がっていきたいです。