Pythonから簡単にHeadless Chromeを利用できるpyppeteerというライブラリがあります。Headless Chromeの操作をラップしてくれてかなり便利なのですが、ほとんどの関数やメソッドが非同期(async)nあので、しばらく遊んでasync/awaitを使った実装に慣れる必要がありそうです。
ただ、公式リポジトリのサンプルコードを見ても分かる通り、browser.newPage()
やclose()
などの前処理・後処理のコードがあります。
async def main(): browser = await launch() page = await browser.newPage() await page.goto('http://example.com') await page.screenshot({'path': 'example.png'}) dimensions = await page.evaluate('''() => { return { width: document.documentElement.clientWidth, height: document.documentElement.clientHeight, deviceScaleFactor: window.devicePixelRatio, } }''') print(dimensions) # >>> {'width': 800, 'height': 600, 'deviceScaleFactor': 1} await browser.close()
同期的な関数であれば、これをクラスでラップしてコンテキストマネージャ(with句)で実装したくなりますよね?私もそう思って調べたところ、async with
という非同期用のコンテキストマネージャがあるそうです。
これを使えば、以下のようにasync withで定形コードをまとめて実装できました。
import asyncio from pyppeteer import launch class Session(object): async def __aenter__(self): self._browser = await launch() self._page = await self._browser.newPage() return self async def get(self, url): await self._page.goto(url) return self._page # Pageを返す async def __aexit__(self, exc_type, exc, tb): await self._browser.close() async def main(): async with Session() as s: page = await s.get("https://google.com") await page.screenshot(path="./example.png") result = await page.mainFrame.content() print(result) if __name__ == "__main__": asyncio.get_event_loop().run_until_complete(main())
async/awaitはHaskellのIO型のdo記法に近いものだと思えばなんとかなりそうです。詳しい人なら、ここでモナドの話を始めるんだと思います。
async with構文はこちらのPEP 492で提案されたもののようなので、後で読んでみます。