質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
並列処理

複数の計算が同時に実行される手法

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

1403閲覧

PythonでTkinterを使ったコードで並列処理がうまくいかない。2つめの処理でウイジェットの値を取得できない。

Q_1986-kt

総合スコア13

並列処理

複数の計算が同時に実行される手法

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2020/06/04 08:23

やりたいこと

PythonでTkinterを使って巡回するURLや読み込むタイミングの日時などを設定し
並列処理で最大2ヶ所のURLをスクレイピングするプログラムを作っています。
以前にも質問をさせてもらってhayataka2049さんに教えてもらった方法で
無事に並列処理はできるようになったのですが、せっかくなのでGUIを使って
アプリケーションみたいに使いたいと思ってTkinterでGUIを作りました。

しかし、1ヶ所の巡回はできるのに、2ヶ所同時に巡回することができません。
自分なりに順を追って調べた結果、URLなどのデータをTkinterのウイジェットから
読み込むタイミングでちゃんと読み込めていないようです。

試したコード

import os import time import sched import concurrent.futures from datetime import datetime from datetime import timedelta from selenium import webdriver import tkinter as tk import tkinter.ttk as ttk # URL def url_select(u): if u == "Google": url = ("https://www.google.com/") elif u == "Yahoo!": url = ("https://www.yahoo.co.jp/") return url def scrap1(url, stop, delay): while (1): dt = datetime.now() et = dt.strftime("%Y/%m/%d %H:%M:%S") if str(dt.second) == delay: driver_path = "C:/Users/takak/Downloads/chromedriver.exe" driver = webdriver.Chrome(driver_path) driver.get(url) time.sleep(1) driver.quit() elif et >= stop: break def scrap2(url, stop, delay): while (1): dt = datetime.now() et = dt.strftime("%Y/%m/%d %H:%M:%S") if str(dt.second) == delay: driver_path = "C:/Users/takak/Downloads/chromedriver.exe" driver = webdriver.Chrome(driver_path) driver.get(url) time.sleep(1) driver.quit() elif et >= stop: break class Application(tk.Frame): def __init__(self, master): super().__init__(master) self.pack() self.master.geometry("300x150") self.master.title("てすと") self.widget() def widget(self): self.frame = ttk.Frame(self) self.frame.pack(anchor="nw", side="top") # self.button1 = tk.Button(self.frame, width=12, text="Start", command=callback) # 実行中押されたままになる self.button1 = Start_button(self.frame, width=12, text="Start")      # 押した時に状態が変化しない実行されたどうかわからない self.button1.grid(row=0, column=2, padx=10, pady=10) self.bln1 = tk.BooleanVar() self.bln1.set(True) self.chk_url1 = tk.Checkbutton(self.frame, variable=self.bln1, text="URL 1") self.chk_url1.grid(row=1, column=0, padx=10, pady=10) self.url1_combo = ttk.Combobox(self.frame, width=10, state="readonly") self.url1_combo["values"] = ("Google", "Yahoo!") self.url1_combo.current(0) self.url1_combo.grid(row=1, column=1, padx=10, pady=10) self.bln2 = tk.BooleanVar() self.bln2.set(True) self.chk_url2 = tk.Checkbutton(self.frame, variable=self.bln2, text="URL 2") self.chk_url2.grid(row=2, column=0, padx=10, pady=10) self.url2_combo = ttk.Combobox(self.frame, width=10, state="readonly") self.url2_combo["values"] = ("Google", "Yahoo!") self.url2_combo.current(1) self.url2_combo.grid(row=2, column=1, padx=10, pady=10) # 読み込みスタートボタンクラスを使うとボタンの状態が変化しない????? class Start_button(tk.Button): def __init__(self, master=None, cnf={}, **kw): tk.Button.__init__(self, master, cnf, **kw) self.bind("<Button-1>", callback) # スクレイピング1 # def scraping1(): # テスト1 & テスト2 def scraping1(url1): # URLの設定 # url1 = "https://www.google.com/" # テスト1 # URL_1 = app.url1_combo.get() # テスト2 # url1 = url_select(URL_1) # 開始&終了時間の設定 実際のコードでは、日付と時間の設定もGUIで設定 start_time1 = "2020/06/04 16:14:00" stop_time1 = "2020/06/04 16:15:00" delay_time1 = "0" # スケジューラー scheduler = sched.scheduler(time.time, time.sleep) run = datetime.strptime(start_time1, "%Y/%m/%d %H:%M:%S") run = int(time.mktime(run.utctimetuple())) scheduler.enterabs(run, 1, scrap1, (url1, stop_time1, delay_time1)) scheduler.run() # スクレイピング2 # def scraping2(): # テスト1 & テスト2 def scraping2(url2): # URLの設定 # url2 = "https://www.yahoo.co.jp/" # テスト1 # URL_2 = app.url2_combo.get() # テスト2 # url2 = url_select(URL_2) # 開始&終了時間の設定 実際のコードでは、日付と時間の設定もGUIで設定 start_time2 = "2020/06/04 16:14:00" stop_time2 = "2020/06/04 16:15:00" delay_time2 = "30" # スケジューラー scheduler = sched.scheduler(time.time, time.sleep) run = datetime.strptime(start_time2, "%Y/%m/%d %H:%M:%S") run = int(time.mktime(run.utctimetuple())) scheduler.enterabs(run, 1, scrap2, (url2, stop_time2, delay_time2)) scheduler.run() # 並行処理 # def callback(): def callback(event): patrol1 = app.bln1.get() patrol2 = app.bln2.get() # URL_1 = app.url1_combo.get() # テスト3 # url1 = url_select(URL_1) # URL_2 = app.url2_combo.get() # テスト3 # url2 = url_select(URL_2) if patrol1 == True and patrol2 == False: print("スクレイピング・テスト1") URL_1 = app.url1_combo.get() # テスト4 url1 = url_select(URL_1) # scraping1() scraping1(url1) elif patrol1 == True and patrol2 == True: with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor: print("スクレイピング・テスト2") URL_1 = app.url1_combo.get() # テスト4 url1 = url_select(URL_1) # executor.submit(scraping1()) executor.submit(scraping1(url1)) URL_2 = app.url2_combo.get() # テスト4 url2 = url_select(URL_2) # executor.submit(scraping2()) executor.submit(scraping2(url2)) if __name__ == "__main__": root = tk.Tk() app = Application(master=root) app.mainloop()

