非同期爬虫類:async/awaitとaiohttpの使用、および例


python 3.5には、asyncio/awaitキーワードが追加され、コールバックの書き方がより直感的で人間的になります.aiohttpは非同期ウェブサービスを提供するライブラリで、サーバ側とクライアントに分かれています.ここでは主にクライアントを使用します.本文は3段階に分けて、第1部分はpython 3を簡単に紹介する.5の非同期、asyncio/awaitキーワード.第2部ではaiohttpクライアント部の使用について説明します.第3部では、CSDNのあるブログのすべての文章をどのように登るかを例に挙げます.
1. async/await
async/awaitはpython 3です.5に新たに加わった特性は,非同期を従来のyield表記から解放し,より直感的になる.3.5までに非同期を使用する場合は、主にyield構文を使用します.例を次に示します.
import asyncio

@asyncio.coroutine  #    ,    asyncio.coroutine(hello())
def hello():
    print('Hello world! (%s)' % threading.currentThread())
    yield from asyncio.sleep(1)  #         ,          ,         
    print('Hello again! (%s)' % threading.currentThread())

loop = asyncio.get_event_loop()  
tasks = [hello(), hello()]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

実は上記の例はasyncioライブラリとcoroutineで修飾されているため,比較的簡略化されている.async/awaitを導入した後、hello()は次のように書くことができます.
async def hello():
    print("Hello world!")
    r = await asyncio.sleep(1)
    print("Hello again!")

この場合@asyncioは使用する必要はありません.coroutinは修飾され、defの前にasyncを加えて非同期関数であり、非同期操作が含まれていることを示します.さらにawaitを使用してyield fromを置き換え、このステップが非同期操作であることを示します.
2. aiohttp
aiohttpはウェブサービス用のライブラリで、廖雪峰を含むネット上の資料の大部分はサーバー側(server)に関するもので、クライアント(client)に関する資料は多くありません.実はaiohttpの資料は完備していません.公式サイトにはいくつかの例しかありません.私も例に従ってひょうたんを描いています.
2.1基本的な使い方
async with aiohttp.get('https://github.com') as r:
        await r.text()

ここでr.text()は、カッコで復号方式、符号化方式、例えば
await resp.text(encoding='windows-1251')

あるいは符号化しない、画像の読み取りに適しているなど、符号化できないものを選択してもよい
await resp.read()

ここで注意しなければならないのはwith...asの文法ですが、これは本文とは関係なく、読者が自分で検索して理解することができます.
2.2 timeoutの設定
追加する必要があります.Timeout(x)
with aiohttp.Timeout(0.001):
    async with aiohttp.get('https://github.com') as r:
        await r.text()

2.3セッションを使用したデータの取得
ここではクラスを導入しますClientSession. まずセッションオブジェクトを作成し、そのセッションオブジェクトでWebページを開きます.sessionはpost,get,put,headなど多くの操作を行うことができ,以下に示すようにする.
async with aiohttp.ClientSession() as session:
    async with session.get('https://api.github.com/events') as resp:
        print(resp.status)
        print(await resp.text())

postメソッドを使用する場合は、対応する文を
session.post('http://httpbin.org/post', data=b'data')

2.4カスタムheaders
これは簡単です.headersをsessionに置きます.get/postのオプションでいいです.注意headersデータはdict形式
url = 'https://api.github.com/some/endpoint'
headers = {
    'content-type': 'application/json'}

await session.get(url, headers=headers)

この方法はpostなどの他の方法にも有効であることは明らかである.
2.5エージェントの使用
この機能を実現するには、セッションオブジェクトを生産する過程でいくつかの修正が必要です.
conn = aiohttp.ProxyConnector(proxy="http://some.proxy.com")
session = aiohttp.ClientSession(connector=conn)
async with session.get('http://python.org') as resp:
    print(resp.status)

こちらはwithと書いてありません.....as….形式ですが、原理は同じです.前のフォーマットに簡単に書き換えることもできます.エージェントが認証する必要がある場合は、proxy_を追加する必要があります.authオプション.
conn = aiohttp.ProxyConnector(
    proxy="http://some.proxy.com",
    proxy_auth=aiohttp.BasicAuth('user', 'pass')
)
session = aiohttp.ClientSession(connector=conn)
async with session.get('http://python.org') as r:
    assert r.status == 200

2.6カスタムクッキー
同じセッションで修正します.
url = 'http://httpbin.org/cookies'
async with ClientSession({
    'cookies_are': 'working'}) as session:
    async with session.get(url) as resp:
        assert await resp.json() == {
    "cookies":
                                         {
    "cookies_are": "working"}}

3.サンプル
第2部の各機能の使い方を見てから、一つの例を完成するのは簡単で、それぞれの機能を組み合わせているにすぎない.次の簡単な爬虫類は、私のブログの下のすべての文章を這うために使われています.
import urllib.request as request
from bs4 import BeautifulSoup as bs
import asyncio
import aiohttp

@asyncio.coroutine
async def getPage(url,res_list):
    print(url)
    headers = {
    'User-Agent':'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'}
    # conn = aiohttp.ProxyConnector(proxy="http://127.0.0.1:8087")
    async with aiohttp.ClientSession() as session:
        async with session.get(url,headers=headers) as resp:
            assert resp.status==200
            res_list.append(await resp.text())


class parseListPage():
    def __init__(self,page_str):
        self.page_str = page_str
    def __enter__(self):
        page_str = self.page_str
        page = bs(page_str,'lxml')
        #       
        articles = page.find_all('div',attrs={
    'class':'article_title'})
        art_urls = []
        for a in articles:
            x = a.find('a')['href']
            art_urls.append('http://blog.csdn.net'+x)
        return art_urls
    def __exit__(self, exc_type, exc_val, exc_tb):
        pass


page_num = 5
page_url_base = 'http://blog.csdn.net/u014595019/article/list/'
page_urls = [page_url_base + str(i+1) for i in range(page_num)]
loop = asyncio.get_event_loop()
ret_list = []
tasks = [getPage(host,ret_list) for host in page_urls]
loop.run_until_complete(asyncio.wait(tasks))

articles_url = []
for ret in ret_list:
    with parseListPage(ret) as tmp:
        articles_url += tmp
ret_list = []

tasks = [getPage(url, ret_list) for url in articles_url]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()