news 2026/10/19 7:47:24

Python爬虫+pandas+Flask:拆解一个数据采集展示项目

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python爬虫+pandas+Flask:拆解一个数据采集展示项目

简介:pythonProject.zip 是一份包含约2000个文件的Python工程压缩包,适合有一定Python基础、希望研读或复用完整项目源码的开发者。压缩包整体约57.14MB,文件类型以1745个py脚本为核心,另有103个js、68个txt、45个c、29个h以及少量md、html、json、xml、cpp、sh文件,覆盖源码逻辑、依赖说明、文本数据、C扩展、前端页面与Shell脚本等常见工程组成部分,结构较为典型。内容预览中可见fortranobject.c、cpu_avx512_*.c等文件,推测项目涉及科学计算或数值优化方向,包含C语言扩展及面向特定CPU指令集的针对性优化,适合对底层实现与性能调优感兴趣的读者深入挖掘。目前已有134人学习浏览,该代码包可作为研究Python项目组织方式、C扩展接口设计或重构思路的参考样例,解压后即可查看完整目录结构与源码细节,便于按模块拆解学习。

1. 拆开 pythonProject.zip 之前:先搞清楚这是个什么工程

拿到一个 pythonProject.zip,解压后看到一堆 .py 文件和几个子文件夹,第一反应通常是「先从哪个文件跑起来」。这个压缩包其实是一套完整的数据采集与展示小系统:爬虫负责抓取公开列表页,pandas 负责清洗去重和聚合统计,Flask 把结果以 JSON 接口和网页表格的形式吐出来。整套东西没有中间件、没有消息队列,一个 SQLite 加三个核心模块就把链路走通了。适合刚学完 Python 基础、想看看真实工程怎么组织的人,也适合需要快速搭「采集 → 分析 → 展示」最小闭环的开发者当脚手架用。下面按拆包顺序讲:架构、核心代码、启动步骤、避坑,最后给出二次改造的具体方案。

2. 工程骨架与技术选型:轻量组合为什么够用

2.1 技术栈:requests + pandas + Flask,全链路只用三个主力库

这个项目最值得先看的一点,是它没有引入重型框架。采集层用的是 requests 加 BeautifulSoup4,而不是 Scrapy;分析层是 pandas;展示层是 Flask。常见做法是「爬虫直接上 Scrapy,站点大了再上 FastAPI」,但这里场景很简单:目标是一个公开列表页,最多翻几页,数据量在千条级别。这个量级下 Scrapy 的调度器、中间件、管道全是负担,反而掩盖了「一个请求怎么进来、怎么解析、怎么落库」这条主线。

我一般会给这种小工程定三个选型标准:一是每个模块只用一层依赖,requests 管 HTTP,BeautifulSoup 管解析,pandas 管表格,互不越界;二是数据落盘用 SQLite 和 CSV,零部署成本;三是接口层用 Flask 而不是 Django,因为这里不需要 ORM、Admin 后台和模板全家桶,一个 app 对象加三个路由就够了。这套组合的代价是性能和并发都有限,但作为学习项目和业务原型完全合格。

2.2 目录结构:不要把代码全堆在根目录

解压后你会看到下面的结构,这也是这个工程最容易被忽略但最值得抄的部分——它把「采集、分析、展示、工具」四条线从代码上就分开了:

pythonProject/ ├── requirements.txt ├── config.py ├── main.py ├── scraper/ │ ├── __init__.py │ └── collector.py ├── analysis/ │ ├── __init__.py │ └── processor.py ├── api/ │ ├── __init__.py │ └── server.py ├── utils/ │ ├── __init__.py │ ├── logger.py │ └── db.py ├── data/ │ ├── raw/ │ └── output/ └── tests/ └── test_pipeline.py

根目录只留三个东西:入口 main.py、全局配置 config.py、依赖清单 requirements.txt。真正的业务逻辑按职责放进了 scraper、analysis、api 三个包,utils 放日志和数据库这类横切工具,data 只存数据。这里有一个值得记住的约定:修改一个功能时,你只动对应的包,不用在入口文件里翻找逻辑。比如要改采集规则,进 scraper/collector.py;要改统计口径,进 analysis/processor.py。

__init__.py在这个工程里只是把目录标记成包,内容可以为空。真正起作用的是各模块内部的类和方法,这一点新人常误解,以为__init__.py必须写导入语句——其实 Python 3.3 之后没有它也能用命名空间包,只是显式加上能让 IDE 识别得更清楚。