やってみたこと

はじめに、テスト1としてURLを並列処理を実行する関数 def scraping1(): とdef scraping2():に
直接記入して実行、無事に並列処理を実行できる。

次に、テスト2として1と同じ位置でget()関数を使って読み込み先を選択
def url_select(u): でURLを選択して実行
1ヶ所目は実行できるが2か所目が実行できない。

get()で読み込むタイミングをテスト3、4と変えてみたがすべて同じで2か所目のURLを取得できません。

2か所目の設定を取得するには、どうすればいいのでしょうか

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

問題の再現は出来ませんでしたが、(start_time の編集が必要?)
疑いのある個所

executor.submit(scraping1(url1))

は、並列処理になってません。

メインスレッド側で scraping1(url1) が実行され、
戻り値である None が executor.submit に渡ります。

scraping1() 関数内に scheduler.run() があるので、そこで止ってるのではないでしょうか。

もう一点、scrape1, scrape2 関数の
if str(dt.second) == delay: 現在時刻の秒数がdelay なら~となってます、
scheduler により時間は指定してるので、時刻それも秒数の確認は不要だと思いますが、
意図通りの条件か確認してみてください。

追記: コメントにより意図通りの挙動であると確認。


追記

解決方法: 並列処理(executor.submit) の部分の使い方
executor.submit(fn, *args, **kwargs)

引き数を渡すには、以下のように変更します

executor.submit(scraping1, url1)

また、scraping1, scraping2 や scrape1, scrape2 はほぼ同じコードなので、
差分である delay も引数にして、同じ関数を使うと良いです。

投稿2020/06/04 16:16

編集2020/06/05 04:40
teamikl

総合スコア8664

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Q_1986-kt

2020/06/05 03:48

teamiklさん、ありがとうございます。 動かなかったのは、start_timeとstop_timeを変更していないからだと思います。 それと、chromedriverのある場所も変更しないとダメです。 start_timeで指定した日時の時間が来たらスクレイピングを始め、 stop_timeの時間まで繰り返し指定したURLを読みに行きます。 現在時刻の秒数 delay(名前がよくなかったかもしれませんが scraping1では 0 ) は、 1分ごとに読みに行くようにしているためです。 例:start_time : 2020/06/05 13:00:00 この時間でスクレイピングを始め stop_time : 2020/06/07 23:55:00 この日のこの時間まで繰り返し実行する。 読みに行くタイミングは、1分毎、13:01:00、13:02:00 、、、 2つ目の処理、scraping2 でも同じように1分ごとに指定したURLを読みに行きます。 delay は 30 にしています。これは、少しでも負担を軽くするため30秒ずらしています。 13:01:30、13:02:30、、、、、この繰り返し。 実際に使用するコードでは、この日付、時間もTkinterで指定するようにしています。
teamikl

2020/06/05 04:19

なるほど、秒数確認の意図は理解できました。解決方法を回答に追記します。
Q_1986-kt

2020/06/05 15:24

ありがとうございました。 関数の渡し方なんですね。 いろいろ勉強しながらやっているので、同じようなことを繰り返していたり 関数の部分もクラスを使ってみたりして、悩みながらやっています。 本番のコードでは、もう一度整理をして見直してみたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問