はじめに
スクレイピングを行うにあたりこちらの注意事項まとめを参考にさせていただきました。
概要
requests-htmlでスクレイピングし、slackにメッセージを飛ばす仕組みを実装します。
スクレイピング対象のページが動的なページのためBeautiful Soupが使えず、requests-htmlを使用しました。
今回、個人利用の目的で
- yahooニュースの東京都のコロナ関連情報ページをスクレイピング
- 表示されている3件のニュースのタイトルと記事urlを取得する
- 取得結果をslackの任意のチャンネルに投稿する
という仕組みを実装します。
環境/使用する技術
- MacOS Big Sur
- python 3.6
- スクレイピングするためのライブラリ requests-html
- Docker version 19.03.13(開発環境構築)
- docker-compose version 1.27.4(開発環境構築)
- slack webhook
開発環境構築
pythonのコードを書くための使い捨て開発環境を作成します。Dockerを使用します。
今回使用するイメージはpythonの3.6バージョンです。
※ requests-htmlがpython3.6のみサポートしているため
開発マシン(Mac)側では適当なディレクトリを掘ってソースコードを以下のように配置します。
├── Dockerfile
├── docker-compose.yml
└── src
└── checkNews.py // スクレイピングするファイル
Dockerfile
Dockerfileを書きます。
touch Dockerfile
Dockerfileは以下とします。
FROM python:3.6
WORKDIR /usr/src/pythonwork
RUN apt-get update && \
pip install requests-html && \
pip install pyppeteer && \
pip install slackweb
docker-compose.yml
docker-compose.ymlを作成します。
touch docker-compose.yml
docker-compose.ymlは以下とします。
version: "3.8"
services:
python:
build: .
volumes:
- ./src:/usr/src/pythonwork
tty: true
working_dir: /usr/src/pythonwork
コンテナの作成
コンテナを作成します。
docker-compose build
開発環境構築は以上です。
webhookの利用設定
slackに投稿する際、Incoming webhookを使うのでこちらに沿って設定を行ってください。
スクレイピングするソースコード
yahooの東京都のコロナニュースの件名と記事のurlを3件取得し、slackの#generalチャンネルに投稿します。
from requests_html import HTMLSession
import slackweb
import re
news_url = "https://news.yahoo.co.jp/pages/article/covid19tokyo"
slackweb_url = "https://hooks.slack.com/services/xxxxxxxxxxxxxxxxxxxxx"
# セッション開始
session = HTMLSession()
r = session.get(news_url)
# HTMLを生成
r.html.render()
for num in range(1, 4):
# スクレイピング
article_title = r.html.find("#tab_1 > ul.dlpThumbLink > li:nth-child(" + str(num) + ") > a > span.dlpThumbText > span:nth-child(1)", first=True)
article_title_text = article_title.text
article_link = r.html.find("#tab_1 > ul.dlpThumbLink > li:nth-child(" + str(num) + ") > a", first=True)
article_url = article_link.absolute_links
# slackへ投稿
slack = slackweb.Slack(url=slackweb_url)
slack.notify(text=article_title_text + '\n' + re.sub("\{|\}|'", "", str(article_url)), channel="#general", username="covid19-news-bot", icon_emoji=":eyes:", mrkdwn=True)
スクレイピングを実行する
コンテナを立ち上げ、コンテナ内に入ります。
docker-compose up -d
docker-compose exec python bash
checkNesw.pyを動かします。
初回実行ではヘッドレスブラウザをインストールします。[W:pyppeteer.chromium_downloader] start chromium download.
# python checkNews.py
エラー:pyppeteer.errors.BrowserError: Browser closed unexpectedlyが出力される
root@353345a20664:/usr/src/pythonwork# python checkNews.py
[W:pyppeteer.chromium_downloader] start chromium download.
Download may take a few minutes.
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 108773488/108773488 [00:03<00:00, 32959625.14it/s]
[W:pyppeteer.chromium_downloader]
chromium download done.
[W:pyppeteer.chromium_downloader] chromium extracted to: /root/.local/share/pyppeteer/local-chromium/588429
Traceback (most recent call last):
File "checkNews.py", line 15, in <module>
r.html.render()
File "/usr/local/lib/python3.6/site-packages/requests_html.py", line 586, in render
self.browser = self.session.browser # Automatically create a event loop and browser
File "/usr/local/lib/python3.6/site-packages/requests_html.py", line 730, in browser
self._browser = self.loop.run_until_complete(super().browser)
File "/usr/local/lib/python3.6/asyncio/base_events.py", line 488, in run_until_complete
return future.result()
File "/usr/local/lib/python3.6/site-packages/requests_html.py", line 714, in browser
self._browser = await pyppeteer.launch(ignoreHTTPSErrors=not(self.verify), headless=True, args=self.__browser_args)
File "/usr/local/lib/python3.6/site-packages/pyppeteer/launcher.py", line 306, in launch
return await Launcher(options, **kwargs).launch()
File "/usr/local/lib/python3.6/site-packages/pyppeteer/launcher.py", line 167, in launch
self.browserWSEndpoint = get_ws_endpoint(self.url)
File "/usr/local/lib/python3.6/site-packages/pyppeteer/launcher.py", line 226, in get_ws_endpoint
raise BrowserError('Browser closed unexpectedly:\n')
pyppeteer.errors.BrowserError: Browser closed unexpectedly:
エラーメッセージで調べてみると、ヘッドレスブラウザを使うにはライブラリが不足しているようです。
こちらの記事を参考にpyppeteerに必要なライブラリをapt-get installしました。
以下参考先サイトさまから引用させていただきます。
sudo apt-get install gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget
コンテナにはrootログインしているのでsudoは省き、実行します。(※DockerfileのRUNに↓を書いておけば再発しません。その際は-yを忘れずに)
apt-get install gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget
再度実行。
root@353345a20664:/usr/src/pythonwork# python checkNews.py
root@353345a20664:/usr/src/pythonwork#
slackに投稿できました。urlをクリックするとちゃんと記事のページを開くことができました。
参考サイト
- pyppeteer.errors.BrowserErrorの対応策
- Dockerのバージョンとdocker-composeのサポート対応表
- requests-htmlについて