tkinter+requests 练习

发布于 2023-01-09  634 次阅读


想用tkinter写个桌面程序

tkinter参考资料主要有官方文档,还有一本台湾人写的书tkinter菜鸟编程.此外还有一堆抄来抄去的书.我觉得只要多练就行了,看看官方写的代码

主要解决问题:

  1. tkinter的控件布局.整洁好看,不过这并不是最重要的
  2. 调用程序,触发一个选项后要有调用的函数
  3. 多线程.不然界面会出现未响应

一些小问题 控件的大小,字体颜色与大小 利用python打开资源管理器Python调Windows的资源管理器打开指定目录 - famiover - OSCHINA - 中文开源技术交流社区

1.布局问题

tkinter布局一般,不像PyQt拖拽式那样简单.

一般用到的控件Label,Entry,Button,messagebox,menu,text,toplevel(打开新窗口)

像按钮啥的暂时用不上

同时还有添加图像时有时图片太大,可以利用

from PIL import Image
photo = Image.open("小猫.jpg")  #括号里为需要显示在图形化界面里的图片

photo = photo.resize((400,400))  #规定图片大小

python 使用图形化界面tkinter显示图片 规定大小!_地中海的博客-CSDN博客_tkinter设置图片大小

2.调用程序

tkinter两种方法

command调用与bind绑定

不过我发现

控件command=callback传参不方便,只能利用

  1. lambda函数
  2. IntVar() StringVar()等这种变量设置

而bind会传一个参数event

可以得到相关参数(例如鼠标点击位置啥的)

建议一个空间不要同时用这两个调用

3.多线程

主要是要将桌面主程序与爬虫等程序分开

上次讲了python多线程利用threading模块的两种写法

import thread
import time
class Mythread(threading.Thread):
    def __init__(self,name=None):
        super().__init__(name=name)
    def run(self):
        for i in range(5):
            n = threading.currentThread()
            time.sleep(1)
            print("现在是{0}的第{1}次".format(n.name,i))
        print("子线程执行完了")
if __name__ == "__main__":
    mythread = Mythread("张三")
    mythread.setDaemon(True) #守护线程,当主线程执行完后,子线程强制关闭
    mythread.start()
    mythread.join() #必须当这个子线程执行完,主线程才开始执行
    for i in range(5):
        time.sleep(1)
        n = threading.currentThread()
        print("现在是{0}的第{1}次".format(n.name, i))
image-20210731230339270

可以看出join之后子线程先执行完,若去掉join

image-20210731230516420

注意主线程与子线程同时执行并且当主线程执行完,子线程关闭

遇到的问题

特别注意的是,我添加图片时,如果在函数里添加,那么图片就没了(程序没有报错,但图片显示空白)

我把那段代码改在主函数里就行了,猜测可能是因为GC机制回收了

完整代码

import time
from tkinter.ttk import *
import tkinter as tk
from tkinter import *
import requests
from tkinter import messagebox
from lxml import etree
import os
import os.path
import threading
from PIL import Image, ImageTk


class Application(tk.Frame):

    def __init__(self, master=None):
        super().__init__(master)
        self.button = tk.Button(self, text="确定", )  # command=self.get_info)
        self.entry = tk.Entry(self, font="times 15")
        self.endbtn = Button(self, text="结束", command=stop)
        self.label1 = tk.Label(self, text="请输入爬取的页数范围:\n例子:1,2", font="times 15", relief="raised")
        self.create_widget()
        self.pack()

    def create_widget(self):
        self.entry.insert(0, "1,2")
        self.label1.pack(side=LEFT, fill=BOTH)
        self.entry.pack(fill=X)
        self.button.pack(fill=BOTH)
        self.button.bind("<Button-1>", self.get_info)
        self.entry.bind("<Return>", self.get_info)

        self.endbtn.pack(fill=X)

    def get_info(self, event):
        entry_info = self.entry.get()
        try:
            fir_page = int(entry_info.split(",")[0])
            sec_page = int(entry_info.split(",")[1])
        except IndexError as e:
            messagebox.showwarning("提示", "输入格式错误\n%s" % e)
            return
        except ValueError as e:
            messagebox.showwarning("提示", "输入格式错误\n%s" % e)
            return
        except Exception as e:
            messagebox.showwarning("提示", "输入格式错误\n%s" % e)
            return
        spider = Spider(fir_page, sec_page)
        spider.setDaemon(True)
        var.set("正在爬取...")
        spider.start()
        # spider.join()   这里如果join了运行爬虫桌面程序多半会卡


