外部文件的 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
        ...