LoginSignup
37
41

More than 5 years have passed since last update.

Python3.5のAsync構文を利用してスクレイピングを行う

Posted at

Pythonでスクレイピングする方法としては既存のライブラリであるScrapyやdemiurge等を利用するという方法がありますが、
今回はPython3.5から追加されているAsync構文を利用して自作してみようと思います。

なおasync・awaitとはなんぞやという説明は行いません。
async・await構文の使い方はこちらの記事が参考になりました。


環境

  • Python3.5.0
  • beautifulsoup4==4.4.1

How To

まずはWebサイトのダウンロード部分から

import asyncio
import urllib.request


class Downloader:
    def __init__(self, urls):
        self.urls = urls

    def run(self):
        loop = asyncio.get_event_loop()
        return loop.run_until_complete(self.fetch())

    async def fetch(self):
        return await asyncio.wait([self.download(i, url) for i, url in enumerate(self.urls)])

    async def download(self, n, url):
        request = urllib.request.Request(url)
        html = urllib.request.urlopen(request).read()
        print("{0} {1} download finish...".format(n, url))
        return html


if __name__ == "__main__":
    downloader = Downloader([
        "https://www.python.org/", 
        "https://www.python.org/about/", 
        "https://www.python.org/downloads/"
    ])

    downloader.run()

結果

1 https://www.python.org/about/ download finish
2 https://www.python.org/downloads/ download finish
0 https://www.python.org/ download finish

コードで特出すべきところとしては、downloadメソッドを並列に実行していることです。
同期的に1つずつダウンロードするのではなく、非同期でダウンロードできていることが確認できると思います。

スクレイピング

これだけだとHTMLをダウンロードしただけで、パースが面倒なのでパーサーを追加するためにコードを修正します。
今回はBeautifulSoupを使い、WebサイトのTitleタグの中身を取得します。

import asyncio
import urllib.request
from bs4 import BeautifulSoup


class Scraping:
    def __init__(self, urls):
        self.urls = urls

    def run(self):
        loop = asyncio.get_event_loop()
        return loop.run_until_complete(self.fetch())

    async def fetch(self):
        return await asyncio.wait(
            [self.scraping(url) for url in self.urls]
        )

    async def scraping(self, url):
        request = urllib.request.Request(url)
        html = urllib.request.urlopen(request).read()
        bs = BeautifulSoup(html, "html.parser")
        print(bs.title.string)


if __name__ == "__main__":
    scraping = Scraping([
        "https://www.python.org/", 
        "https://www.python.org/about/", 
        "https://www.python.org/downloads/"
    ])

    scraping.run()

結果

Welcome to Python.org
Download Python | Python.org
About Python™ | Python.org

まとめ

と簡単にはですが、これで自作のスクレイピング処理が実装できました。
後はクローリング機能を実装することで立派なフレームワークになります。
クローリングについては、、、クローラー/Webスクレイピング Advent Calendar等を参考にすると良いのではないでしょうか(丸投げ

Async構文の3.4との比較ですが、
* コルーチンを定義する際に@asyncio.coroutineデコレータを付ける必要がなくなり、async defと完結になった。
* yield from とジェネレータと混同しやすかった構文がawait文になりシンプルでわかりやすくなった。
かなと思います。

今回のコードは全てGithubに公開しているので何かあればそちらを参照してください。

参考

37
41
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
37
41