0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

.run_in_executorによる非同期処理はwait関数を無視して次のコード行へ移る

Posted at

Seleniumを非同期で使い、ページ収集したかった。しかし、収集前に処理が終了してしまうため、困っていた。

原因は.run_in_executorであった。.run_in_executorは同期関数を非同期化できる関数である。
しかし、loop.run_until_completeと併用でコード完了までプログラムは実行できるかもしれないが、
loop.run_until_completeの次の行へ処理が移るので、非同期処理群を完了してから、次の処理に移りたい時には使えない。最初どこに原因があるか分からず、asyncio.wait併用で止めようと試みた。知らなかったのでMEMO.

.run_in_executorによる非同期化 素直な非同期化関数
.run_until_completeで非同期可処理群の終了までプログラム終了を待つか? YES? YES
asyncio.waitと併用で、非同期可処理群の終了までコード停止。次の行へ処理進まないか? NO YES
  • 問題のコード
"""
参考元: https://stackoverflow.com/questions/50303797/python-webdriver-and-asyncio
こちらのコードは、loop.run_until_completeがあっても、取得未完了でも、次の行のコードに到達してしまう。
"""
import asyncio
from concurrent.futures.thread import ThreadPoolExecutor

from selenium import webdriver
from loguru import logger
from selenium.webdriver.chrome import service

executor = ThreadPoolExecutor(10)


def scrape(url, *, loop):
    logger.debug(f"scrape func in. {url=}")
    # 注意) .run_in_executorは同期関数を非同期化できる関数である。
    # しかし、loop.run_until_completeと併用でコード完了までプログラムは実行できるかもしれないが、
    # loop.run_until_completeの次の行へ処理が移るので、非同期処理群を完了してから、次の処理に移りたい時は使えない
    loop.run_in_executor(executor, scraper, url)


def scraper(url):
    logger.debug(f"scraper func in. {url=}")
    chromedriver = '/opt/homebrew/bin/chromedriver'
    chrome_service = service.Service(executable_path=chromedriver)
    driver = webdriver.Chrome(service=chrome_service)
    driver.get(url)
    print(driver.page_source)
    print("END of scraper func")
    return driver.page_source


loop = asyncio.get_event_loop()
for url in ["https://www.google.com/"] * 10:
    scrape(url, loop=loop)

res = loop.run_until_complete(asyncio.gather(*asyncio.all_tasks(loop)))
asyncio.wait(res, return_when=asyncio.ALL_COMPLETED) # 非同期関数処理が終了前にこちらの行へ移る。
loop.close()
print(f".run_until_completeを抜けた!:{res=}")

解決策は.run_in_executorを使わず、素直に非同期関数として実装する。

  • 修正後のコード
"""
https://stackoverflow.com/questions/50303797/python-webdriver-and-asyncio
こちらのコードは、`loop.run_until_complete`で次の行のコードに到達せず待つ。
"""
import asyncio
from concurrent.futures.thread import ThreadPoolExecutor

from selenium import webdriver
from loguru import logger
from selenium.webdriver.chrome import service

executor = ThreadPoolExecutor(10)


async def scrape(url, *, loop): # 非同期関数化した。
    logger.debug("scrape func in")
    await loop.run_in_executor(executor, scraper, url)


def scraper(url):
    logger.debug(f"scraper func in. {url=}")
    chromedriver = '/opt/homebrew/bin/chromedriver'
    chrome_service = service.Service(executable_path=chromedriver)
    driver = webdriver.Chrome(service=chrome_service)
    driver.get(url)
    print("scraper func3")
    print(driver.page_source)
    print("END of scraper func")
    return driver.page_source


loop = asyncio.get_event_loop()
for url in ["https://www.google.com/"] * 10:
    scrape(url, loop=loop)

res = loop.run_until_complete(asyncio.gather(*[scrape(url, loop=loop) for url in ["https://www.google.com/"] * 10]))
print(f".run_until_completeを抜けた!")  # gatherで非同期処理すべて終わってからこちらのコードへ移る
print(res)
0
0
0

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?