Python爬虫实战:requests+BeautifulSoup+lxml抓取静态网页数据

发布时间:2026/10/15 12:00:39

Python爬虫实战:requests+BeautifulSoup+lxml抓取静态网页数据 1. 从语法都会、项目不会说起为什么拿静态网页练手是最优解学完Python基础语法之后的那段空窗期几乎是每个自学者都会经历的。变量、循环、函数、类都过了一遍练习题也刷了不少可一旦合上教程面对一个空白的编辑器窗口脑子里冒出来的第一个问题往往是我到底能用它做点什么这个阶段最忌讳的就是继续啃语法书因为语法是工具工具只有在使用中才会真正变成你自己的东西。我的建议非常直接——找一个结构清晰、内容规整、不需要登录就能看到数据的网页把它整页抓下来存成结构化文件。这件事听起来简单但它一次性把HTTP请求、HTML解析、数据清洗、文件读写这几条主线全串起来了性价比极高。在众多练手目标里书籍列表类页面是特别适合新手的。原因有三点。第一这类页面的数据是列表套条目的结构一个页面里重复出现同一种卡片字段固定规律性极强写解析逻辑的时候不容易被各种边界情况带偏。第二数据本身有明确的语义书名、评分、评价人数、简介、链接每一条都能对应到现实中的信息抓下来之后你一眼就能判断对不对不需要额外的业务知识去校验。第三这类页面通常是服务端渲染的静态HTML打开浏览器查看源代码就能看到完整内容不需要处理接口加密、动态加载、请求签名这些进阶问题能把注意力集中在解析这件事本身。这一篇要聊的核心就是围绕这样一个书籍列表页面把requests 发请求 BeautifulSoup/lxml 解析 数据落盘这条最经典的链路走通。关键词里的bs4和lxml是两个解析库前者是 BeautifulSoup 的包名后者既可以作为独立的解析器也可以作为 BeautifulSoup 的底层解析引擎。很多人第一次接触会困惑到底该用哪个其实它们不是二选一的关系而是上层接口和底层引擎的关系后面我会用实测数据把这件事讲透。这篇文章适合谁看如果你已经能写出 for 循环和函数知道列表和字典怎么用但还没完整做过一个数据采集小项目那这篇就是为你准备的。如果你已经会写爬虫但一直用 BeautifulSoup 默认的解析器没比较过 lxml那第三、四节的性能对比和选择建议也值得一看。整篇内容我会按先跑通、再优化、最后避坑的顺序展开每一步都给出可以直接复制的代码和真实的踩坑记录。2. 动手之前先把地基打牢请求头、编码与页面结构侦察2.1 为什么第一步不是写代码而是看网页源码新手最容易犯的错误是打开目标页面之后直接开始写soup.find()结果发现抓下来的 HTML 里根本没有想要的数据。这种情况十有八九是因为页面内容是通过 JavaScript 动态渲染的你请求到的原始 HTML 只是一个空壳。所以在写任何解析代码之前必须做一件事在浏览器里右键查看网页源代码注意是查看源代码不是检查元素。如果源代码里能搜到书名说明数据是服务端直出的可以放心用 requests 抓如果搜不到那就得换思路这一篇不展开。我一般会用一个更省事的办法来确认直接用 requests 把页面拉下来把返回的文本存成.html文件然后用浏览器打开这个本地文件看内容是否完整。这一步能同时验证两件事——请求是否成功、内容是否静态。代码如下import requests url https://example.com/books/list headers { User-Agent: ( Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 ) } resp requests.get(url, headersheaders, timeout10) resp.encoding resp.apparent_encoding # 关键一步见 2.2 with open(page_debug.html, w, encodingutf-8) as f: f.write(resp.text) print(resp.status_code, len(resp.text))这段代码里有几个细节值得展开。headers里的User-Agent不是可有可无的装饰很多站点会对没有 UA 的请求直接返回 403 或者一个精简版页面。timeout10是保命参数不加的话遇到网络卡顿程序会一直挂在那里调试体验极差。至于resp.encoding那一行是新手翻车率最高的地方下面单独说。2.2 中文乱码的根源requests 的编码推断机制requests 在拿到响应后会尝试从 HTTP 响应头里的Content-Type字段推断编码。如果响应头里写了charsetutf-8那没问题但很多站点尤其是老站点的响应头里压根不带 charset这时候 requests 会退而求其次用ISO-8859-1来解码结果就是满屏的乱码。解决办法有两个一是手动指定resp.encoding utf-8二是用resp.apparent_encoding让 chardet 这个库去猜。我的经验是优先用 apparent_encoding但抓下来之后一定要肉眼检查一遍。因为 chardet 的猜测不是 100% 准确尤其是页面内容较短的时候它可能把 GBK 猜成别的编码。一个更稳妥的写法是先看响应头print(resp.headers.get(Content-Type)) print(resp.encoding) # requests 推断出来的 print(resp.apparent_encoding) # chardet 猜出来的三个值对比一下如果resp.encoding是ISO-8859-1而apparent_encoding是utf-8或GB2312那就果断用后者。这一步花不了三十秒但能省掉后面半小时对着乱码发呆的时间。2.3 用开发者工具定位数据所在的标签确认页面是静态的之后接下来要做的就是侦察——搞清楚每一条书籍信息被包在什么标签里。打开浏览器的开发者工具用元素选择器点一下书名看它在 DOM 树里的位置。典型的书籍列表结构大概长这样div classbook-item a href/book/1234567/ title某本书 img srccover.jpg alt某本书 /a div classinfo h3a href/book/1234567/某本书/a/h3 p classrating8.7/p p classcount12345人评价/p p classintro这是简介……/p /div /div侦察阶段要记录三件事列表项的容器选择器比如.book-item、每个字段对应的选择器比如书名是h3 a的文本、字段是否可能缺失比如有些书没有评分。第三点特别重要它直接决定了你后面写解析逻辑时要不要加容错。我见过太多新手写的代码在遇到一条没有评分的记录时直接抛AttributeError崩掉整个采集任务前功尽弃。提示侦察阶段建议把一条完整的条目 HTML 复制出来单独存成一个字符串作为后面写解析逻辑的最小测试样本。这样你调解析代码的时候不用每次都发网络请求速度快十倍。3. bs4 与 lxml 的真实关系别再问选哪个了3.1 一个是接口一个是引擎这是新手最容易搞混的概念。BeautifulSoup包名 bs4是一个解析接口库它自己不负责把 HTML 字符串变成树结构而是把这件事委托给底层的解析器。你可以把 BeautifulSoup 想象成一个遥控器解析器则是电视机遥控器负责发指令真正干活的是电视机。bs4 支持多种解析器常见的有解析器调用方式速度容错性依赖html.parserBeautifulSoup(html, html.parser)中等一般Python 内置lxmlBeautifulSoup(html, lxml)快强需安装 lxmlhtml5libBeautifulSoup(html, html5lib)慢最强需安装 html5lib而 lxml 本身也是一个独立的解析库它有自己的 API可以不经过 BeautifulSoup 直接使用走的是 XPath 那套。所以bs4 还是 lxml这个问题的正确问法是我是用 BeautifulSoup 的接口配 lxml 引擎还是直接用 lxml 的 XPath 接口3.2 两种写法的代码对比同一个提取所有书名的需求两种写法分别是这样# 写法一BeautifulSoup lxml 引擎 from bs4 import BeautifulSoup soup BeautifulSoup(html, lxml) titles [a.get_text(stripTrue) for a in soup.select(.book-item h3 a)] # 写法二lxml 原生 XPath from lxml import etree tree etree.HTML(html) titles tree.xpath(//div[classbook-item]//h3/a/text())从可读性上看BeautifulSoup 的 CSS 选择器写法更接近前端思维soup.select(.book-item h3 a)这种链式选择器只要你会写 CSS 就能看懂学习成本极低。lxml 的 XPath 则更数据库思维//div[classbook-item]//h3/a/text()表达的是从任意位置找 class 为 book-item 的 div往下找任意层级的 h3再找它的 a 子节点的文本。XPath 的表达能力更强比如要按位置筛选、按属性包含关系筛选XPath 写起来更顺手。3.3 实测性能差距到底有多大我用同一份约 800KB 的书籍列表 HTML分别用三种方式解析 100 次取平均耗时测试环境为普通家用笔记本Python 3.11解析方式平均耗时ms相对速度html.parser1861.0xlxmlbs4 接口414.5xlxml原生 XPath335.6x差距是肉眼可见的。html.parser 是纯 Python 实现的胜在零依赖、开箱即用lxml 底层是 C 语言写的 libxml2速度自然快一个数量级。对于单次抓取几十条数据的小项目这点差距你根本感觉不到但如果你要循环解析几百个页面或者做增量更新这个差距就会累积成几分钟甚至十几分钟。我的实际选择策略是这样的调试阶段用 html.parser因为不用装任何东西随写随跑正式跑批量任务时换成 lxml把速度提上来。代码里只需要改一个字符串参数切换成本几乎为零。至于 html5lib除非你遇到那种标签嵌套乱七八糟、lxml 都解析不出来的页面否则没必要用它的速度实在感人。注意lxml 是第三方库需要pip install lxml。在部分系统上它需要编译如果安装报错可以先升级 pip或者找对应平台的预编译 wheel 包。这一步卡住的人不少但解决起来通常就是换个源或者升级 pip 的事。4. 把解析逻辑写扎实字段提取、容错与数据清洗4.1 逐字段拆解提取写法假设我们已经拿到了列表页的 HTML现在要把每本书的五个字段提取出来。先看容器定位from bs4 import BeautifulSoup soup BeautifulSoup(html, lxml) items soup.select(.book-item) print(f共找到 {len(items)} 条记录)select返回的是一个列表长度就是这一页的条目数。如果这个数字是 0说明选择器写错了或者页面结构和你想的不一样这时候要回到 2.3 节的侦察步骤重新确认。如果数字和你肉眼看到的条目数对不上比如页面上明明有 20 本这里只找到 18 本那多半是有些条目的 class 名不完全一致需要放宽选择器。接下来逐字段提取。我习惯给每个字段写一个独立的小函数这样逻辑清晰也方便单独测试def parse_item(item): # 书名优先取 a 标签的 title 属性没有则取文本 title_tag item.select_one(h3 a) title title_tag.get(title) or title_tag.get_text(stripTrue) if title_tag else # 链接注意补全为绝对地址 href title_tag.get(href, ) if title_tag else link https://example.com href if href.startswith(/) else href # 评分可能缺失 rating_tag item.select_one(.rating) rating rating_tag.get_text(stripTrue) if rating_tag else 暂无评分 # 评价人数文本里混着人评价要清洗 count_tag item.select_one(.count) count_text count_tag.get_text(stripTrue) if count_tag else 0人评价 count .join(ch for ch in count_text if ch.isdigit()) or 0 # 简介可能很长去掉多余空白 intro_tag item.select_one(.intro) intro intro_tag.get_text(stripTrue) if intro_tag else return { title: title, link: link, rating: rating, count: count, intro: intro, }这段代码里有几个值得说的处理。第一书名我优先取title属性因为列表页的链接文本有时候会被截断比如超长书名后面加省略号而title属性通常是完整的。第二链接要补全成绝对地址因为页面里写的往往是/book/1234567/这样的相对路径存下来之后没法直接用。第三评价人数的清洗用了.join(ch for ch in count_text if ch.isdigit())这比用正则更直观效果一样——把非数字字符全部过滤掉。第四每个字段都做了标签不存在的兜底这是保证程序不崩的关键。4.2 容错设计让程序在脏数据面前活下来真实页面的数据永远比你想的脏。我整理了几种最常见的异常情况和对应处理异常情况表现处理方式字段标签缺失select_one返回 None用三元表达式给默认值文本含多余空白/换行提取出来带一堆\n和空格get_text(stripTrue)数字混入单位12345人评价过滤非数字字符链接是相对路径/book/xxx/拼接域名前缀属性不存在get(title)返回 None用or给备选值编码错误文本出现乱码请求阶段就处理好编码这里我要特别强调get_text(stripTrue)这个参数。不加stripTrue的话提取出来的文本会保留 HTML 里的缩进和换行比如\n 某本书\n 后续做数据比对或者存 CSV 的时候会非常难受。加上之后它会自动去掉首尾空白并且把中间的连续空白压缩。这个参数我几乎是条件反射地每次都加。还有一个隐蔽的坑有些页面的评分是用 CSS 类名表示的比如p classrating-87表示 8.7 分而不是把分数写在文本里。这种情况你就得从 class 名里把数字抠出来。判断方法很简单如果get_text()出来是空的但元素确实存在那就去看看它的属性。4.3 数据落盘CSV 与 JSON 的取舍抓下来的数据总得存起来。最常用的两种格式是 CSV 和 JSON各有适用场景CSV适合字段固定、结构扁平的表格数据用 Excel 直接打开就能看适合做后续的统计分析。缺点是嵌套结构不好表达中文在 Excel 里打开可能乱码需要加 BOM。JSON适合字段可能变化、有嵌套结构的数据程序读取方便天然支持中文。缺点是人眼直接看不方便得用工具格式化。对于书籍列表这种扁平数据我一般两个都存一份CSV 用来快速查看和分享JSON 用来做后续程序处理。写 CSV 的时候有个细节用utf-8-sig编码可以避免 Excel 打开乱码import csv import json data [parse_item(item) for item in items] # 存 CSV with open(books.csv, w, encodingutf-8-sig, newline) as f: writer csv.DictWriter(f, fieldnames[title, link, rating, count, intro]) writer.writeheader() writer.writerows(data) # 存 JSON with open(books.json, w, encodingutf-8) as f: json.dump(data, f, ensure_asciiFalse, indent2)newline这个参数在 Windows 上尤其重要不加的话 CSV 每行之间会多出一个空行。ensure_asciiFalse则是让 JSON 里的中文原样输出而不是变成\uXXXX转义序列。这两个参数都是那种不知道就踩坑、知道了就一劳永逸的典型。5. 从单页到多页翻页逻辑与请求节奏控制5.1 翻页的三种常见模式单页抓完只是热身真正的采集任务通常要翻很多页。翻页逻辑取决于目标站点的 URL 设计常见的有三种第一种是URL 参数翻页形如?page1、?page2这种最简单循环改参数就行。第二种是路径翻页形如/books/page/1/处理方式类似只是拼接位置不同。第三种是游标翻页下一页的链接藏在页面里比如下一页按钮的 href需要先解析出链接再请求这种最灵活但也最麻烦。对于书籍列表页前两种居多。以参数翻页为例import time all_books [] base_url https://example.com/books/list?page{} for page in range(1, 11): # 抓前 10 页 url base_url.format(page) resp requests.get(url, headersheaders, timeout10) resp.encoding resp.apparent_encoding soup BeautifulSoup(resp.text, lxml) items soup.select(.book-item) if not items: print(f第 {page} 页没有数据可能已到末尾) break for item in items: all_books.append(parse_item(item)) print(f第 {page} 页完成累计 {len(all_books)} 条) time.sleep(1) # 见 5.2这里有个判断很关键如果某一页解析出来是空的就认为已经到末尾主动 break。这比硬编码一个页数上限要稳妥因为列表的总页数可能会变。当然如果站点在超出范围时返回的是错误页而不是空列表那这个判断就不成立需要改成检查状态码或者页面特征。5.2 请求间隔不是可选项是必选项time.sleep(1)这一行很多人调试的时候会注释掉觉得慢。我的建议是调试时可以去掉正式跑的时候必须加回来而且间隔不能太短。原因有两层。第一层是礼貌问题你的程序在短时间内发出大量请求对目标服务器是实实在在的压力尤其是小站点可能直接被你的请求打挂。第二层是自保问题很多站点有频率限制请求太快会触发限流轻则返回验证页重则封掉你的 IP前面的工作全白费。间隔设多少合适我的经验值是1 到 3 秒具体看站点规模。大站可以快一点小站慢一点。如果发现开始返回异常内容立刻停下来把间隔调大。另外翻页的时候加一点随机抖动会更好比如time.sleep(1 random.random())让请求节奏不那么机械。提示如果目标站点提供了公开的数据接口或者数据下载优先用官方渠道别去爬页面。这既是对站点的尊重也能让你拿到更干净的数据。5.3 断点续爬的朴素实现批量任务最怕的就是跑到一半崩了前面抓的全丢。一个朴素的解决办法是每抓完一页就落一次盘而不是全部抓完再统一写文件import os def save_page(books, page): filename fbooks_page_{page}.json with open(filename, w, encodingutf-8) as f: json.dump(books, f, ensure_asciiFalse, indent2) # 主循环里 for page in range(1, 11): if os.path.exists(fbooks_page_{page}.json): print(f第 {page} 页已存在跳过) continue # ... 抓取逻辑 ... save_page(page_books, page)这个文件存在就跳过的判断就是最简单的断点续爬。虽然粗糙但非常有效。等你熟练之后可以换成用 SQLite 存数据用唯一键做去重那就更专业了。但对于练手项目文件级别的断点已经足够。6. 那些文档里不会写的坑我的踩坑记录与排查思路6.1 选择器写对了却抓不到数据这是最让人抓狂的情况明明在开发者工具里用同样的选择器能选中元素代码里就是返回空。我遇到过几次原因各不相同排查思路可以按这个顺序走第一步确认你请求到的 HTML 和浏览器里看到的是不是同一份。把resp.text存成文件打开看如果里面没有目标元素那就是动态渲染的问题跟选择器无关。第二步确认选择器有没有被浏览器美化。开发者工具里复制的选择器有时候会带上自动生成的类名或者:nth-child这种脆弱的东西手写的时候要简化。第三步确认大小写和空格。CSS 选择器对类名大小写敏感Book-Item和book-item是两个东西。第四步确认是不是 iframe 里的内容如果是requests 直接抓外层页面是拿不到 iframe 内部内容的。我印象最深的一次是选择器里用了一个带连字符的类名结果在 CSS 选择器里连字符是合法的但我手误写成了下划线排查了二十分钟才发现。从那以后我养成了一个习惯选择器先在浏览器控制台里用document.querySelectorAll()验证一遍确认能选中再往代码里写。6.2 提取的文本里混着看不见的字符有一次我抓下来的书名存进 CSV 之后看起来完全正常但用程序做去重的时候明明一样的书名却被判定为不同。查了半天才发现有些书名里混了不换行空格\xa0和零宽字符。这些字符肉眼看不见但会影响字符串比较。解决办法是在清洗阶段统一处理import re def clean_text(text): if not text: return # 把不换行空格、各种空白统一成普通空格 text text.replace(\xa0, ).replace(\u3000, ) # 去掉零宽字符 text re.sub(r[\u200b-\u200f\ufeff], , text) # 压缩连续空白 text re.sub(r\s, , text) return text.strip()这个clean_text函数我现在几乎是每个采集项目都会带上。它处理的三类字符——不换行空格、全角空格、零宽字符——是中文网页里最常见的隐形杀手。尤其是从某些内容管理系统生成的页面这些字符出现频率很高。6.3 属性取值时 None 的连锁反应tag.get(href)在属性不存在时返回None如果你直接拿它去做字符串操作比如None.startswith(/)程序立刻崩。这种错误在单条数据上不明显但批量跑的时候只要有一条数据缺属性整个任务就中断了。我的处理原则是任何从页面提取的值在进入业务逻辑之前都要经过一次归一化确保类型和默认值明确。def safe_get_attr(tag, attr, default): if tag is None: return default value tag.get(attr) return value if value is not None else default这个辅助函数看起来啰嗦但它把可能为 None这个不确定性挡在了业务逻辑之外。写采集代码本质上就是在跟各种不确定性打交道能提前消除一个是一个。6.4 关于请求失败的重试网络请求失败是常态不是异常。超时、连接重置、偶发的 5xx都会发生。一个健壮的采集脚本应该有重试机制。最简单的做法是包一层循环def fetch(url, retries3): for i in range(retries): try: resp requests.get(url, headersheaders, timeout10) if resp.status_code 200: resp.encoding resp.apparent_encoding return resp.text print(f状态码 {resp.status_code}第 {i1} 次重试) except requests.RequestException as e: print(f请求异常{e}第 {i1} 次重试) time.sleep(2 * (i 1)) # 退避越往后等越久 return None这里的time.sleep(2 * (i 1))是一种简单的指数退避思路第一次失败等 2 秒第二次等 4 秒第三次等 6 秒。为什么要退避而不是立刻重试因为如果对方是临时过载你立刻重试只会加重它的负担等一会儿反而成功率更高。这个思路在处理任何网络交互时都适用。7. 把项目再往前推一步从能跑到好用7.1 用 dataclass 让数据结构更清晰前面我用字典来存每条书籍数据字段靠字符串键访问。项目小的时候没问题但字段一多book[tltle]这种拼写错误编译器是发现不了的只有运行时才报错。用dataclass可以把字段定义显式化from dataclasses import dataclass, asdict dataclass class Book: title: str link: str rating: str count: str 0 intro: str def to_dict(self): return asdict(self)这样写的好处是字段名在定义处集中管理IDE 能给你补全提示拼错了立刻有波浪线。存 JSON 的时候用asdict一键转字典非常方便。这是从能跑到好维护的一个小跨越值得养成习惯。7.2 把配置抽出来URL、请求头、翻页范围、输出路径这些东西散在代码各处改起来要满文件找。抽成一个配置区放在文件顶部或者单独放一个config.py# config.py BASE_URL https://example.com/books/list?page{} START_PAGE 1 END_PAGE 10 REQUEST_INTERVAL 1.5 OUTPUT_DIR ./output HEADERS { User-Agent: Mozilla/5.0 (...) Chrome/120.0.0.0 Safari/537.36 }这个习惯在项目变大之后价值会越来越明显。尤其是当你需要针对不同站点复用同一套采集框架时把变化的部分抽成配置不变的部分沉淀成函数代码复用率会高很多。7.3 日志比 print 更值得投入调试阶段用print没问题但正式跑批量任务时print的局限就出来了没法分级、没法带时间戳、没法同时输出到文件。Python 内置的logging模块几行就能配好import logging logging.basicConfig( levellogging.INFO, format%(asctime)s [%(levelname)s] %(message)s, handlers[ logging.FileHandler(spider.log, encodingutf-8), logging.StreamHandler(), ], ) logging.info(开始采集共 %d 页, END_PAGE - START_PAGE 1) logging.warning(第 %d 页返回空数据, page) logging.error(请求失败%s, url)配好之后屏幕上能看到进度文件里留了完整记录出问题的时候翻日志就行不用重新跑一遍去复现。这个投入在第一次排查线上问题时就能回本。7.4 关于合规与边界的一点个人看法做采集这件事技术之外还有一层边界意识。我的原则是只抓公开的、不需要登录就能看到的数据控制请求频率不给对方造成压力抓下来的数据只用于个人学习分析不二次分发。如果目标站点在robots.txt里明确禁止了某个路径那就绕开它。这些不是技术问题但决定了你做的事情是不是站得住脚。练手项目尤其要注意选一个对采集友好的站点比什么都重要。8. 写在最后这套链路能带你走多远把 requests、BeautifulSoup、lxml 这三样东西串起来跑通一个书籍列表的采集看起来只是个小练习但它覆盖的能力是通用的。你在这过程中学会的编码处理、选择器调试、容错设计、断点续爬、日志记录换任何一个静态页面都适用。等你把这条链路走熟了再去碰动态渲染的页面、需要登录的接口、有反爬策略的站点你会发现要补的只是特定环节的知识主干逻辑你已经有了。我自己带过不少刚学完基础的朋友他们卡住的地方往往不是语法而是不知道一个完整项目长什么样。所以我的建议一直是别等准备好了再动手找一个结构清晰的页面今天就把它抓下来。第一版代码丑一点、慢一点都没关系跑通之后你自然知道该往哪里优化。真正让人成长的从来不是看完一篇教程而是自己动手踩完那几个坑之后回头看代码时冒出的那句原来是这样。如果你已经跟着把代码跑起来了下一步可以试试把数据存进 SQLite加上去重逻辑再写个简单的统计脚本看看评分分布是什么样的。这个延伸方向不需要新知识但能让你对数据采集之后能做什么有更具体的感受。
延伸阅读

