상세 컨텐츠

본문 제목

[인프런 리프 2기] 13. 파이썬 중급 과정 4주차(3) 최종실습

경험/2021 인프런리프2기

by mizu-umi 2021. 4. 3. 17:30

본문

728x90

관련글 ▼

[인프런 리프 2기] 10. 파이썬 중급 과정 3주차(4) 일급함수-3,4

[인프런 리프 2기] 11. 파이썬 중급 과정 4주차(1) 병행성

[인프런 리프 2기] 12. 파이썬 중급 과정 4주차(2) 병렬성

 


 

 

파이썬 중급 최종실습 - AsyncIO

  • 가상환경 생성
  • 타겟 사이트 살펴보기
  • 프로그램 설명
  • async, await 설명
  • 프로그램 테스트

드디어 강의의 마지막에 도달했다! 갑자기 이런저런 일이 겹쳐서 오늘에서야 마무리 짓게 되었는데 내가 가장 궁금했던 내용이었으나 아직 뭐가 뭔지 모르겠다...ㅋㅋㅋㅋㅋ

 



AsyncIO

비동기 I/O Coroutine 작업을 쉽게 할 수 있도록 도와주는 모듈이다. Generator가 반복적인 객체를 return을 사용해서 받는다면 asyncIO는 Non-blocking 비동기 처리를 해준다. 참고로 pip install을 통해 설치해야만 사용 가능하다.

 

  • Blocking I/O : 호출된 함수가 자신의 작업이 완료될 때까지 제어권을 가짐. 타 함수는 대기해야함
  • NonBlocking I/O : 호출된 함수(subroutine)가 return후 호출한 함수(mainroutine)에 제어권을 전달한다 -> 타 함수는 일을 지속한다.

장단점

  • 스레드 단점 : 디버깅이 어렵고, 자원 접근 시 레이스 컨디션(race condition), 데드락 -> 고려 후 코딩
  • 코루틴 장점 : 하나의 루틴만 실행해서 lock을 관리할 필요가 없음. 제어권으로 실행한다.
  • 코루틴 단점 : 사용함수가 비동기로 구현이 되어 있어야 하거나 또는 직접 비동기로 구현해야 한다. 

멀티 스크래핑 실습

import asyncio
import timeit
from urllib.request import urlopen
from concurrent.futures import ThreadPoolExecutor
import threading

 

  • urlopen은 blocking 함수이기때문에 asyncio로 구현하려는 효과가 크지 않다.
  • asyncio 모듈을 쓰려면 코루틴을 thread와 process를 결합해서 사용해야한다.

crawling으로 데이터를 수집할때 자동화된 코드를 만들어서 웹페이지에서 데이터를 수집하자. 단일스레드보다 블록IO를 사용하면 정확하고 빠르게 크롤링이 가능하다.

 

1. 우선 실행 시작 시간과 크롤링할 웹사이트의 리스트를 만든다.

# 실행 시작 시간
start = timeit.default_timer()

# 서비스 방향이 비슷한 사이트로 실습
urls = ['http://daum.net','http://naver.com','http://tistory.com']

 

2. 그 다음, 실행문을 작성한다.

if __name__ == '__main__':
    # 루프 초기화 : 제어권을 서로 주고 받겠다
    loop = asyncio.get_event_loop() 

    # 작업 완료까지 대기
    loop.run_until_complete(main())
    # loop를 끝날때까지 돌린다.

    # 수행 시간 계산
    duration = timeit.default_timer() - start

    # 총 실행 시간
    print('Total Running Time : ', duration)
  • asyncio.get_event_loop() : 현재의 이벤트 루프를 가져온다.

 

3. 실행문을 작성하고 main() 함수를 만든다.

async def main():
    # 스레드 풀
    executor = ThreadPoolExecutor(max_workers=10)

    # future 객체를 모아서 gather에서 실행
    futures = [
        asyncio.ensure_future(fetch(url, executor)) for url in urls
    ]

    # 결과 취합
    rst = await asyncio.gather(*futures)

    print()
    print('Result : ', rst)
  • 함수를 구별하는 방법
    • def만 보고는 closure인지 generator인지 구별할 수 없다. async, 즉 비동기 함수라는 것을 명시하기 위해서 def 앞에 async를 작성한다.
  • max_workers=N : ThereadPoolExecutor에서 최대 몇개의 worker를 돌릴지 지정해준다.
  • ensure_future(obj, *, loop=None) 메소드 : obj인자를 future류인지 검사한다.
  • await : generator의 yield와 같다.

 

4. 앞서 작성한 main() 함수에 호출할 함수를 작성한다.

async def fetch(url, executor):
    # 쓰레드 명 출력
    print('Thread Name : ', threading.current_thread().getName(), 'Start', url)

    # 실행
    res = await loop.run_in_executor(executor, urlopen, url)

    print('Thread Name : ', threading.current_thread().getName(), 'Done', url)

    # 결과 반환
    return res.read()[0:5]
  • threading : 스레드 기반.
  • threading.current_thread() : 호출자의 제어 스레드에 해당하는 현재 Thread 객체를 반환한다.
  • getName() : 스레드 객체의 식별 이름을 가져오는 것.
  • run_in_executor(executor,func,*args) : 지정된 실행기에서 func 가 호출되도록 배치한다.

BeautifulSoup4

뷰티플수프는 HTML과 XML 파일로부터 데이터를 뽑아내기 위한 파이썬 라이브러리이다. 여러분이 선호하는 해석기와 함께 사용하여 일반적인 방식으로 해석 트리를 항해, 검색, 변경할 수 있다. 주로 프로그래머의 수고를 덜어준다.(출처)

 

from bs4 import BeautifulSoup
  • asyncIO처럼 별도로 import해서 사용해야한다. 상기에서 사용한 코드에 추가한다.

 

async def fetch(url, executor):
    # 쓰레드 명 출력
    print('Thread Name : ', threading.current_thread().getName(), 'Start', url)

    # 실행
    res = await loop.run_in_executor(executor, urlopen, url)
    
    soup = BeautifulSoup(res.read(), 'html.parser')

    # 전체 페이지 소스 확인
    print(soup.prettify())

	# 제목만 가져오기
    result_data = soup.title
    
    print('Thread Name : ', threading.current_thread().getName(), 'Done', url)

    # 결과 반환
    return result_data
  • 기본구조 : BeautifulSoup(html_doc, 'html.parser')
    • 원하는 결과를 가져오고 싶으면 객체.bs4의속성을 가져오면된다.
  • ex. soup.prettify() / soup.title

< 강의 출처 - 인프런 우리를 위한 프로그래밍 파이썬 중급 (Inflearn Original) >

728x90
반응형

관련글 더보기

댓글 영역