class Spider(threading.Thread):
    # global info
    # location = os.getcwd() + '/fake_useragent.json'
    # ua = UserAgent(path=location)
    __headers = {'user_agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
                               "Chrome/92.0.4515.107 Safari/537.36 Edg/92.0.902.55", "Connection": "close"}
    # __base_url = "https://konachan.net/"
    # __url = "https://konachan.net/post?page={}"
    __url = "https://gelbooru.com/index.php?page=post&s=list&tags=all&pid={}"
    flag = False

    def __init__(self, fir_page, sec_page):
        Spider.flag = False
        super().__init__(None)
        # self.fir_page = fir_page
        # self.sec_page = sec_page
        self.fir_page = (fir_page - 1) * 42
        self.sec_page = sec_page * 42
        self.tag = True

    def run(self):
        pb.start()
        self.parse()
        pb.stop()
        var.set("爬取完毕")
        messagebox.showinfo("提示消息", "爬取结束了")

    def parse(self):
        text.delete("1.0", END)
        requests.adapters.DEFAULT_RETRIES = 5
        text.insert(END, "下载至{}\n".format(os.getcwd() + r'\images或\videos'))
        # info += "下载至{}\n".format(os.getcwd() + r'\images或\videos')
        # msg_var.set(info)
        for n in range(self.fir_page, self.sec_page + 1):
            if Spider.flag:
                break
            time.sleep(1.5)
            try:
                res = requests.get(self.__url.format(n), headers=self.__headers)
            except requests.exceptions.ConnectionError as e:
                messagebox.showinfo("出现问题", "爬取频繁,请连接代理\n%s" % e)
                pb.stop()
                var.set("爬取完毕")
                return
            selector = etree.HTML(res.text)
            # images_urls = selector.xpath("//ul[@id='post-list-posts']/li//a[@class='thumb']/@href")
            images_urls = selector.xpath("//article//a/@href")
            for image_url in images_urls:
                if Spider.flag:
                    break
                self.parse_next(image_url)

    def parse_next(self, image_url):
        # image_id = image_url.split("/")[3]
        # next_url = self.__base_url + image_url
        image_id = image_url.split("=")[-1]
        next_url = image_url
        time.sleep(1.5)
        res = requests.get(next_url, headers=self.__headers)
        selector = etree.HTML(res.text)
        # img = selector.xpath("//img[@id='image']/@src")[0]
        try:
            img = selector.xpath("//img[@id='image']/@src")[0]
        except IndexError:
            img = selector.xpath("//video/source[1]/@src")[0]  # 这是图片
            self.tag = False
        self.download(img, image_id)

    def download(self, img, image_id):
        if Spider.flag:
            return
        time.sleep(0.5)
        suffix = img.split(".")[-1]
        res = requests.get(img, headers=self.__headers)
        text.insert(END, "正在下载:%s\n" % img)
        # info += "正在下载:%s\n" % img
        # msg_var.set(info)
        if self.tag:
            if not os.path.exists("./images"):
                os.mkdir("./images")
            with open('images/{0}.{1}'.format(image_id, suffix), 'wb+') as f:
                f.write(res.content)
        else:
            if not os.path.exists("./videos"):
                os.mkdir("./videos")
                with open('videos/{0}.{1}'.format(image_id, suffix), 'wb+') as f:
                    f.write(res.content)
        text.insert(END, "下载成功:%s\n" % img)
        # info += "下载成功:%s\n" % img
        # msg_var.set(info)


def stop():
    Spider.flag = True


def open_current():
    base_path = os.getcwd()
    if not os.path.exists("./images"):
        is_cre = messagebox.askyesno("提示", "不存在图片文件夹,是否创建?")
        if is_cre:
            os.mkdir("./images")
            path = base_path + r"\images"
        else:
            path = base_path
    else:
        path = base_path + r"\images"
    os.system("explorer.exe %s" % path)


def show_myinfo():
    my_info = """
        website:http://sekyoro.top/
        github:https://github.com/drowning-in-codes
        一起学习
    """
    toplevel = Toplevel()
    toplevel.title("关于我")
    toplevel.iconbitmap("th.ico")
    toplevel.geometry("400x350")
    info_label = Label(toplevel, font="times 15", text=my_info, image=info_tk_image, compound="top", justify="left")
    info_label.pack()


def set_window():
    root.title("桌面爬虫")
    root.geometry("400x450")
    root.iconbitmap("th.ico")
    label.pack()
    menubar = Menu(root)
    filemenu = Menu(menubar, tearoff=0)
    menubar.add_cascade(label="File", menu=filemenu)
    filemenu.add_command(label="Open", command=open_current)
    filemenu.add_separator()
    filemenu.add_command(label="Exit", command=root.destroy)
    helpmenu = Menu(menubar, tearoff=0)
    helpmenu.add_command(label="About me", command=show_myinfo)
    menubar.add_cascade(label="Help", menu=helpmenu)
    root.config(menu=menubar)
    text_label = Label(root, textvariable=var).pack()


if __name__ == "__main__":
    root = tk.Tk()
    image = Image.open("OIP-C.gif")
    image = image.resize((200, 200))
    # info = "爬取信息\n"
    image_tk = ImageTk.PhotoImage(image)
    label = tk.Label(root, font="20", fg="blue", text="欢迎使用", image=image_tk, compound="top")
    var = StringVar()
    set_window()
    pb = Progressbar(root, mode="indeterminate", length=200, name="进度")
    pb["value"] = 0
    pb["maximum"] = 100
    pb.pack()
    app = Application(root)
    info_image = Image.open("info.png")
    info_image = info_image.resize((200, 200))
    info_tk_image = ImageTk.PhotoImage(info_image)
    # msg_var = StringVar()
    # msg = Message(root, textvariable=msg_var, relief="raised")
    # msg.pack(padx=10, pady=10)
    # msg_var.set(info)
    #  待滚轮的text
    yscrollbar = Scrollbar(root)
    text = Text(root)
    yscrollbar.pack(side=RIGHT, fill=Y)
    text.pack()
    yscrollbar.config(command=text.yview)
    text.config(yscrollcommand=yscrollbar.set)
    app.mainloop()
image-20210731231013251

最后pyinstaller -w -F name.py打包就行了

大概长这样,看来要多多尝试

届ける言葉を今は育ててる
最后更新于 2023-01-24