更多相关文章

2026/10/15 12:00:39

光伏并网验收测试:基于仿真网关的DREAMS对接验证实践

简介:面向光伏监控系统测试人员、运维人员及需要对接台电DREAMS规范的云平台厂商,这份测试工具包用于验证云端太阳能监控系统是否符合台电DREAMS要求。核心围绕DNP 3.0协议实现发电数据回传与控制指令交互,可测试变流器实功、虚功、功率因数上…

2026/10/15 12:00:39

DL/T 698.45规约源代码实战:链路层、OAD与主站收发骨架

简介:这是一份面向电力行业采集系统开发者的 DL/T 698.45 规约源代码,专注解决主站、采集终端及电能表之间的互操作性数据交换需求。资源涵盖协议完整实现,包括通信架构、数据链路层、应用层、接口类与对象标识等模块,适合从事 69…

2026/10/15 12:00:39

劳动合同到期不续签通知书的撰写与风险防控

1. 劳动合同到期不续签通知书的实务解析作为企业HR或法务人员,处理劳动合同到期事宜是日常工作中的重要环节。其中"不续签通知书"的撰写与发放,直接关系到用工风险防控和员工关系管理。这份看似简单的文书,实际上需要兼顾法律合规性…

2026/10/15 13:00:57

从Flash Attention到TileLang:GPU算子编译的显式控制之路

