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に公開しているので何かあればそちらを参照してください。