AWSLambdaを用いて定時にpythonスクレイピングコードを実行する


はじめに

pythonを学び始めて少し時間が経ち、なんだかそれっぽいものを作ってみたくなりました。
そこで、【保存版・初心者向け】Python 目的別チュートリアルを読んでみて、初心者向けのwebスクレイピングを行うコードを書いてみることにしました。

目標完成物

毎朝「日経×TECH」からトップニュースとトップページURLをSlackに投稿する。

環境

python3.7.2
Windows10

手順

1. ニュースサイトの見出しをスクレイピング
2. Slackに転送
3. AWSLambdaに載せて毎朝6時に実行するよう設定

ニュースサイトの見出しをスクレイピング

まずは対象となるサイトから求めている情報を取得するコードを作成します。
この章は【PythonでWebスクレイピングをしよう(天気予報)】を参考にしています。

インストールするモジュール

requests:webページから何かをダウンロードしたいときに使用するモジュール
BeautifulSoup:取得したWebページから情報を抽出するモジュール

コード

nikkei_IT.py
#まだpip install していなかったらやっておきましょう。
import requests
from bs4 import BeautifulSoup

def scraping_jr():
    #スクレイピングしたいwebページのURL
    TARGET_URL = "https://tech.nikkeibp.co.jp/news/index_it.html"

    res = requests.get(TARGET_URL)
    soup = BeautifulSoup(res.text, 'lxml')

    #取得したい箇所のhtmlタグを選択
    container = soup.find("article").find('div',{'class':'articleList'})
    info_elements = container.find("ul",{'class':'top_panel_B'}).select('li')

    links = []

    for info_element in info_elements:
        links[len(links):len(links)] = info_element.find("div",{'class':'text'}).find('a')

注意

findメソッドは引数として指定したタグの要素を一つだけ持ちます。
対してselectメソッドは指定したタグの要素をすべてリストとして持ちます。

今回はトップニュースのみを取得したかったので、該当部分のみをfindで選択し、その選択領域内のニュースすべて(「li」タグ)をselectメソッドを用いて取得しました。

あとはliタグ内のニュース見出しを抽出すれば完成です。
新たに作成したlinksリストに格納していきます。

Slack転送

取得した内容を自分のSlackに転送します。
この章の内容は、【Python3でslackに投稿する】を参考にしています。

nikkei_IT.py
#slackwebモジュールもpip install しておきましょう。
import requests, slackweb
from bs4 import BeautifulSoup

SLACK_WEB_URL = "(WebhookのURL)"

    #中略

slack = slackweb.Slack(SLACK_WEB_URL)

    for link in links:
       l = [link, TARGET_URL]
       message = "\n".join(l)

       slack.notify(text=message, unfurl_links = 'true')

Incoming Webhook の設定をする必要があります。方法は【Python3でslackに投稿する】を参考にしてください。

「そもそもWebhook とはなんぞ?」という方は、【Webhookとは?】をご覧ください。

進捗確認

現在のコードはこんな感じ。

nikkei_IT.py
import requests, slackweb
from bs4 import BeautifulSoup

def scraping_jr():
    TARGET_URL = "https://tech.nikkeibp.co.jp/news/index_it.html"
    SLACK_WEB_URL = "(WebhookのURL)"

    res = requests.get(TARGET_URL)
    soup = BeautifulSoup(res.text, 'lxml')

    container = soup.find("article").find('div',{'class':'articleList'})
    info_elements = container.find("ul",{'class':'top_panel_B'}).select('li')

    links = []

    for info_element in info_elements:
        links[len(links):len(links)] = info_element.find("div",{'class':'text'}).find('a')

    slack = slackweb.Slack(SLACK_WEB_URL)

    for link in links:
       l = [link, TARGET_URL]
       message = "\n".join(l)

       slack.notify(text=message, unfurl_links = 'true')

scraping_jr()

とりあえずこれで、実行すれば求めていた動きはしてくれます。

AWSLambdaに載せる

ではこのコードをlambdaに載せて、定期的に自動で実行させましょう。
この章は以下の記事を参考にしています。
a【AWS】lambdaファンクションを定期的に実行する
b【AWS】Lambdaでpipしたいと思ったときにすべきこと

そもそもlambdaとは

awsのサービスの一つです。
特定のイベントが生じた際に、アップロードしておいたコードを自動で実行してくれます。
詳しくはこちら

トリガーの設定

a(【AWS】lambdaファンクションを定期的に実行する)の「定期実行の設定」以降を参考にして行いましょう。
今回はルールタイプをスケジュール式にしました。
毎朝6時に実行させたかったので、【cron(0 21 * * ? *)】と入力しました。書き方の詳細はこちら

コードの実装

b(【AWS】Lambdaでpipしたいと思ったときにすべきこと)を参考に行いましょう。
この記事の手順を経ないと、外部モジュールをimportすることができません。
*zipファイルの作成は普通にGUIでやりました。

進捗確認・注意点

現在のコードやディレクトリの状態はこんな感じ。

実は、 scraping_jr 関数を定義している部分に event と context という引数を新たに指定しています。
理由はわかりませんが、「引数が2個ないといけないのに0個しかないよ!」とのエラーがでたので、資料bの真似をしました。

テストも成功。翌朝の起床時には狙い通り「日経×TECH」のトップニュースがに届いていました。
これでめでたく完成です。

(蛇足)システムを定期的に実行するために・・・

毎朝システムを実行するためにいちいちわざわざPCを起動させるのは面倒なので今回はlambdaにコードを載せました。
最初はGoogleDriveのColabを使ってスマホと連携できないかと思って少し調べてみましたが、なんだかよくわからなかったので諦めました。

まとめ

今回はpythonとAWSLambdaを用いて定時にwebスクレイピングを行い、内容をSlackに転送するシステムを作成しました。
もっと効率的なコードの書き方やツールがありましたら、ご教示ください。

参考資料

【保存版・初心者向け】Python 目的別チュートリアル
PythonでWebスクレイピングをしよう(天気予報)
Python3でslackに投稿する
Webhookとは?
【AWS】lambdaファンクションを定期的に実行する
【AWS】Lambdaでpipしたいと思ったときにすべきこと
AWS Lambda とは
AWS Lambdaで定期処理を実行するためのRateとCronの設定