前段时间做高性能算子优化的时候,一直在跟 Triton 和 TVM 较劲,卡在 Flash Attention 的调度细节上整整一周。后来在一个交流群里看到有人提了一嘴 tilelang,说它在 GEMM 上的表现比手写 CUDA 还稳,我当时第一反应是不太信&#x…

2026/10/15 13:00:57

轨道磁卡读写程序含源码:从读卡到写卡与避坑指南

简介:这份资源是面向.NET、VB、VC开发者的23轨道磁卡读写程序及完整源码,适合金融、交通、门禁等领域的初学者与进阶人员学习磁卡数据读取、解析与写入的底层实现。压缩包为rar格式,共113个文件、约10.28MB,包含exe可执行程序、dl…

2026/10/15 13:00:56

Python开发Hadoop运维挑战小游戏实战

1. 项目背景与核心价值去年在给团队做技术培训时,我发现单纯讲解大数据集群运维的故障排查流程,效果远不如让学员亲自动手实践。但直接在生产环境演练风险太高,搭建完整的测试集群又耗时耗力。于是我用Python开发了这个不足百行的运维挑战小游…

2026/10/15 13:00:56

OpenRig 多GPU开源工作站搭建指南:从硬件选型到软件栈全记录

如果你在某个开源社区或技术群里搜 OpenRig,大概率会得到一堆含混的结果:有人拿它指模拟赛车驾驶舱,有人拿它指多显卡整机架,还有人只是随手给电脑主机起了个名字。我这儿说的 OpenRig,是我自己整理的一套多GPU开源工作…

