外部文件的 class 函数有问题:'NoneType' object 没有属性 'iloc'
Problem with a function of a class of an external file: 'NoneType' object has no attribute 'iloc'
我有一个 window 可以在文本框中打印新闻内容。您必须先单击“查看标题”按钮,然后单击 select 标题,最后单击“查看内容”按钮。它工作正常,没有任何问题。
在这个问题中,我将 window 代码放在外部文件 class 中 (page1.py)
。该文件将在主文件 (main.py)
中打开。如果我点击“查看标题”按钮,它工作正常,但如果我点击“查看内容”按钮,我在函数 def content
:
中出现错误
item = df.iloc[selection[-1]]
AttributeError: 'NoneType' object has no attribute 'iloc'
我希望当我点击“查看内容”按钮时,新闻的内容打印在文本框中。正如我上面所说,代码在一个只有一个 window 的简单文件中时......它工作得很好,而现在如果放在外部 class 我有问题。
我是 Python 的新手。你能告诉我答案中的代码吗?否则我可能看不懂,谢谢和抱歉
Main.py
import tkinter as tk
from tkinter import ttk
from PIL import ImageTk
from page1 import Page1
root = tk.Tk()
root.geometry('480x320')
topbar = tk.Frame(root, bg='#e10a0a', height=43)
topbar.pack(fill='x')
style = ttk.Style()
style.theme_use('default') # select a theme that allows configuration of ttk.Notebook
# put the tabs at the left with white background
style.configure('TNotebook', tabposition='wn', background='white', tabmargins=0)
# configure tab with white background initially, yellow background when selected
style.configure('TNotebook.Tab', background='white', width=10, focuscolor='yellow', borderwidth=0)
style.map('TNotebook.Tab', background=[('selected', 'yellow')])
nb = ttk.Notebook(root)
nb.pack(fill='both', expand=1)
page1 = Page1(nb)
nb.add(page1, text='aaaaa', compound='left')
root.mainloop()
page1.py
import tkinter as tk
from tkinter import ttk
from tkinter import *
from tkinter import ttk
import tkinter as tk
import tkinter.font as tkFont
from tkinter import ttk
import tkinter as tk # PEP8: `import *` is not preferred
from tkinter import ttk
from tkinter.scrolledtext import ScrolledText
import requests
import requests_cache
from bs4 import BeautifulSoup
import pandas as pd
import re
import json
from dateutil import parser
import datetime
import locale
class Page1(tk.Frame):
def __init__(self, master, **kw):
super().__init__(master, **kw)
def get_data_for(place):
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'
}
results = []
response = requests.get(f'https://www.tuttomercatoweb.com/{place}/', headers=headers)
print('url:', response.url)
print('status:', response.status_code)
#print('html:', response.text[:1000])
soup = BeautifulSoup(response.content, 'html.parser')
#Cover
cover_news = soup.find('div', {'class':'box pp'})
link = cover_news.find('a', href=True)['href']
coverNewsResponse = requests.get(link, headers=headers)
coverSoup = BeautifulSoup(coverNewsResponse.content, 'html.parser')
jsonStr = str(coverSoup.find('script'))
jsonStr = re.search('({.*})', jsonStr).group(1)
jsonData = json.loads(jsonStr)
timePublished = parser.parse(jsonData['datePublished']).strftime("%H:%M")
datePublished = parser.parse(jsonData['datePublished']).strftime("%Y-%m-%d")
title = jsonData['headline']
news = f" {timePublished} {place.upper()}, {title} (TMW)"
results.append( [datePublished, timePublished, place, title, news, link] )
# Side panel
side_news = soup.find_all('div', {'class':'box small'})
for each in side_news:
link = each.find('a', href=True)['href']
sideNewsResponse = requests.get(link, headers=headers)
sideSoup = BeautifulSoup(sideNewsResponse.content, 'html.parser')
jsonStr = str(sideSoup.find('script'))
jsonStr = re.search('({.*})', jsonStr).group(1)
jsonData = json.loads(jsonStr)
timePublished = parser.parse(jsonData['datePublished']).strftime("%H:%M")
datePublished = parser.parse(jsonData['datePublished']).strftime("%Y-%m-%d")
title = jsonData['headline']
news = f" {timePublished} {place.upper()}, {title} (TMW)"
results.append( [datePublished, timePublished, place, title, news, link] )
news = soup.find_all('div', attrs={"class": "tcc-list-news"})
for each in news:
for div in each.find_all("div"):
timePublished = div.find('span', attrs={'class': 'hh serif'}).text
datePublished = div.find_previous('div', {'class':'tcc-border upper date'})
if datePublished:
if datePublished.text in ['Ieri']:
yesterday = datetime.datetime.today() - datetime.timedelta(days = 1)
datePublished = yesterday.strftime("%Y-%m-%d")
else:
locale.setlocale(locale.LC_ALL, '') #locale.setlocale(locale.LC_ALL, 'it')
currentYear = datetime.datetime.today().strftime("%Y")
dateStr = datePublished.text
dateStr = datetime.datetime.strptime(dateStr + ' ' + currentYear, '%A %d %B %Y')
datePublished = dateStr.strftime("%Y-%m-%d")
else:
datePublished = datetime.datetime.today().strftime("%Y-%m-%d")
title = " ".join(span.text for span in div.select("a > span"))
news = f" {timePublished} {place.upper()}, {title} (TMW)"
link = div.find('a')['href']
results.append( [datePublished, timePublished, place, title, news, link] )
return results
def all_titles():
global df
allnews = [] # local variable
for place in ['atalanta', 'bologna']:
print('search:', place)
results = get_data_for(place)
print('found:', len(results))
allnews += results
text_download.insert('end', f"search: {place}\nfound: {len(results)}\n")
df = pd.DataFrame(allnews, columns=['date', 'time', 'place', 'title', 'news', 'link'])
df = df.sort_values(by=['date', 'time', 'place', 'title'], ascending=[False, False, True, True])
df = df.drop_duplicates(subset=['date', 'time', 'place', 'title'])
df = df.reset_index(drop=True)
listbox_title.delete('0', 'end')
for index, row in df.iterrows():
listbox_title.insert('end', row['news'])
def content(event=None): # `command=` executes without `event`, but `bind` executes with `event` - so it needs default value
# tuple
selection = listbox_title.curselection()
print('selection:', selection)
if selection:
item = df.iloc[selection[-1]]
#print('item:', item)
url = item['link']
#print('url:', url)
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'
}
# keep page in database `SQLite`
# https://github.com/reclosedev/requests-cache
# https://sqlite.org/index.html
session = requests_cache.CachedSession('titles')
response = session.get(url, headers=headers)
#response = requests.get(url, headers=headers)
soup = BeautifulSoup(response.content, 'html.parser')
content_download = "\n".join(item.get_text() for item in soup.select("div.text.mbottom"))
text_download.delete('1.0', 'end') # remove previous content)
text_download.insert('end', content_download)
# --- main ---
df = None
# window = tk.Tk()
# window.geometry("800x800")
frame_title = tk.Frame(self)
frame_title.pack(fill='both', expand=True, pady=5, padx=5)
listbox_title = tk.Listbox(frame_title, selectbackground="#960000", selectforeground="white", bg="white")
listbox_title.pack(side='left', fill='both', expand=True)
scrollbar_title = tk.Scrollbar(frame_title)
scrollbar_title.pack(side='left', fill='y')
scrollbar_title['command'] = listbox_title.yview
listbox_title.config(yscrollcommand=scrollbar_title.set)
listbox_title.bind('<Double-Button-1>', content) # it executes `content(event)`
# ----
text_download = ScrolledText(self, bg="white")
text_download.pack(fill='both', expand=True, pady=0, padx=5)
# ----
buttons_frame = tk.Frame(self)
buttons_frame.pack(fill='x')
button1 = tk.Button(buttons_frame, text="View Titles", command=all_titles) # don't use `[]` to execute functions
button1.pack(side='left', pady=5, padx=5)
button2 = tk.Button(buttons_frame, text="View Content", command=content) # don't use `[]` to execute functions
button2.pack(side='left', pady=5, padx=(0,5))
我想你已经声明 df
是嵌套在你的 __init__
方法下的函数之一的全局变量......然后在你的主要部分中,它实际上运行在__init__
,您将 df
设置为 None
。您需要更加小心地使变量成为全局变量,并多考虑一下函数作用域;您定义的那些函数是否需要在 __init__
的范围内,或者它们可以有不同的范围?
解决这个问题的简单方法是在 all_titles()
函数中将 global df
更改为 nonlocal df
。
但是,我建议通过将所有 df
更改为 self.df
:
来使用实例变量
class Page1:
def __init__(self, master, **kw):
...
def all_titles():
...
# changed df to self.df
self.df = pd.DataFrame(allnews, columns=['date', 'time', 'place', 'title', 'news', 'link'])
self.df = self.df.sort_values(by=['date', 'time', 'place', 'title'], ascending=[False, False, True, True])
self.df = self.df.drop_duplicates(subset=['date', 'time', 'place', 'title'])
self.df = self.df.reset_index(drop=True)
listbox_title.delete('0', 'end')
for index, row in self.df.iterrows():
listbox_title.insert('end', row['news'])
def content(event=None):
...
if selection:
item = self.df.iloc[selection[-1]]
...
...
self.df = None
...
我有一个 window 可以在文本框中打印新闻内容。您必须先单击“查看标题”按钮,然后单击 select 标题,最后单击“查看内容”按钮。它工作正常,没有任何问题。
在这个问题中,我将 window 代码放在外部文件 class 中 (page1.py)
。该文件将在主文件 (main.py)
中打开。如果我点击“查看标题”按钮,它工作正常,但如果我点击“查看内容”按钮,我在函数 def content
:
item = df.iloc[selection[-1]]
AttributeError: 'NoneType' object has no attribute 'iloc'
我希望当我点击“查看内容”按钮时,新闻的内容打印在文本框中。正如我上面所说,代码在一个只有一个 window 的简单文件中时......它工作得很好,而现在如果放在外部 class 我有问题。
我是 Python 的新手。你能告诉我答案中的代码吗?否则我可能看不懂,谢谢和抱歉
Main.py
import tkinter as tk
from tkinter import ttk
from PIL import ImageTk
from page1 import Page1
root = tk.Tk()
root.geometry('480x320')
topbar = tk.Frame(root, bg='#e10a0a', height=43)
topbar.pack(fill='x')
style = ttk.Style()
style.theme_use('default') # select a theme that allows configuration of ttk.Notebook
# put the tabs at the left with white background
style.configure('TNotebook', tabposition='wn', background='white', tabmargins=0)
# configure tab with white background initially, yellow background when selected
style.configure('TNotebook.Tab', background='white', width=10, focuscolor='yellow', borderwidth=0)
style.map('TNotebook.Tab', background=[('selected', 'yellow')])
nb = ttk.Notebook(root)
nb.pack(fill='both', expand=1)
page1 = Page1(nb)
nb.add(page1, text='aaaaa', compound='left')
root.mainloop()
page1.py
import tkinter as tk
from tkinter import ttk
from tkinter import *
from tkinter import ttk
import tkinter as tk
import tkinter.font as tkFont
from tkinter import ttk
import tkinter as tk # PEP8: `import *` is not preferred
from tkinter import ttk
from tkinter.scrolledtext import ScrolledText
import requests
import requests_cache
from bs4 import BeautifulSoup
import pandas as pd
import re
import json
from dateutil import parser
import datetime
import locale
class Page1(tk.Frame):
def __init__(self, master, **kw):
super().__init__(master, **kw)
def get_data_for(place):
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'
}
results = []
response = requests.get(f'https://www.tuttomercatoweb.com/{place}/', headers=headers)
print('url:', response.url)
print('status:', response.status_code)
#print('html:', response.text[:1000])
soup = BeautifulSoup(response.content, 'html.parser')
#Cover
cover_news = soup.find('div', {'class':'box pp'})
link = cover_news.find('a', href=True)['href']
coverNewsResponse = requests.get(link, headers=headers)
coverSoup = BeautifulSoup(coverNewsResponse.content, 'html.parser')
jsonStr = str(coverSoup.find('script'))
jsonStr = re.search('({.*})', jsonStr).group(1)
jsonData = json.loads(jsonStr)
timePublished = parser.parse(jsonData['datePublished']).strftime("%H:%M")
datePublished = parser.parse(jsonData['datePublished']).strftime("%Y-%m-%d")
title = jsonData['headline']
news = f" {timePublished} {place.upper()}, {title} (TMW)"
results.append( [datePublished, timePublished, place, title, news, link] )
# Side panel
side_news = soup.find_all('div', {'class':'box small'})
for each in side_news:
link = each.find('a', href=True)['href']
sideNewsResponse = requests.get(link, headers=headers)
sideSoup = BeautifulSoup(sideNewsResponse.content, 'html.parser')
jsonStr = str(sideSoup.find('script'))
jsonStr = re.search('({.*})', jsonStr).group(1)
jsonData = json.loads(jsonStr)
timePublished = parser.parse(jsonData['datePublished']).strftime("%H:%M")
datePublished = parser.parse(jsonData['datePublished']).strftime("%Y-%m-%d")
title = jsonData['headline']
news = f" {timePublished} {place.upper()}, {title} (TMW)"
results.append( [datePublished, timePublished, place, title, news, link] )
news = soup.find_all('div', attrs={"class": "tcc-list-news"})
for each in news:
for div in each.find_all("div"):
timePublished = div.find('span', attrs={'class': 'hh serif'}).text
datePublished = div.find_previous('div', {'class':'tcc-border upper date'})
if datePublished:
if datePublished.text in ['Ieri']:
yesterday = datetime.datetime.today() - datetime.timedelta(days = 1)
datePublished = yesterday.strftime("%Y-%m-%d")
else:
locale.setlocale(locale.LC_ALL, '') #locale.setlocale(locale.LC_ALL, 'it')
currentYear = datetime.datetime.today().strftime("%Y")
dateStr = datePublished.text
dateStr = datetime.datetime.strptime(dateStr + ' ' + currentYear, '%A %d %B %Y')
datePublished = dateStr.strftime("%Y-%m-%d")
else:
datePublished = datetime.datetime.today().strftime("%Y-%m-%d")
title = " ".join(span.text for span in div.select("a > span"))
news = f" {timePublished} {place.upper()}, {title} (TMW)"
link = div.find('a')['href']
results.append( [datePublished, timePublished, place, title, news, link] )
return results
def all_titles():
global df
allnews = [] # local variable
for place in ['atalanta', 'bologna']:
print('search:', place)
results = get_data_for(place)
print('found:', len(results))
allnews += results
text_download.insert('end', f"search: {place}\nfound: {len(results)}\n")
df = pd.DataFrame(allnews, columns=['date', 'time', 'place', 'title', 'news', 'link'])
df = df.sort_values(by=['date', 'time', 'place', 'title'], ascending=[False, False, True, True])
df = df.drop_duplicates(subset=['date', 'time', 'place', 'title'])
df = df.reset_index(drop=True)
listbox_title.delete('0', 'end')
for index, row in df.iterrows():
listbox_title.insert('end', row['news'])
def content(event=None): # `command=` executes without `event`, but `bind` executes with `event` - so it needs default value
# tuple
selection = listbox_title.curselection()
print('selection:', selection)
if selection:
item = df.iloc[selection[-1]]
#print('item:', item)
url = item['link']
#print('url:', url)
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'
}
# keep page in database `SQLite`
# https://github.com/reclosedev/requests-cache
# https://sqlite.org/index.html
session = requests_cache.CachedSession('titles')
response = session.get(url, headers=headers)
#response = requests.get(url, headers=headers)
soup = BeautifulSoup(response.content, 'html.parser')
content_download = "\n".join(item.get_text() for item in soup.select("div.text.mbottom"))
text_download.delete('1.0', 'end') # remove previous content)
text_download.insert('end', content_download)
# --- main ---
df = None
# window = tk.Tk()
# window.geometry("800x800")
frame_title = tk.Frame(self)
frame_title.pack(fill='both', expand=True, pady=5, padx=5)
listbox_title = tk.Listbox(frame_title, selectbackground="#960000", selectforeground="white", bg="white")
listbox_title.pack(side='left', fill='both', expand=True)
scrollbar_title = tk.Scrollbar(frame_title)
scrollbar_title.pack(side='left', fill='y')
scrollbar_title['command'] = listbox_title.yview
listbox_title.config(yscrollcommand=scrollbar_title.set)
listbox_title.bind('<Double-Button-1>', content) # it executes `content(event)`
# ----
text_download = ScrolledText(self, bg="white")
text_download.pack(fill='both', expand=True, pady=0, padx=5)
# ----
buttons_frame = tk.Frame(self)
buttons_frame.pack(fill='x')
button1 = tk.Button(buttons_frame, text="View Titles", command=all_titles) # don't use `[]` to execute functions
button1.pack(side='left', pady=5, padx=5)
button2 = tk.Button(buttons_frame, text="View Content", command=content) # don't use `[]` to execute functions
button2.pack(side='left', pady=5, padx=(0,5))
我想你已经声明 df
是嵌套在你的 __init__
方法下的函数之一的全局变量......然后在你的主要部分中,它实际上运行在__init__
,您将 df
设置为 None
。您需要更加小心地使变量成为全局变量,并多考虑一下函数作用域;您定义的那些函数是否需要在 __init__
的范围内,或者它们可以有不同的范围?
解决这个问题的简单方法是在 all_titles()
函数中将 global df
更改为 nonlocal df
。
但是,我建议通过将所有 df
更改为 self.df
:
class Page1:
def __init__(self, master, **kw):
...
def all_titles():
...
# changed df to self.df
self.df = pd.DataFrame(allnews, columns=['date', 'time', 'place', 'title', 'news', 'link'])
self.df = self.df.sort_values(by=['date', 'time', 'place', 'title'], ascending=[False, False, True, True])
self.df = self.df.drop_duplicates(subset=['date', 'time', 'place', 'title'])
self.df = self.df.reset_index(drop=True)
listbox_title.delete('0', 'end')
for index, row in self.df.iterrows():
listbox_title.insert('end', row['news'])
def content(event=None):
...
if selection:
item = self.df.iloc[selection[-1]]
...
...
self.df = None
...