使用 Python 和 Tkinter 构建简易职位推荐引擎

发布: (2026年1月31日 GMT+8 11:19)
7 min read
原文: Dev.to

Source: Dev.to

是否曾想过创建一个根据你的技能推荐工作的桌面应用?在本教程中,我们将使用 PythonTkinterttkbootstrap 构建一个 职位推荐引擎

我们将涵盖:

  • 项目搭建
  • 定义职位数据结构
  • 获取并筛选职位
  • 构建简易推荐引擎
  • 使用 Tkinter UI 显示结果
  • 添加分页和搜索

让我们开始吧! 🚀

1. 设置与安装依赖

首先,确保已安装 Python 3。然后安装所需的包:

pip install ttkbootstrap pillow requests

包说明

用途
ttkbootstrap现代 Tkinter UI 样式
Pillow处理和显示图像(公司徽标)
requests从 URL 获取徽标

2. 定义职位数据结构

我们将使用 dataclass 来表示每个职位。这使得存储和管理职位信息变得简单。

from dataclasses import dataclass
from typing import List

@dataclass
class Job:
    title: str
    company: str
    url: str
    description: str
    skills: List[str]
    location: str
    logo_url: str = ""
    score: float = 0.0

✅ 这为每个职位提供了标题、公司、地点、技能、徽标,以及用于推荐的评分。

3. 获取职位

在本教程中,我们将使用一个静态的职位列表。以后你可以将其替换为 API 调用。

from typing import List

def fetch_jobs(query: str) -> List[Job]:
    jobs_data = [
        Job(
            "Python Developer",
            "TechCorp",
            "https://example.com/job1",
            "Develop backend applications using Python.",
            ["Python", "Django"],
            "Remote",
            "https://via.placeholder.com/100x100.png?text=TechCorp",
        ),
        Job(
            "Frontend Engineer",
            "Webify",
            "https://example.com/job2",
            "Build responsive web apps with React.",
            ["JavaScript", "React"],
            "NY, USA",
            "https://via.placeholder.com/100x100.png?text=Webify",
        ),
        Job(
            "Data Scientist",
            "DataWorks",
            "https://example.com/job3",
            "Analyze large datasets and build ML models.",
            ["Python", "ML", "Pandas"],
            "SF, USA",
            "https://via.placeholder.com/100x100.png?text=DataWorks",
        ),
    ]

    # Filter jobs by query keyword
    query_tokens = set(query.lower().split())
    matched_jobs = []
    for job in jobs_data:
        text_to_match = " ".join(
            [
                job.title.lower(),
                job.description.lower(),
                " ".join(job.skills).lower(),
            ]
        )
        if any(token in text_to_match for token in query_tokens):
            matched_jobs.append(job)
    return matched_jobs

💡 提示: 这个简单的搜索会匹配职位标题、描述或技能中的关键词。

4. 构建一个简单的推荐引擎

我们将根据关键字匹配为每个职位分配分数,以对结果进行排序。

def recommend_jobs(query: str, candidates: List[Job], top_n: int = 5):
    query_tokens = set(query.lower().split())
    recommendations = []

    for job in candidates:
        text_to_match = " ".join(
            [
                job.title.lower(),
                job.description.lower(),
                " ".join(job.skills).lower(),
            ]
        )
        score = sum(1 for token in query_tokens if token in text_to_match)
        recommendations.append((job, score))

    recommendations.sort(key=lambda x: x[1], reverse=True)
    return recommendations[:top_n]

✅ 更高的分数意味着更好的匹配。这是一个简单的基于内容的推荐。

5. 加载公司标志

我们将使用 Pillow 在应用中获取并显示图像。

import io
import requests
from PIL import Image, ImageTk

logo_cache = {}

def load_image(url: str, size: tuple = (80, 80)):
    """Download an image, resize it, and cache the PhotoImage."""
    if not url:
        return None
    if url in logo_cache:
        return logo_cache[url]
    try:
        resp = requests.get(url, timeout=10)
        resp.raise_for_status()
        img = Image.open(io.BytesIO(resp.content))
        img = img.resize(size, Image.ANTIALIAS)
        photo = ImageTk.PhotoImage(img)
        logo_cache[url] = photo
        return photo
    except Exception:
        return None

💡 提示: 缓存可以避免多次下载相同的图像。

6. 构建 Tkinter UI

现在让我们使用 ttkbootstrap 构建用户界面。

import tkinter as tk
import ttkbootstrap as tb
from ttkbootstrap.widgets.scrolled import ScrolledText

# ----------------------------------------------------------------------
# Main window
# ----------------------------------------------------------------------
app = tb.Window(
    title="Job Recommendation Engine",
    themename="flatly",
    size=(980, 720),
)

# ----------------------------------------------------------------------
# Top Section – Search Bar
# ----------------------------------------------------------------------
top = tb.Frame(app, padding=15)
top.pack(fill=tk.X)