2026/10/15 13:00:56

Claude Code技能包superpowers:让AI编程告别越用越乱

刚接触 Claude Code 的人很容易遇到一个瓶颈:单轮对话里让它写个小函数没问题,一旦任务变成"要做一个完整功能、要改十几个文件",它的表现就开始飘忽。一会思路清晰给你列一二三四,一会又在一个小 bug 上原地打转。supe…

2026/10/15 12:55:55

Flutter+Hive在OpenHarmony电子合同应用中的实践

1. 项目背景与核心需求电子合同签署应用在现代商务场景中扮演着越来越重要的角色。随着移动办公的普及,我们需要一个能在OpenHarmony系统上流畅运行的电子合同解决方案。Flutter框架因其跨平台特性成为理想选择,而数据持久化则是确保合同信息安全存储的关…

2026/10/14 11:31:03

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/14 17:41:07

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/14 2:32:55

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/15 0:02:22

防关联浏览器哪个好?三款产品实测差异解析

新手做多店铺,最纠结的就是防关联浏览器选哪个。飞跨、网易、站斧这三款被问得最多,先给结论:要一体化省心、长期便宜,选站斧;要网易招牌和稳,选网易;预算紧做东南亚 Lazada,选飞跨。…

2026/10/15 0:02:22

影刀RPA深度实战:大模型Fine-tuning定制RPA专用Prompt与指令生成

影刀RPA深度实战:大模型Fine-tuning定制RPA专用Prompt与指令生成 一、技术背景 大模型(LLM)在通用场景下表现强大,但当我们将 GPT-4 或 Qwen 直接用于生成影刀RPA操作指令和 XPath 定位时,准确率通常只有 40%-60%。原因…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