2.3 配置先行:config.py 和 requirements.txt 的分工

config.py 把一切「跑起来前要改的东西」集中到了一起,这是这个小工程最实用的设计。URL、请求头、超时、休眠范围、数据库路径,全部以字典形式放在一处,代码里没有任何写死的链接。这样做的好处是:换数据源时你不需要碰业务代码,改配置即可。

# -*- coding: utf-8 -*- """全局配置:改数据源、调采集参数都在这里,业务代码不碰。""" SCRAPE_CONFIG = { "base_url": "https://example.com/list", # 目标列表页地址 "headers": { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", }, "timeout": 10, # 单次请求超时秒数 "max_pages": 5, # 最多抓几页 "retry_interval": 2, # 请求失败后重试等待秒数 "sleep_range": (1, 3), # 两次请求之间的随机休眠区间 } DB_CONFIG = { "path": "data/app.db", # SQLite 文件路径 }

参数说明里最容易忽略的是sleep_range:它是一个元组,采集代码会用random.uniform(1, 3)在区间内取随机值。这个随机不是玄学,是避免固定间隔请求被目标站点的限流策略识别。timeout配合重试逻辑使用,请求在超时后不会立刻抛异常,而是进入最多三次的重试循环,重试间隔由retry_interval控制。

requirements.txt 则负责锁定依赖的底线版本,写法上我一般用大于等于而不是精确锁定,方便在不同环境下安装:

requests>=2.28 beautifulsoup4>=4.11 lxml>=4.9 pandas>=1.5 flask>=2.2

注意这里显式声明了 lxml。虽然 BeautifulSoup 默认也能用 Python 标准库解析,但速度慢且容错差,指定 lxml 作为解析器是工程里的标准做法。requirements.txt 和 config.py 的分工可以概括成一句话:前者管「装什么」,后者管「怎么配」,两者都改完才能跑通。

3. 核心模块拆解:一条数据从网页到图表的完整链路

3.1 采集层 collector.py:会话复用、重试、解析三段式

采集模块是这个项目里代码量最大的一层,核心思路是「请求、解析、翻页」分别封装成独立方法。先看完整实现:

# -*- coding: utf-8 -*- """采集层:抓取公开列表页,解析出结构化记录。""" import time import random from typing import List, Dict import requests from bs4 import BeautifulSoup from config import SCRAPE_CONFIG from utils.logger import get_logger logger = get_logger("collector") class Collector: """列表页采集器:负责抓取、解析、翻页。""" def __init__(self, base_url: str, headers: dict = None): self.base_url = base_url self.session = requests.Session() self.session.headers.update(headers or SCRAPE_CONFIG["headers"]) def fetch_page(self, page: int) -> str: """抓取单页 HTML,带三次重试和状态码校验。""" for attempt in range(3): try: resp = self.session.get( self.base_url, params={"page": page}, timeout=SCRAPE_CONFIG["timeout"], ) resp.raise_for_status() return resp.text except requests.RequestException as e: logger.warning("第 %s 页第 %s 次请求失败: %s", page, attempt + 1, e) time.sleep(SCRAPE_CONFIG["retry_interval"]) raise RuntimeError(f"页面抓取失败: page={page}") def parse_page(self, html: str) -> List[Dict]: """从 HTML 里抽取标题、链接、日期字段。""" soup = BeautifulSoup(html, "lxml") items = [] for node in soup.select("ul.list li"): link = node.find("a") if not link: continue date_node = node.find("span", class_="date") items.append({ "title": link.get_text(strip=True), "url": link.get("href"), "date": date_node.get_text(strip=True) if date_node else "", }) return items def run(self, max_pages: int = 5) -> List[Dict]: """串行抓取多页,页间随机休眠。""" results: List[Dict] = [] for page in range(1, max_pages + 1): html = self.fetch_page(page) results.extend(self.parse_page(html)) time.sleep(random.uniform(*SCRAPE_CONFIG["sleep_range"])) return results