tb.Label(
    top,
    text="Job Recommendation Engine",
    font=("Segoe UI", 16, "bold"),
).pack(anchor=tk.W)

query_entry = tb.Entry(top, font=("Segoe UI", 12))
query_entry.pack(fill=tk.X, pady=8)

tb.Button(
    top,
    text="Search",
    bootstyle="primary",
    command=lambda: print("Search clicked!"),
).pack(anchor=tk.E)

# ----------------------------------------------------------------------
# Results Area
# ----------------------------------------------------------------------
result_frame = tb.Frame(app, padding=(15, 5))
result_frame.pack(fill=tk.BOTH, expand=True)

result_box = ScrolledText(result_frame, autohide=True)
result_box.pack(fill=tk.BOTH, expand=True)

text = result_box.text
text.configure(state="disabled", wrap="word")
# This is where job results will be displayed.

# ----------------------------------------------------------------------
# Pagination Controls
# ----------------------------------------------------------------------
nav = tb.Frame(app, padding=10)
nav.pack(fill=tk.X)

prev_btn = tb.Button(nav, text="← Prev", bootstyle="secondary")
prev_btn.pack(side=tk.LEFT)

page_label = tb.Label(nav, text="Page 1", font=("Segoe UI", 10))
page_label.pack(side=tk.LEFT, padx=10)

next_btn = tb.Button(nav, text="Next →", bootstyle="secondary")
next_btn.pack(side=tk.LEFT)

# ----------------------------------------------------------------------
# (Optional) Hook up pagination and search logic here
# ----------------------------------------------------------------------

💡 您现在可以将 SearchPrevNext 按钮挂接到前面定义的函数,以在结果页面之间进行导航。

7. 综合示例

下面是一个将所有内容结合在一起的最小示例。可以随意添加更多功能(例如,详细的职位视图、保存收藏等)。

def display_results(recommendations):
    """Populate the ScrolledText widget with job cards."""
    text.configure(state="normal")
    text.delete("1.0", tk.END)

    for job, score in recommendations:
        # Load logo (cached)
        logo = load_image(job.logo_url)

        # Build a simple text block for each job
        block = f"""\
{job.title} ({job.location})
Company: {job.company}
Score: {score}
Skills: {', '.join(job.skills)}
Link: {job.url}
"""
        text.insert(tk.END, block + "\n" + "-" * 40 + "\n\n")
    text.configure(state="disabled")

def on_search():
    query = query_entry.get()
    candidates = fetch_jobs(query)          # In a real app, fetch all jobs first
    recommendations = recommend_jobs(query, candidates, top_n=10)
    display_results(recommendations)

# Bind the search button
search_btn = tb.Button(
    top,
    text="Search",
    bootstyle="primary",
    command=on_search,
)
search_btn.pack(anchor=tk.E, pady=5)

# Start the Tkinter main loop
if __name__ == "__main__":
    app.mainloop()

就这样!现在你拥有一个使用 Python、Tkinter 和 ttkbootstrap 构建的功能完整的桌面职位推荐应用。 🎉

欢迎尝试更复杂的推荐算法、集成真实的职位 API,或进一步美化 UI。祝编码愉快!

将搜索输入与获取、推荐和显示职位关联

使用 threading 在执行搜索时保持 UI 响应。

import threading

def perform_search():
    """Called when the user presses the Search button."""
    query = query_entry.get().strip()
    if not query:
        return
    # Run the heavy work in a background thread
    threading.Thread(target=search_thread, args=(query,), daemon=True).start()

def search_thread(query):
    """Background worker that fetches jobs, ranks them, and prints the results."""
    candidates = fetch_jobs(query)
    recommendations = recommend_jobs(query, candidates)
    for job, score in recommendations:
        print(f"{job.title} @ {job.company} | Score: {score}")

全屏控制

  • 进入全屏模式
  • 退出全屏模式

试一试

  1. 运行脚本

    python job_recommender.py
  2. 在搜索框中输入关键字(例如 PythonReact)。

  3. 双击职位标题以打开其链接(如果你已经实现了此功能)。

  4. 使用 Prev / Next 按钮浏览结果。

完整源代码

完整项目已在 GitHub 上提供:

https://github.com/rogers-cyber/JOBREC

结论

恭喜! 🎉 现在你已经拥有一个可运行的 职位推荐引擎,具备以下功能:

  • Python 数据类
  • 简单的 基于内容的推荐
  • 带有搜索、结果显示和分页功能的 Tkinter UI

后续步骤

  • 用真实的 API 调用替换静态职位列表。
  • 添加模糊匹配,实现更智能的搜索。
  • 为 UI 增强图片、图标和过滤器等功能。

Job Recommendation Engine Screenshot

Back to Blog

相关文章

阅读更多 »