環境システム株式会社公式HP

〒660-0083 兵庫県尼崎市道意町7-1-3
尼崎リサーチ・インキュベーションセンター512

アイコン06-6657-5130

アイコンsales@hydrolab.co.jp

お問い合わせ

アイコン06-6657-5130

アイコンsales@hydrolab.co.jp

お問い合わせ

蛇使いな彼女BLOG

【第130回】非同期処理とFletアプリのその後~asyncioタイムアウト成功とWindows11のビルド方法~

2025.06.06

環境システム モトハシです。

あれから数日粘った結果、fletアプリを起動している状態でのみ、asyncioが正常に起動していることが確認できました!!(どうやらイベントループでしかasyncioが使えないという事だったみたいです!)

そしてタイムアウト処理の方法は、

通常の関数に非同期処理によるコントロールを追加したい場合、

def ⇒ async def の書き換えに加え "await asyncio.sleep"の一行を任意の場所に追加するだけでOKでした💡

class data_request():
    def __init__(self,list,time_1,time_2):

    ・・・(省略)

    async def exe(self,):

        for i in range(0,self.Days+1):
            key=self.start_date+dt.timedelta(days=i)

            url="{}{}&block_no={:04}&year={}&month={}&day={}&view=".format(self.URL,self.prec,self.block,key.year,key.month,key.day)
            with urllib.request.urlopen(url) as response:
                r= response.read()

            # Read HTML
            soup =bs4.BeautifulSoup(r, 'html.parser')

            try:
                context = soup.find("table",{ "class" : "data2_s" })
                tab=[]
                for row in context.find_all('tr'):
                    cells=row.find_all('td')
                    if len(cells) == 0:
                        continue
                    tab.append([cell.get_text() for cell in cells])

            ・・・(省略)

                await asyncio.sleep(0.1)

            except AttributeError as error:
                print('loop Index: {}\n{}'.format(key,error))
                await asyncio.sleep(0.1)

        return self.result.values


    async def SELECTED_TAB(select):
        if select == 0:

        ・・・(省略)

                # Wait until scraping is finished

                task = asyncio.wait_for(inst.exe(), timeout=60)  # 60秒でタイムアウト
                try:
                    task_result = await task
                    View.controls[2].content.controls.pop()
                    temporary['result']=task_result
                    temporary['columns']=inst.cols  
                    Notification.content.value='The data extraction process is complete.'
                    Notification.bgcolor=ft.Colors.CYAN_300,
                    page.open(Notification)
                    page.update()

                except asyncio.TimeoutError: 
                    print("タスクがタイムアウトしました")
                    View.controls[2].content.controls.pop()
                    temporary['result']=None
                    temporary['columns']=inst.cols
                    Notification.content.value='Timeout expired.\nPlease shorten the date range.'
                    Notification.bgcolor=ft.Colors.PINK_300
                    page.open(Notification)
                    page.update()

上記に書き換えた状態で実行すると、ケースバイケースでこのような結果が返ってきます。

loop Index: 2025-05-07 00:00:00
'NoneType' object has no attribute 'find_all'
0
タスクがタイムアウトしました
>>>

60秒のタイムアウトはネット環境にも左右されますが、モトハシが試したところ、スクレイピングの有効日付範囲としては大体3か月程度となります。

データ取得10分間隔 ⇒ 90日×24時間×6=12960回/60秒

この場合、1秒あたり216回データを取りに行ってる計算となり、1回のループにかかる時間は約0.0046秒です。

asyncio.sleep(0.1)なので、本来ループ1回あたり0.1秒のスリープとなるはずですが、0.0046秒しか掛かっていないことから非同期状態と同期状態の時間の流れは違う動きをしていることが分かります。

ちなみに、どうしてスリープさせるのか?

この理由について、基本的にasyncio.sleep(またはtime.sleep)はサーバー上の処理の負荷を軽減する意味があるとのことです。

目安としてスリープ時間はだいたい1秒~3秒程度らしいですが、

今回の例では、1秒にしてしまうと一度に取得できるデータ範囲が1か月程度となってしまうため、

利便性の面から0.1秒としました。

実際はもっとスリープ時間を長くしたほうがよさそうです。

このように、開発者としては使う側の快適さと、サーバーや他の利用者の権利の両方考えなくてはいけないという事ですね💦


flet0.27+Windows11+Python3.9.21 conda 環境によるビルド

最後に出来上がったアプリケーションのビルドですが、最新のflet 0.27.6 (-2025.05.07現在)とWindows11およびPython3.9.21と相性が悪い(?)ようで、

ビルドに成功してもOSや依存関係が原因でアプリの実行エラーになってしまいました。

requirements.txtの追加やWindows Defenderの設定を変えても改善しなかったのですが、fletのバージョンを 0.27.5にダウングレードすることで正常にアプリが立ち上がることが分かりました!!

その時の実際のログとpyproject.toml、requirements.txtを記述しておきます。

[project]
name = "myapp"
version = "0.1.0"
description = ""
readme = "README.md"
requires-python = ">=3.9"
authors = [
    { name = "Flet developer", email = "you@example.com" }
]
dependencies = [
  "flet[all]>=0.27.5",
  "flet_map==0.1.0",
  "certifi",
  "pandas",
]

[tool.flet]
# org name in reverse domain name notation, e.g. "com.mycompany".
# Combined with project.name to build bundle ID for iOS and Android apps
org = "com.mycompany"

# project display name that is used as an app title on Android and iOS home screens,
# shown in window titles and about app dialogs on desktop.
product = "KOOTA"

# company name to display in about app dialogs
company = "Flet"

# copyright text to display in about app dialogs
copyright = "Copyright (c) 2025 hydrolab.com"

[tool.flet.app]
path = "src"

[tool.uv]
dev-dependencies = [
    "flet[all]>=0.27.5",
]

[tool.poetry]
package-mode = false

[tool.poetry.group.dev.dependencies]
flet = {extras = ["all"], version >= "0.27.5"}
flet==0.27.5
flet_map==0.1.0
beautifulsoup4==4.13.4
numpy==2.*
pandas== 2.*
certifi==2025.1.31

以上、参考になれば幸いです!

今回はここまでです♪

pagetop