好友
阅读权限 10
听众
最后登录 1970-1-1
本帖最后由 fff123123 于 2023-11-3 22:07 编辑
使用tkiter和requests制作的ui小说爬取,在pycharm里面可以正常运行,点击按钮可以执行绑定事件,
但是打包后exe文件运行,点击搜索确认按钮无反应 即这个方法confirmation_search
文件结构
main.py
bin
tk_ui.py
requestsutils.py
yuan.json
main.py为程序入口,然后启动tk_ui.py界面,在tk_ui里面按钮绑定了requestsutils中的一个网络请求方法,没有使用多线程
请问是为什么,是只有tkiter这样吗,我之前使用pyqt5打包的没有问题
main.py文件
from bin.tk_ui import MyApp
def main ():
app = MyApp()
app.mainloop()
if __name__ == '__main__' :
main()
tk_ui.py文件
from tkinter import *
from tkinter import ttk, messagebox
import tkinter.font as tf
from . import requestutils
class MyApp(Tk):
""" 继承 Tk ,创建自己的桌面应用程序类 """
def __init__ (self ):
""" 构造函数 """
super ().__init__ ()
self .title('Frame' )
# self.iconbitmap('./bin/Tk.ico')
self .geometry('800x500' )
# self.resizable(width=False, height=False) # 阻止 Python GUI 的大小调整
# 底界面
self .frame = Frame(self , bg ='#90c0c0' )
self .frame.pack(expand =True, fill =BOTH)
self .frame2 = Frame(self .frame) # 界面 1
self .frame3 = Frame(self .frame) # 界面 2
self .frame4 = Frame(self .frame) # 界面 3
self .current_page = self .frame2
self .init_ui()
def init_ui (self ):
""" 初始化界面 """
# 书籍存储搜索结果的二维列表
self .result = list ()
# 章节存储搜索结果的二维列表
self .result2 = list ()
# 章节内容存储搜索结果的二维列表
self .result3 = list ()
# 设置字体
ft = tf.Font(family =" 微软雅黑 " , size =10 )
# frame1 是 frame 的第 1 个子控件,从左向右布局 , 按钮所在的控件
frame1 = Frame(self .frame, bg ='#f0f0f0' )
frame1.pack(side =LEFT)
# 3 个 Button 是 frame2 的子控件,自上而下布局
Button(frame1, text =' 书架 ' , width =10 , command =self .first_page).pack(padx =5 , pady =5 ) # 切换为界面 1
Button(frame1, text =' 搜索 ' , width =10 , command =self .second_page).pack(padx =5 , pady =5 ) # 切换为界面 2
Button(frame1, text =' 阅读 ' , width =10 , command =self .third_page).pack(padx =5 , pady =5 ) # 切换为界面 3
# 第一个界面 ui
Label(self .frame2, text =' 界面 1' , bg ='#90c0c0' ).grid(row =1 , column =1 , padx =10 , pady =10 )
# 第二个界面 ui
# 顶部单独的一个 frame
frame2_1 = Frame(self .frame3, bg ='#90c0c0' )
frame2_1.pack(side =TOP, fill ="x" )
self .combo_source_book = ttk.Combobox(frame2_1, values =(" 全本免费小说 " , " 某点中文网 " ), state ='readonly' )
self .combo_source_book.current(1 ) # 选择第一个
self .combo_source_book.grid(row =0 , column =1 , padx =2 , pady =2 , sticky ="nsew" )
self .entry_book_name = Entry(frame2_1, width =30 )
self .entry_book_name.grid(row =0 , column =2 , padx =2 , pady =2 , sticky ="nsew" ) # 书名获得
Button(frame2_1, text =' 确认 ' , width =12 , command =self .confirmation_search). \
grid(row =0 , column =3 , padx =2 , pady =2 , sticky ="nsew" ) # 搜索确认键
# 设置行和列的权重
frame2_1.grid_rowconfigure(0 , weight =1 )
frame2_1.grid_columnconfigure(0 , weight =1 )
frame2_1.grid_columnconfigure(1 , weight =2 )
frame2_1.grid_columnconfigure(2 , weight =1 )
# 搜索结果显示
self .lb_books_name = Listbox(self .frame3)
self .lb_books_name.pack(expand =True, side =TOP, fill =BOTH, padx =5 , pady =5 )
# 绑定双点击函数 获取具体章节 内容
self .lb_books_name.bind("<Double-Button-1>" , lambda event: self .get_book(self .lb_books_name.curselection()))
# 绑定单击函数 提示具体信息
self .lb_books_name.bind("<<ListboxSelect>>" ,
lambda event: self .get_book_introduce(self .lb_books_name.curselection()))
# 底部 书籍具体信息显示
# 创建文本框
self .book_introduce = Text(self .frame3, height =1 )
self .book_introduce.pack(expand =True, side =TOP, fill =BOTH, padx =5 , pady =5 )
# 第三个界面 ui
frame3_1 = Frame(self .frame4, bg ='#90c0c0' )
frame3_1.grid(row =0 , column =0 , padx =2 , pady =2 , sticky ="nsew" )
frame3_2 = Frame(self .frame4, bg ='#90c0c0' )
frame3_2.grid(row =1 , column =0 , padx =2 , pady =2 , sticky ="nsew" )
self .frame4.grid_columnconfigure(0 , weight =1 )
self .frame4.grid_rowconfigure(0 , weight =1 )
self .frame4.grid_rowconfigure(1 , weight =25 )
# 第一个 frame
self .frame3_1_name = StringVar() # 获取书名
Label(frame3_1, textvariable =self .frame3_1_name).grid(row =0 , column =0 , padx =2 , pady =2 , sticky ="nsew" )
Button(frame3_1, text =" 上一章 " ).grid(row =0 , column =1 , padx =2 , pady =2 , sticky ="nsew" )
Button(frame3_1, text =' 下一章 ' ).grid(row =0 , column =2 , padx =2 , pady =2 , sticky ="nsew" )
# 设置行和列的权重
frame3_1.grid_rowconfigure(0 , weight =1 )
frame3_1.grid_columnconfigure(0 , weight =3 )
frame3_1.grid_columnconfigure(1 , weight =1 )
frame3_1.grid_columnconfigure(2 , weight =1 )
# 第二个 frame
self .lb_chapter_name = Listbox(frame3_2)
# 绑定双点击函数 获取具体章节 内容
self .lb_chapter_name.bind("<Double-Button-1>" ,
lambda event: self .get_chapter_content(self .lb_chapter_name.curselection()))
# 多行文本
self .lb_chapter_info = Text(frame3_2, font =ft)
self .lb_chapter_name.grid(row =0 , column =0 , padx =2 , sticky ="nsew" )
self .lb_chapter_info.grid(row =0 , column =1 , padx =2 , sticky ="nsew" )
# 设置行和列的权重
frame3_2.grid_rowconfigure(0 , weight =1 )
frame3_2.grid_columnconfigure(0 , weight =1 )
frame3_2.grid_columnconfigure(1 , weight =5 )
# 默认显示界面 1 ,并记录当前界面
self .current_page.pack(expand =True, side =LEFT, fill =BOTH, padx =5 , pady =5 )
# 书籍搜索确认键
def confirmation_search (self ):
s1 = self .combo_source_book.current() # 书源
print (" 第 " + str (s1) + " 个书源 " )
s2 = self .entry_book_name.get() # 书名
# 检查 清除 listbox 框内的内容
numble = self .lb_books_name.size()
if numble != 0 :
self .lb_books_name.delete(0 , END)
if s2 != "" :
# 根据书源搜索书
self .result = requestutils.requests_util(s2, s1, 0 )
if self .result3 != '' :
self .lb_books_name.insert(END, " 成功 " )
else :
self .lb_books_name.insert(END, " 失败 " )
print (self .result)
# 把搜索结果显示在 listbox 上
for item in self .result:
self .lb_books_name.insert(END, item[0 ] + "----" + item[1 ] + " \n\n " )
else :
# 提示没有输入书名
messagebox.showinfo(' 提示 ' , ' 没有输入书名 ' )
# 具体书籍 url 获取具体章节信息 章节内容
def get_book (self , index):
# 设置阅读界面的书名
s = self .entry_book_name.get() # 书名
self .frame3_1_name.set(s)
# 跳转到阅读界面
self .third_page()
for i in index:
# 获取书的 url
# 获取书籍内容 / 获取章节内容
s = self .result[-1 ]
# 网络请求获取章节信息
s1 = self .combo_source_book.current() # 书源
# 检查 清除 listbox 框内的内容
# 根据书源搜索具体书籍
self .result2 = requestutils.requests_util(s, s1, 1 )
print (self .result2)
# 清除阅读界面的章节数据
self .lb_chapter_name.delete(0 , END)
# 设置阅读界面的章节数据
for item in self .result2:
# 添加章节名
self .lb_chapter_name.insert(END, item[0 ])
break
# 获取章节内容
def get_chapter_content (self , index):
for i in index:
# 获取书的 url
url = self .result2[-1 ]
# 网络请求获取章节信息
s1 = self .combo_source_book.current() # 书源
# 检查 清除 listbox 框内的内容
# 根据书源搜索具体书籍
self .result3 = requestutils.requests_util(url, -1 , 2 )
print (type (self .result3))
self .lb_chapter_info.delete(1.0 , END)
self .lb_chapter_info.insert(END, self .result3)
# 获取书籍介绍
def get_book_introduce (self , index):
print (index)
for i in index:
# 显示书籍具体信息
self .book_introduce.config(state =NORMAL)
# 清除内容
self .book_introduce.delete("1.0" , END)
self .book_introduce.insert("0.0" ,
self .result[0 ] + " " + self .result[1 ] + " \n\n " + self .result[2 ])
self .book_introduce.config(state =DISABLED)
break
# 跳转到第一个 ui
def first_page (self ):
if self .current_page != self .frame2:
self .current_page.pack_forget() # 取消显示当前界面,并不是销毁
self .current_page = self .frame2
self .current_page.pack(expand =True, side =LEFT, fill =BOTH, padx =5 , pady =5 ) # 显示界面 1
# 跳转到第二个 ui
def second_page (self ):
if self .current_page != self .frame3:
self .current_page.pack_forget() # 取消显示当前界面,并不是销毁
self .current_page = self .frame3
self .current_page.pack(expand =True, side =LEFT, fill =BOTH, padx =5 , pady =5 ) # 显示界面 2
# 跳转到第三个 ui
def third_page (self ):
if self .current_page != self .frame4:
self .current_page.pack_forget() # 取消显示当前界面,并不是销毁
self .current_page = self .frame4
self .current_page.pack(expand =True, side =LEFT, fill =BOTH, padx =5 , pady =5 ) # 显示界面 3
requestsutils.py文件
import json
import random
import time
import requests
from bs4 import BeautifulSoup
from lxml import etree
# 随机返回请求头 user_agent
def getheader ():
user_agent_list = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 "
"Safari/537.36 Edg/117.0.2045.47" ,
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36" ,
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1"
]
return random.choice(user_agent_list)
# 读取 json 文件
def getMovieUrl ():
with open ("./bin/yuan.json" , "r" , encoding ='utf-8' ) as file:
t = file.read()
link = json.loads(t)
return link
# 普通网络请求 书名 / 完整书 url 书源选择 (0/1) 搜索书 (0)/ 搜索书的具体信息 (1)
def requests_util (search, book_source_item, coeffcient):
# ip 代{过}{滤}理
proxy = {
"http" : "117.186.232.73:8080"
}
book_source_item = book_source_item + 1
# 读取 json 文件
item = getMovieUrl()
item = item[book_source_item]
# 存储搜索结果的二维列表
result = list ()
# 网络请求准备
name = item["bookSourceName" ]
url = item["bookSourceUrl" ]
search_url = item["search_url" ]
chapter_search_url = item["chapter_search_url" ]
headers = item["headers" ]
rules = item["rules" ]
rules_chapter = item["rules_chapter" ]
headers['User-Agent' ] = getheader()
# 搜索书
if coeffcient == 0 :
# 需要拼接
search_url += search
print (" 书源名 " + name, " 书源网站 " + url, " 搜索网址 " + search_url)
response = requests.get(url =search_url, headers =headers, proxies =proxy, timeout =20 )
# 搜索具体书籍 章节
elif coeffcient == 1 :
print (" 书源名 " + name, " 书源网站 " + url, " 搜索网址 " + search)
response = requests.get(url =search, headers =headers, proxies =proxy, timeout =20 )
# 搜索具体内容
elif coeffcient == 2 :
print (name, " 搜索网址 " + search)
response = requests.get(url =search, headers =headers, proxies =proxy, timeout =20 )
time.sleep(1 )
# response.encoding = response.apparent_encoding # 设置编码,防止由于编码问题导致文字错乱
# print(response.text) # 查看请求到的内容
content = response.content.decode("utf-8" )
soup = BeautifulSoup(content, "html.parser" )
# print(soup.text)
if coeffcient == 0 :
print (" 搜索书籍 " )
# 打印筛选结果
t1 = rules["basics" ]
t2 = rules["book_name" ]
t3 = rules["book_author" ]
t4 = rules["info" ]
t5 = rules["url_part" ]
elements = soup.select(t1)
# print(elements)
for element in elements:
# 基本信息
s = list ()
print (" 具体文本信息:书名 作者 介绍 " )
book_name = element.select_one(t2).text
book_author = element.select_one(t3).text
info = element.select_one(t4).text
s.append(book_name)
s.append(book_author)
s.append(info)
# url 信息
url_part = element.select_one(t5).get("href" )
spilt_after_url = url_part.split("book" )[-1 ]
new_url = chapter_search_url + spilt_after_url
print (" 分割后字符串 " + spilt_after_url)
s.append(new_url)
result.append(s)
# 设置只保持 10 个搜索的结果
if len (result) > 10 :
break
# print(result)
return result
elif coeffcient == 1 :
print (" 搜索章节 " )
# 返回 章节名 章节 url
t1 = rules_chapter["basics" ]
t2 = rules_chapter["part1" ]
elements = soup.select(t1)
# print(elements)
for element in elements:
chapters = element.find_all(t2)
for chapter in chapters:
s = list ()
name = chapter.text
part_url = chapter.get("href" )
# 找有没有前缀 没有 有前缀 删掉 com 之前的内容
if "com" in part_url:
chapter_url = url + part_url.split("com" , 1 )[-1 ]
else :
chapter_url = url + part_url
# print(name)
# print(url)
s.append(name)
s.append(chapter_url)
result.append(s)
return result
elif coeffcient == 2 :
print (" 搜索章节内容 " )
# 创建一个 XPath 解析器
parser = etree.HTMLParser()
tree = etree.HTML(content, parser =parser)
# search 为 url
# 某点的规则 find(www.qidian.com)
if "www.qidian.com" in search:
# 使用 XPath 选择器筛选页面数据
# 创建一个 XPath 解析器
parser = etree.HTMLParser()
tree = etree.HTML(content, parser =parser)
# 使用 XPath 选择器筛选页面数据
script_element = tree.xpath('//script[@id="vite-plugin-ssr_pageContext"]/text()' )
json_data = script_element[0 ].strip()
data = json.loads(json_data)
value = data['pageContext' ]['pageProps' ]['pageData' ]['chapterInfo' ]['content' ].replace("<p>" , " \n " )
print (value)
return value
# 全本的规则
elif "www.qbmfxs.com" in search:
# 使用 XPath 选择器筛选页面数据 str
value = tree.xpath("/html/body/div[4]/div/div[1]/div[3]//p/text()" )
print (value)
return value
这是main.spec文件
# -*- mode: python ; coding: utf-8 -*-
a = Analysis(
['main.py'],
pathex=['D:\\other_folders\\code_workplace\\read_book'],
binaries=[],
datas=[('D:\\other_folders\\code_workplace\\read_book\\bin\\requestutils.py','bin'),('D:\\other_folders\\code_workplace\\read_book\\bin\\tk_ui.py','bin'),('D:\\other_folders\\code_workplace\\read_book\\bin\\yuan.json','bin\\yuan.json')],
hiddenimports=['tkinter','requests'],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name='main',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
coll = COLLECT(
exe,
a.binaries,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='main',
)