逻辑上分三段看。第一段是构造函数里用requests.Session()创建会话并统一设置请求头,比每次请求都重新传 headers 更高效,因为 Session 会复用 TCP 连接,对列表页这种连续翻页场景能明显减少握手开销。第二段是fetch_page,关键在于raise_for_status()——它会把 4xx、5xx 响应变成异常,避免你拿着一张 403 页面当成功数据去解析,这是血泪经验,不校验状态码的话,很多翻车都发生在下游清洗环节而不是采集环节。第三段是parse_page,用 CSS 选择器ul.list li定位每条记录的容器节点,再用 find 提取字段。

参数上要注意两点。params={"page": page}用的是 GET 查询参数,如果你的目标站是路径式分页(比如/list/p2),要把这行改成f"{self.base_url}/p{page}"。max_pages默认是 5,这只是个安全默认值,真实项目里应该根据目标列表的总页数来设。run方法用串行方式翻页,对这个量级没问题;如果以后目标站有几千页,再改成线程池或换成 Scrapy 不迟。

3.2 分析层 processor.py:清洗与聚合的逻辑都在这

采集层拿到的是一堆字典,很多字段可能是空的,日期格式也可能五花八门。分析模块负责把这堆半成品变成干净的表格,核心是「先清洗、后聚合」两个步骤:

# -*- coding: utf-8 -*- """分析层:pandas 清洗去重,按天聚合统计。""" import pandas as pd from utils.logger import get_logger logger = get_logger("processor") class DataProcessor: """把采集结果转成 DataFrame,执行标准分析链路。""" def __init__(self, items: list): self.df = pd.DataFrame(items) def clean(self) -> "DataProcessor": """去空、去重、日期转换,返回自身支持链式调用。""" self.df.dropna(subset=["title", "date"], inplace=True) self.df.drop_duplicates(subset=["url"], inplace=True) self.df["date"] = pd.to_datetime(self.df["date"], errors="coerce") self.df = self.df[self.df["date"].notna()] return self def aggregate_by_day(self) -> pd.DataFrame: """按天统计条目数,返回含 day 和 count 两列的 DataFrame。""" daily = self.df.copy() daily["day"] = daily["date"].dt.date return daily.groupby("day").size().reset_index(name="count") def export_csv(self, path: str) -> None: """导出清洗后的明细数据,utf-8-sig 避免 Excel 乱码。""" self.df.to_csv(path, index=False, encoding="utf-8-sig") logger.info("已导出 %s 条记录到 %s", len(self.df), path)

clean方法里三个操作各解决一类问题。dropna(subset=["title", "date"])删掉关键字段为空的记录,比如页面里那些没有日期的置顶项;drop_duplicates(subset=["url"])按链接去重,防止翻页过程中重复条目被采集两次;pd.to_datetime(errors="coerce")把不标准的时间字符串转成统一的 datetime 类型,解析不了的会变成 NaT,紧接着用notna()过滤掉。

这里最容易被忽视的设计是clean返回self。这样调用方可以写成DataProcessor(items).clean().aggregate_by_day()这种链式结构,少写中间变量,读起来也更像一条流水线。aggregate_by_day里先copy()再新增 day 列,是为了避免修改原始明细数据——这是一个好习惯,聚合永远不动原始表,后续要重新定义统计口径时不用重新清洗。

3.3 展示层 server.py:Flask 接口与简易看板

展示层在这个工程里承担两个职责:对外提供 JSON 接口给其他系统调用,对内提供一个最简单的 HTML 表格页看结果。代码很短,但把「接口返回结构化数据、页面负责渲染」这层分工表达得很清楚:

# -*- coding: utf-8 -*- """展示层:Flask 提供 JSON 接口和简单页面。""" from flask import Flask, jsonify, render_template_string from analysis.processor import DataProcessor from utils.db import load_records app = Flask(__name__) @app.route("/api/daily") def daily(): """返回按天聚合的统计结果,供前端或其他系统消费。""" records = load_records() processor = DataProcessor(records).clean() summary = processor.aggregate_by_day() return jsonify(summary.to_dict(orient="records")) @app.route("/") def index(): """直接渲染一个简易看板页面。""" import pandas as pd try: summary = pd.read_csv("data/output/daily_summary.csv") rows = summary.to_dict(orient="records") except FileNotFoundError: rows = [] return render_template_string(INDEX_HTML, data=rows) INDEX_HTML = """ <!DOCTYPE html> <html> <head><meta charset="utf-8"><title>采集数据看板</title></head> <body> <h1>每日采集量</h1> <table border="1"> <tr><th>日期</th><th>数量</th></tr> {% for row in data %} <tr><td>{{ row.day }}</td><td>{{ row.count }}</td></tr> {% endfor %} </table> </body> </html> """

