Python

Python Asyncio - 코루틴, 크롤링

foxlee 2022. 1. 18. 16:15

복수개의 사이트에서 웹 페이지 가져오는 작업을 비동기로 구현하여 속도 개선

코루틴

  • async/await 문법으로 선언된 코루틴은 asyncio 응용 프로그램을 작성하는 기본 방법

Awaitable Object

  • await 표현식에서 사용될 수 있을 때 어웨이터블 객체 - 직접 async 함수를 작성하거나  외부 라이브러리의 경우 async로 구현된 것을 사용해야함

Future

  • 비동기 연산의 최종 결과를 나타내는 특별한 저수준 awaitable object로, Future 객체를 기다릴 때, 그것은 코루틴이 Future가 다른 곳에서 해결될 때까지 기다릴 것을 뜻합니다. 콜백 기반 코드를 async/await와 함께 사용하려면 asyncio의 Future 객체가 필요합니다.

아래 코드에서 순서

  1. loop = get_event_loop() 현재 이벤트 루프를 가져옴
  2. loop.run_until_complete(async_fun(data_list)) async_fun 안에서 future들이 완료할때까지 실행
  3. async_fun 함수가 실행되고 그 안에서 비동기 작업의 함수를 append 하여 future list를 만들고
  4. gather awaitable 객체들을 gather 호출로 동시에 실행됨
## async를 적용한 여러 페이지 로딩하는 부분만, 그외 부분은 삭제함.
import asyncio
import aiohttp

# ./cralwer.py
class crawler:

    async def load(self, session):
        async with session.get(
            self._parse_url, headers=self._set_header()
        ) as response:
            text = await response.read()
            self._doc = BeautifulSoup(text, "lxml")

#./main.py
async def async_load_site(session, crawl):
	# 4
    await crawl.load(session) 
    # await : 다른 async 함수를 호출할때는 await(즉 await를 쓰려면 async를 써줘야함)
	# await : 를 정의한 곳에서 기다리지 않고 다른 작업에 권한을 넘겨줌(다른 작업을 진행)
    
async def load_all_sites(crawlers):
    async with aiohttp.ClientSession() as session:
        load_list = []
		# 2
		for crawl in crawlers:
        	# ensure_future : future - 작업 객체가 스케쥴 됨
            load = asyncio.ensure_future(async_load_site(session, crawl))
            load_list.append(load)
		# 3
		# ensure_future 의 작업 객체들을 동시 실행, 끝날때까지 기다림 결과를 리턴 받을 수도 있음
        await asyncio.gather(*load_list, return_exceptions=True)
        

def run_crawler():
    keywords = ["keyword1", "keyword2", "keyword3"]
    crawler_objects = [
        CrawlerCambridge,
        CrawlerCollins,
        CrawlerMacmillan,
        CrawlerMerriam,
        CrawlerOxford,
    ]

    crawlers = []
    for keyword in keywords:
        definitions = []
        examples = []

        for crawler_object in crawler_objects:
            crawler = crawler_object(logging)
            crawler.keyword = keyword
            crawler.keyword_type = keyword_type
            crawler.set_parse_url()
            crawlers.append(crawler)

	# 1
	# get_event_loop() : 이벤트 루프를 가져옴
    # run_until_complete : future(Future의 인스턴스)가 완료할 때까지 실행 / 퓨처의 결과를 반환하거나 퓨처의 예외를 일으
    asyncio.get_event_loop().run_until_complete(load_all_sites(crawlers))
    

    # 페이지 비동기로 모두 로딩 후의 로직
    for crawler in crawlers:
        crawler.parse()
        definitions.extend(crawler.definitions)
        examples.extend(crawler.examples)


if __name__ == "__main__":
       run_crawler()