Bonjour à tous,

J'espère que tout va bien pour vous en ces temps de crise sanitaire.

Pour un projet j'ai réalisé un programme en Python. Ce programme permet de scraper (avec bs4) un site de bourse et ensuite d'afficher les valeurs scrapées dans un graphique sur une fenêtre Tkinter. J'ai ajouté un thread pour pouvoir scraper les valeurs tout en continuant à mettre à jour le graphique.
Le programme fonctionne correctement à première vue, mais au bout du deuxième ou troisième lancement il plante et affiche une erreur "main thread is not in mainloop()" puis "une erreur est survenue lors du démarrage du noyau" ou "Tcl_AsyncDelete: async handler deleted by the wrong thread".

J'ai essayé de jouer sur les temps de boucle (after et time.sleep) et j'ai mis des join() pour être sûr que le thread se termine en fermeture du programme.
J'ai effectué différentes recherches, mais je n'ai toujours pas trouvé solution à mon problème.

Code :

Fichier principale
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
 
#Librairies
import tkinter as tk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib
matplotlib.use('Agg')
import time
import pandas as pd
from threading import Thread
from tkinter import messagebox
 
#Importation du fichier scraping.py
import scraping
 
#variables
global plot_counter
plot_counter= 0
global launch_scraping
launch_scraping = False
global df
 
df = pd.DataFrame(columns = ['value', 'time', 'trend'])
 
#Appelle une fois la fonction start_scraping  dans scraping.py
def data_scraping():
    global df
    print("Scraping")
    df=scraping.start_scraping(df)
    df = df.sort_values('time')
    time.sleep(5)
 
#Appelle data_scraping si launch scraping est True    
def scrap_thread():
    global launch_scraping
    print("Thread beginning")
    while True:
        if launch_scraping:
            data_scraping()
        else:
            break
    print("Thread ending")
 
 
# Si scrap_button est True launch scraping devient false et inversement
#S'il est faux => lance le thread et la fonction refresh
def click_scrap_button(event):
    global launch_scraping
    if launch_scraping:
        launch_scraping = False
        scrap_button_var.set("Launch scraping")
    else:
        scrap_button_var.set("Stop scraping")
        launch_scraping = True
        global thread_1
        thread_1=Thread(target=scrap_thread)
        thread_1.start()
        refresh()
 
 
#refresh le graphique dans la fenêtre tkinter avec les nouvelles données scrapées      
 
def refresh():
    global plot_counter
    global launch_scraping   
    if not launch_scraping:
        thread_1.join()
        print("No refreshing")
        return    plot_counter += 1
    print("Refreshing")        # deleting previous line
    ax.lines.pop(0)       
        # create a new line  
    ax.plot(df.time[:plot_counter], df.value[:plot_counter])
    canvas.draw()        
        #wait 5 s then call refresh again
    app.after(8000, refresh)
 
#Permet de quitter l'application
def quit_app():
    global launch_scraping
    launch_scraping = False
    thread_1.join()
    if messagebox.askokcancel("Quit", "Do you want to quit?"):
        app.destroy() #Creating the scraping application
 
 
#Initialisation de la fenêtre tkinter
 
app = tk.Tk()
frame_top = tk.Frame(app)
frame_top.pack(side='top', fill="both")
frame_title = tk.Frame(app)
frame_title.pack(fill="both")
title=tk.Label(frame_title, text="DAX price of share")
title.pack()
frame_center = tk.Frame(app)
frame_center.pack(fill="both")
frame_under_fig=tk.Frame(app)
frame_under_fig.pack(fill="both")
scrap_button_var= tk.StringVar()
scrap_button=tk.Button(frame_under_fig, textvariable=scrap_button_var)
scrap_button.pack()
scrap_button_var.set("Launch scraping")   fig = Figure()
canvas = FigureCanvasTkAgg(fig, frame_center)
canvas.get_tk_widget().pack()  
ax = fig.add_subplot(111)
ax.plot([], [])
app.bind("<ButtonRelease-1>", click_scrap_button) 
app.protocol("WM_DELETE_WINDOW", quit_app)
app.mainloop()

fichier scraping.py

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
 
import pandas as pd
from bs4 import BeautifulSoup
from datetime import datetime,timedelta
import requests
 
#Scrap les valeurs de la bourse et les insère dans une dataframe
def start_scraping(dataframe):
 
 
    info_scrap={}
 
    page = requests.get("https://www.boursorama.com/bourse/indices/cours/5pDAX/")
 
    soup = BeautifulSoup(page.text,'lxml')
 
    # we assign to "value" the last value of exchange rate we scraped
    value= soup.find("span",attrs={"class":"c-instrument c-instrument--last"}).text
    # this value is a string, we need to replace space by empty string before converting it into float
    value=value.replace(' ','')
    print(value)
    #we assign to trend the last value of the trend we scraped
    trend=soup.find("span",attrs={"class":"c-instrument c-instrument--variation"}).text
 
    current_time=datetime.now()
 
 
 
    # value is a string we convert it into float and then add it to the dictionnary info_scrap
    info_scrap["value"] = float(value)
    info_scrap["trend"]=trend
    info_scrap["time"]=current_time
 
    # each value of the dictionnary are appended to the dataframe
    dataframe=dataframe.append(info_scrap, ignore_index=True)
    # the function return the dataframe we put in parameter
    return dataframe

Merci d'avance pour votre aide,
Bonne journée,
Webuzo