/api/daily接口的链路是:从 SQLite 读记录 → 走一遍清洗 → 聚合 → 转成 JSON 列表。to_dict(orient="records")是关键参数,它会把 DataFrame 转成[{"day": "2025-01-01", "count": 12}]这种结构,直接能被jsonify序列化;如果漏掉这个参数,pandas 默认转成{列名: 值列表}的形态,前端拿到的就是另一种结构。/页面则直接读上次分析落盘的 CSV,用 Jinja2 模板循环渲染成表格。

这种「接口实时算、页面读文件」的设计看着有点不一致,其实是故意的:接口面向程序调用,要求数据新鲜,所以每次实时计算;页面面向人看,数据量小、不常变,读 CSV 已经足够,省去每次打开页面都重算一遍的开销。真要统一,把页面也改成查接口即可,二三十行代码的事,后面改造章节会提到。

4. 跑通全流程:从建虚拟环境到看到第一条数据

4.1 准备环境:venv 与依赖安装

跑任何 Python 项目,第一步都是隔离环境,别直接用全局 Python 装依赖。这个工程依赖里有 pandas 这种带二进制包的库,全局环境下极易和已有项目的版本冲突。虚拟环境用标准库 venv 就能建,不需要装额外工具:

cd pythonProject python -m venv venv

在 Windows 上激活命令是venv\Scripts\activate,macOS 和 Linux 上是source venv/bin/activate。激活后命令行前缀会变成(venv),这代表当前 shell 用的已经是隔离环境了。接着安装依赖:

pip install -r requirements.txt

这里有个常见坑:如果安装过程中 lxml 或 pandas 编译报错,说明 pip 在尝试从源码构建。常见做法是先用国内镜像源装,或者升级 pip 到最新版本再试,因为新版 pip 对预编译 wheel 的选择策略更好。装完后用pip list检查一下五个核心库都在,再走下一步。

4.2 改配置:数据源、请求参数、输出路径

跑之前先改 config.py。这个工程默认连的是 example.com,直接跑当然会失败,所以第一步是把base_url换成你想采集的真实公开列表页。有三个参数需要根据目标站点情况调整:

参数默认值怎么调
base_urlexample.com/list改成目标的列表页地址
headers通用 UA目标站有反爬时补 Referer、Cookie
max_pages5目标页数少就调小,避免抓空

headers里通常只需要 User-Agent 就能通过大多数站点的基础校验。如果目标网站要求登录才能看列表,那这个项目默认的匿名抓取就不够用了,需要往 headers 里加 Cookie,或者在 Collector 初始化时传入带 Cookie 的 headers 字典。max_pages不要盲目调大,先跑一页看看返回结构,确认parse_page里的ul.list li选择器能匹配到节点,再把页数调上去——这个习惯能帮你省掉大半排查时间。

4.3 启动与验证:三条命令和一个自检清单

配置改完后,启动顺序是先跑数据管道,再起展示服务:

python main.py python api/server.py

第一条命令执行的是「采集 → 清洗 → 聚合 → 导出」全流程,正常输出应该类似:

INFO [main] 开始采集 https://example.com/list INFO [collector] 完成第 1 页,解析到 20 条记录 INFO [processor] 已导出 96 条记录到 data/output/items.csv INFO [main] 分析完成,输出到 data/output/

看到分析完成后,检查data/output/目录下是否生成了items.csv和daily_summary.csv两个文件。日志是判断流程是否正常的第一手依据,如果某一步日志没出现,就从那一步往前查。第二条命令启动 Flask,默认监听 5000 端口,浏览器打开http://127.0.0.1:5000/应该能看到表格页面,访问http://127.0.0.1:5000/api/daily应该返回 JSON。

自检清单按顺序过一遍:虚拟环境激活了吗、config.py 的 base_url 换了吗、日志里有没有 WARNING、CSV 生成了吗、接口返回的是不是合法 JSON。这五条都通过,项目就跑通了。如果卡在某一步,下一章就是为这些情况准备的。

5. 避坑指南:pythonProject 最常见的五个翻车点

5.1 现象:抓下来的 HTML 全是乱码或空字符串

原因基本只有一个:requests 自动猜测的编码不对,或者页面内容是通过 JavaScript 动态渲染的,requests 只拿到了空壳 HTML。判断方法是把resp.text前 500 字打印出来看,如果里面是乱码,就是编码问题;如果内容是标准的 HTML 标签但找不到你要的数据,就是动态渲染问题。

解决:编码问题用resp.encoding = resp.apparent_encoding强制按页面 meta 声明解码,或者直接指定目标站点常用的utf-8、gbk。动态渲染问题则要在parse_page之前先确认这站是不是前后端分离,如果是,直接找它的 JSON 数据接口往往比解析 HTML 更省事。

5.2 现象:pandas 报No columns to parse

这条错误信息几乎都出在pd.DataFrame(items)这一步,但根子在采集层。items 是空列表,或者 items 里的字典字段名不一致,pandas 建表时找不到预期列。

解决:别在分析层调,回到采集层打印前三条parse_page的返回结果。如果返回空列表,说明 CSS 选择器ul.list li没匹配到节点,用浏览器 F12 检查目标页面实际的 DOM 结构,按真实结构调整选择器。如果字段名对不上,比如页面里没有class="date"的节点,字段会变成空字符串而不是缺失,这时要在 parse 方法里做兜底,而不是指望分析层处理。

5.3 现象:CSV 用 Excel 打开中文乱码

这个问题的原因非常明确:pandas 默认to_csv用的是 utf-8 编码,而 Windows 版 Excel 打开 CSV 时默认按 GBK 解码,两边对不上就乱码了。

解决:我的习惯是导出 CSV 一律用encoding="utf-8-sig",这个编码会在文件开头写入 BOM 标记,Excel 能据此正确识别为 UTF-8。注意utf-8-sig和utf-8是有区别的,只改这里就行,不用动其他代码。如果你要交付给下游程序读取的话,utf-8-sig也兼容,不影响 pandas 和 Python 的open正常读取。

5.4 现象:Flask 接口 404,或者启动时报端口被占用

404 一般是路由问题,接口路径访问错了,或者server.py根本没起在新的进程里——改完代码后旧进程还在,访问的是旧版本。端口占用则是另一个进程已经占用了 5000,常见于上次启动没关干净。

解决:先netstat -ano | findstr 5000查出占用进程的 PID,确认是不是自己遗留的 Flask 进程,是就杀掉。404 的话,访问http://127.0.0.1:5000/api/daily前先确认路由装饰器写的是@app.route("/api/daily"),斜杠和大小写差一个字符都不行。开发阶段可以把app.run(debug=True)打开,这样改代码自动重载,也能在页面里直接看到报错堆栈。

5.5 现象:SQLite 报database is locked

这个错误本质是多个进程同时写同一个 SQLite 文件,SQLite 默认只允许一个写者。在这个工程里,典型场景是采集主流程写库的同时,Flask 接口也在写库,或者你开了多个终端反复跑main.py。

解决:先检查有没有重复进程,把多余的停掉。代码层面,sqlite3.connect()加上timeout=10参数,让写入在锁等待 10 秒再放弃,能解决大部分偶发冲突。如果冲突频繁,说明业务已经不适合单文件 SQLite 了,应该换成独立的 MySQL 或用队列串行化写入——这是判断「要不要换数据库」的一个清晰信号。

6. 二次开发实战:把 pythonProject 改造成自己的脚手架

这个工程最大的价值是提供了一个结构清晰的基线,往里面加东西比从零写快得多。最常见的三个改造方向是换数据源、加持久化、加定时任务。

换数据源是最优先的改造。除了改base_url,核心工作集中在parse_page的选择器上。不同网站的结构差异很大,但有一步可以复用:先把目标页面存成data/raw/下的本地 HTML 文件,写解析逻辑时直接读本地文件,调好选择器再换成线上抓取。这样调试不被网络和反爬干扰,改一次跑一遍,比每次都抓线上快一个量级。改完之后记得data/raw/目录本身不提交版本库,在 gitignore 里把它和data/output/一起忽略掉。

加持久化的话,现有utils/db.py已经提供了save_records和load_records两个函数,底层是 SQLite。换成 MySQL 或 PostgreSQL 只需要改这两个函数内部:把sqlite3.connect换成 SQLAlchemy 引擎,to_sql和read_sql的调用方式不变。pandas 对两类数据库的接口是统一的,这一步的改动量比想象中小:

# utils/db.py 改造示例:从 SQLite 换到 MySQL from sqlalchemy import create_engine engine = create_engine("mysql+pymysql://user:pass@localhost/dbname") def save_records(items): import pandas as pd pd.DataFrame(items).to_sql("items", engine, if_exists="append", index=False) def load_records(): import pandas as pd return pd.read_sql("select * from items", engine).to_dict(orient="records")

要注意的是,换成 MySQL 后原来的DB_CONFIG里的path要改成连接串,database is locked这类 SQLite 特有的问题也就不存在了。另一个高频改造是定时跑采集,用标准库的schedule包就能实现,在main.py外面包一层循环:每天固定时间执行main(),跑完再自动调一次server.py里的接口刷新看板。如果以后要追赶更多站点,把 Collector 的run方法改成线程池并发抓多页,再在__init__里按站点传入不同 header,就是一个多源采集器了。

我从一个教训里养成了习惯:拿到任何陌生 zip,先跑一遍完整流程再读代码,跑通时的日志输出就是理解代码的索引。这个工程也一样,main.py的日志打印了每一步的入口和出口,顺着日志找对应模块,比从入口文件逐行读代码快得多。从那以后我每次拆新项目都强制自己先python main.py再打开编辑器,这个习惯能让陌生代码的熟悉速度提升一大截。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/19 7:46:05

GATr:让Transformer从‘背坐标’进化到‘算几何’

前阵子看了个很有意思的工作&#xff1a;GATr&#xff08;Geometric Algebra Transformer&#xff09;&#xff0c;全称是几何代数Transformer。我当时的反应和大多数人一样——Transformer不是处理文本和图像的吗&#xff0c;怎么跑去学"几何直觉"了&#xff1f;但深…

作者头像 李华
网站建设 2026/10/19 7:45:09

JSP+MySQL九宫格日志系统:轻量级B/S架构毕设实战

简介&#xff1a;本资源是一套完整的JavaWeb毕业设计项目源码及配套材料&#xff0c;面向计算机专业本科生、Java初学者与课程设计实践者&#xff0c;解决毕业设计选题难、系统开发流程不清晰、前后端功能模块整合经验不足等实际问题。压缩包为ZIP格式&#xff0c;大小25.18MB&…

作者头像 李华
网站建设 2026/10/19 7:44:36

控制系统设计必会:MAD流程与MATLAB建模仿真实战

做控制系统设计&#xff0c;很多人第一反应就是拿 PID 先怼上去&#xff0c;调几个参数看看波形不对再改。这种"瞎试"路数不是不行&#xff0c;但遇到稍微复杂的对象&#xff0c;比如多变量系统、带滞后环节、有强耦合的被控对象&#xff0c;很快就会陷入"调了这…

作者头像 李华
网站建设 2026/10/19 7:44:27

大模型微调实战:2026程序员AI转型避坑指南与三大技术栈

这两年跟程序员聊AI转型&#xff0c;聊得最多的其实不是“要不要学”&#xff0c;而是“学了很久还是不知道能干什么”。这是2026年最典型的焦虑&#xff1a;大模型微调、技术栈、Agent、RAG、Prompt工程这些词满天飞&#xff0c;课程也囤了一大堆&#xff0c;最后发现自己还是…

作者头像 李华
网站建设 2026/10/19 7:43:59

ONNX模型FP16与INT8优化实战:显存、延迟与精度平衡指南

不是从零起步的人&#xff0c;多半是被模型体积和推理延迟逼到墙角&#xff0c;才回头来啃这块硬骨头。onnx格式本身只是一个中间表示&#xff0c;真正决定部署效果的是你在这个中间层上做了什么优化。FP16转换和INT8量化这两招&#xff0c;一个负责降精度省显存&#xff0c;一…

作者头像 李华
网站建设 2026/10/19 7:43:39

VC6.0下HID设备读写:用户态API实现USB通信完整示例

简介&#xff1a;这是来自VC6.0环境下HID设备读写的一份完整示例工程&#xff0c;面向嵌入式系统开发者、驱动开发初学者以及需要与USB人机交互设备通信的Windows程序员。工程围绕设备枚举、打开、报告读写与关闭等核心流程&#xff0c;展示了如何使用SetupDi设备信息API枚举HI…

作者头像 李华