news 2026/10/26 4:36:20

Python数据抓取从入门到实战:requests+BeautifulSoup+pandas全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python数据抓取从入门到实战:requests+BeautifulSoup+pandas全流程

如果你跟着这个系列走到第十篇,应该已经能用 Python 写出处理文件、操作列表字典、定义类和函数的程序了。但我想问一个很现实的问题:你手头的练习数据是从哪来的?如果每次都要打开 Excel 手动复制粘贴,那 Python 目前对你来说还只是个“高级计算器”。从这一篇开始,我们把 Python 变成真正的“数据工人”——让程序自己去互联网上获取公开数据,再整理成干净的分析表。

这篇的主题是网络数据抓取与结构化处理,面向刚学完 Python 基础、想往人工智能或数据分析方向走的人。我会用一个“某图书信息采集 Demo”把 requests、BeautifulSoup、pandas 和 SQLite 串起来走一遍,从最基础的请求开始,讲到动态页面处理、JSON 接口调用、数据清洗入库,最后整理一份实战中频繁踩坑的排查清单。学完这一篇,你应该能独立完成“从网页抓到结构化表格”的完整流程,而且知道每一步为什么要这么写。

1. 抓取方案设计:先想清楚再写代码

1.1 先确认抓取目标与合规边界

很多人一提“抓数据”就兴奋,打开终端就开始import requests。我的建议是反过来:先花十分钟回答三个问题——数据源在哪?数据是怎么渲染的?允许不允许抓?这三个问题决定了你后面是半小时收工还是折腾一整天。

先说数据源。你要抓的是静态 HTML 页面、浏览器动态渲染的页面,还是后端直接返回 JSON 的接口?这三种来源的处理方式完全不同:静态页面直接解析 HTML 就行,动态页面可能需要驱动浏览器,而 JSON 接口往往是最省事的那条路。判断方法很简单,打开浏览器开发者工具,切到 Network 面板,刷新页面,看看页面内容是不是在某个 XHR 或 Fetch 请求的响应里。如果响应里直接就是带字段名的数据,那基本可以走 JSON 路线。

再讲合规。这个容易被新手忽略,但不是小事:开始写代码前,先看一眼目标站点根目录下的 robots.txt,以及网站的使用条款。我只建议抓取公开数据、用于个人学习和研究,并且请求频率要克制,不要并发开太大,更不要抓涉及个人隐私的内容。某图书信息采集 Demo 里的示例我放在本地环境跑,你换成线上公开站点时务必先确认边界。说白了,抓取本身不是问题,问题的核心是:你有没有尊重对方的服务器和规则。

1.2 抓取与处理的主干框架

一次完整的数据抓取,无论多复杂,都可以拆成四件事:发起请求、解析内容、清洗数据、存储分析。你可以理解为去图书馆借书:先向图书馆管理员提出申请(请求),拿到书之后找到你要的内容并记下来(解析),把笔记里的错别字和冗余信息去掉(清洗),最后按主题归档(存储分析)。任何环节出了问题,后面所有步骤都会跟着出问题。

这四步的顺序是固定的,因为每一步都依赖上一步的输出。请求不到页面,解析就是空谈;解析出脏数据,清洗就得加班;清洗完没有落盘,程序一重启数据就没了,等于白干。很多新手喜欢跳跃着写,先写“解析”再补“请求”,发现报错一堆,其实问题往往出在前一步的输出根本不符合预期。

所以在动手前,我强烈建议你把这条主链写在一张纸上:请求 -> 解析 -> 清洗 -> 存储。然后在每个箭头旁边写下“怎么验证这一步成功”。请求成功的标志是状态码 200 并且响应内容里包含目标页面;解析成功的标志是能打印出至少一条你要的字段;清洗成功的标志是数据没有重复和缺失;存储成功的标志是文件或数据库里能重新查出来。这个清单就是你的验收标准,也是排查问题的索引。

1.3 工具选型:怎么组合才顺手

网上关于爬虫工具的组合说法很多,我个人的偏好是:静态页面用requests + BeautifulSoup,动态页面用Playwright,清洗和存储用pandas + SQLite。这套组合是我在大量项目里用下来最顺手的,不是因为它功能最全,而是因为它足够轻、足够直观、出了问题容易定位。

为什么静态页面不用 Selenium?因为 Selenium 要额外管理浏览器驱动,而且启动浏览器的开销远大于发一个 HTTP 请求。如果服务器直接返回 HTML,你根本不需要一个完整的浏览器。为什么动态页面推荐 Playwright?因为它的安装体验比 Selenium 好很多,pip install playwright之后一条命令就能装好浏览器内核,API 设计也更现代,用同步写法也能写得很顺手。

工具不在多,在于和场景匹配。下面这张表是我自己在项目里做选择时的依据,你直接抄就行:

场景推荐工具选择理由
静态 HTML 页面requests + BeautifulSoup轻量、快、选择器直观,适合绝大多数列表页和详情页
动态渲染页面Playwright能执行 JavaScript,安装简单,不需要额外下驱动
后端 JSON 接口requests接口直接返回结构化数据,解析成本最低
清洗与汇总pandas去重、缺失值、类型转换都有现成方法
轻量持久化SQLite单文件数据库,无需装服务端,适合个人项目

这套组合还有一个好处:后续接机器学习时,pandas 的 DataFrame 可以直接作为特征工程的输入,不需要中间再转换一次格式。

2. 环境准备与最小可运行示例

2.1 用虚拟环境隔离依赖

我见过不少同学图省事,直接用全局 Python 环境pip install装了一堆包,结果 A 项目要 requests 2.x、B 项目要 1.x,装来装去把环境搞乱了。从系列一开始我就反复强调虚拟环境,这一篇必须再提一次:每个项目单独建一个环境,是成本最低、收益最高的好习惯。

创建和使用虚拟环境只需要三步。在项目根目录执行:

python -m venv venv

Windows 下激活:

venv\Scripts\activate

macOS 或 Linux 下激活:

source venv/bin/activate

激活后终端命令行会出现(venv)前缀,这时候再装依赖就不会污染全局环境。接下来安装本篇需要的库:

pip install requests beautifulsoup4 pandas lxml playwright

如果后面要操作数据库,再加一个sqlalchemy,它能让 pandas 与数据库的交互更顺滑。安装完成后可以用pip list确认版本,重点确认 Python 版本在 3.8 以上——这个系列默认环境不低于 3.8,我用的时候一般是在 3.10 或 3.11,基本没遇到过兼容性问题。

2.2 第一个网络请求:requests 入门

很多人第一次写requests.get()就直接对一个公开网站发起请求,结果要么超时,要么返回的状态码不是你预期的 200,马上就开始怀疑人生。所以我建议先在本地起一个最小环境,先把“请求 -> 响应”这条链路跑通,再上真实场景。

在项目目录下创建一个demo.html,内容就是一组图书列表,方便后面解析练习。你可以先用一段最简单的 HTML:

<ul class="book-list"> <li class="book-item">import requests resp = requests.get("http://localhost:8000/demo.html", timeout=10) print(resp.status_code) print(resp.text[:200])

运行后你应该能看到 200 和 HTML 片段。这里有两个细节值得新手注意。第一是timeout参数,我建议永远不要省略,默认情况下 requests 可能等很久;给你一个 10 秒的限制,如果目标地址有问题就能快速失败,而不是卡住整个程序。第二是resp.status_code,它只表示服务器返回了状态,并不代表你拿到了想要的内容,后续还要通过响应体来做判断。

2.3 用 BeautifulSoup 完成第一次解析

拿到响应文本后,下一个任务是把 HTML 里包裹的字段提取出来。BeautifulSoup 最擅长的就是这件事。下面这段代码会选中ul.book-list里所有的li.book-item,再把每个条目的书名打印出来:

from bs4 import BeautifulSoup soup = BeautifulSoup(resp.text, "html.parser") items = soup.select("ul.book-list > li.book-item") for item in items: title = item.select_one(".title").text print(title)

select()返回所有匹配的元素列表,select_one()返回第一个匹配的元素。如果你学过 CSS,上手会非常快;即使没学过,现查也够用:.表示 class,#表示 id,>表示直接子元素,空格表示后代元素。解析器我用的是html.parser,这是标准库自带的,够用且没有额外依赖。

这里说一下为什么解析器不直接选lxml。lxml解析速度更快、容错更好,但需要编译安装底层库,在部分 Windows 机器上可能遇到环境问题。先用html.parser跑通流程,之后如果遇到性能问题再切换成lxml,这属于优化手段,不是起步必需品。

3. 静态页面抓取与结构化提取实战

3.1 请求头与参数的细节

本地请求跑通之后,切换到真实站点时最容易被新手忽略的就是请求头。很多服务器会拒绝没有User-Agent的请求,或者把你识别成脚本程序。最直接的解法:打开你自己的浏览器开发者工具,在 Network 面板里随便点开一个请求,找到User-Agent的值,复制到代码里。

一个比较完整的请求头写法是这样:

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, headers=headers, timeout=10)

User-Agent是在告诉服务器“我是个普通浏览器”,这不是欺骗,而是让服务器正常提供内容。部分页面还会校验Referer字段,表示请求来源页面,遇到这种情况可以把目标页面的地址填进去。你不需要把所有请求头都搬过来,先带着User-Agent跑,遇到拒绝再逐个加。

请求参数方面,常见的是 URL 后面跟查询参数,例如分页页码。你可以在 URL 里直接拼,也可以用 requests 自带的params:

params = { "page": 1, "size": 20, } resp = requests.get(url, params=params, headers=headers, timeout=10)

用params的好处是 requests 会自动帮你处理 URL 编码,不用担心中文参数或者特殊字符把 URL 搞乱。

3.2 从 HTML 中提取结构化数据

提取数据的核心思路是:先通过元素的 class 或 id 定位到最小重复单元,再在每个单元里提取具体字段。还是以图书列表为例,定位到li.book-item之后,书名、作者、价格、评分都在这个单元内部,逐个提取即可。

items = soup.select("ul.book-list > li.book-item") books = [] for item in items: title = item.select_one(".title").text.strip() author = item.select_one(".author").text.strip() price = float(item.select_one(".price").text) rating = float(item.select_one(".rating").text) books.append({ "title": title, "author": author, "price": price, "rating": rating, }) print(books)

这里我连续用了两个float()转换,是因为 HTML 里拿到的都是字符串,比如"59.0",如果不转,后面没法做平均值和排序。strip()则是去掉文本两端的空白字符和换行,真实页面的 HTML 经常带大量缩进,不处理的话数据里会混进\n和空格,清洗阶段还得返工。

实际情况中,字段内容可能更复杂,比如价格可能是"¥59.00"或"59元"。这时候你就得先按某种规则替换或拆分,再做类型转换。比较通用的做法是用正则表达式提取数字部分:

import re price_text = "¥59.00" price = float(re.search(r"[\d.]+", price_text).group())

这里有个细节:正则[\d.]+会把59.00提出来,但如果价格写成1,200.50,里面有逗号,就需要先去掉逗号。所以我会建议你先把真实页面上的所有字段格式列一遍,把规则理清楚再写代码,而不是看到一个报错改一个。

3.3 多页面抓取与礼貌限速

真实项目不可能只有一个页面。大多数网站的列表页会带分页参数,比如?page=1、?page=2。我们只需要在循环里拼接不同的页码,然后重复“请求、解析、提取、保存”的流程:

import time import random base_url = "http://localhost:8000/demo.html?page={}" for page in range(1, 6): url = base_url.format(page) resp = requests.get(url, headers=headers, timeout=10) # 解析并提取... print(f"page {page} done") time.sleep(random.uniform(1, 2))

循环本身没什么难度,真正的难点是“别把对方服务器打崩”。所以当你在写循环抓取时,time.sleep不应该被省略。random.uniform(1, 2)的意思是每次随机等待 1 到 2 秒,比固定等 1 秒更接近人的行为,也更不容易触发对方的频率限制。

还有一种习惯我在做长任务时一定会用:记录进度。比如我已经抓到第 40 页,程序在 41 页崩了,如果脚本没有记录,我得从第 1 页重新跑,浪费时间不说,还会给对方服务器增加负担。最简单的做法是把已抓到的数据 ID 存到一个集合里,每次请求前先判断这个 ID 是否已经抓过,或者把当前页码写进一个状态文件,恢复运行时从状态文件继续。

4. 动态页面与 API 接口抓取实战

4.1 优先寻找 JSON 接口

遇到动态渲染页面,新手最容易犯的错误是直接上浏览器自动化。其实你先花五分钟做一件事:打开开发者工具的 Network 面板,刷新页面,筛选 XHR 或 Fetch 请求,看看数据是不是已经在某个 JSON 响应里了。

很多现代网站的结构是:后端提供一个 JSON 接口,前端用 JavaScript 把 JSON 渲染成表格或卡片。这种情况下你根本不需要浏览器,直接请求那个 JSON 接口就能拿到干净的数据,效率和稳定性都远高于解析 HTML。接口地址通常在响应里能看到,比如我见过很多接口的路径像是/api/books?page=1,返回内容是一串带字段名的 JSON。

用 requests 请求 JSON 接口,解析成本极低:

resp = requests.get("https://example.com/api/books?page=1", headers=headers, timeout=10) data = resp.json() for book in data["data"]["items"]: print(book["title"], book["price"])

注意我这里的https://example.com/api/books?page=1是演示用的虚拟地址,不是真的有这个接口,实际使用时以你目标站点的请求为准。resp.json()会直接把响应文本解析成 Python 的字典或列表,接下来你要做的事情就是看懂这个嵌套结构、按 key 取数据。

有个技巧值得分享:拿到 JSON 响应后,先别急着写循环。把data渲染成可读格式看一遍,比如print(json.dumps(data, ensure_ascii=False, indent=2)),搞清楚嵌套层次,再动手写提取代码。很多人写错 key、报KeyError,基本都是因为跳过了这一步。

4.2 用 Playwright 处理动态渲染页面

如果确实找不到 JSON 接口,页面内容又必须是浏览器执行完 JavaScript 之后才能出现,这时候才轮到浏览器自动化。我推荐 Playwright,因为它安装过程比 Selenium 顺滑得多,而且内置了自动等待机制,不容易出现“元素还没加载出来就去找它”的竞态问题。

安装完库之后,需要再执行一条命令安装浏览器内核:

playwright install chromium

然后是最小示例,访问我们的本地页面、等待列表元素出现、提取所有书名:

from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch(headless=True) page = browser.new_page() page.goto("http://localhost:8000/demo.html", timeout=15000) page.wait_for_selector("ul.book-list li.book-item") titles = page.locator(".title").all_text_contents() print(titles) browser.close()

wait_for_selector是 Playwright 里我非常喜欢的一个方法,它会一直等到目标选择器出现在页面上,或者达到超时时间。没有这段等待,脚本很容易因为页面还在加载而找不到元素。headless=True代表无头模式,就是浏览器在后台运行、不弹窗口,适合跑自动化任务;调试的时候可以改成False,亲眼看它怎么操作页面,能帮你更快定位问题。

4.3 请求频率、重试与异常兜底

真实网络环境很复杂,请求失败是常态。你的脚本必须把“失败重试”和“异常兜底”写进去,否则一个超时就能让整个抓取任务中断。

一个简单的重试模板是这样:

for attempt in range(3): try: resp = requests.get(url, headers=headers, timeout=10) if resp.status_code == 200: break except requests.RequestException: time.sleep(2 ** attempt) else: raise SystemExit(f"请求失败:{url}")

2 ** attempt是二进制的指数退避策略:第一次失败等 2 秒,第二次失败等 4 秒,第三次等 8 秒。给服务器一个喘息时间,也能提高临时抖动后的成功率。如果三次都失败,就果断放弃,记录下来去看原因,比无限循环耗下去好得多。

关于状态码,这里有几个常见信号。403表示服务器拒绝访问,429表示你请求太频繁、被限流了,418可能是对方故意拦截脚本。遇到这些情况,先检查自己的频率是不是太快、请求头是不是完整,而不是盲目加大并发去“硬刚”。

5. 数据清洗、存储与简单分析

5.1 用 pandas 完成清洗去重

抓下来的数据一定是“脏”的:可能有重复的书名、缺失的作者名、价格字段混着货币符号和空白字符。这时候就该 pandas 上场了。

先把之前循环收集的books列表变成 DataFrame:

import pandas as pd df = pd.DataFrame(books) print(df.head())

然后做三件最基本的清洗操作。第一是去重,很多页面在不同位置会重复展示同一本书,按标题去重最可靠:

df.drop_duplicates(subset=["title"], inplace=True)

第二是处理缺失值。真实数据里作者字段可能为空,评分可能缺失。最简单的策略是直接删除有缺失值的行,但如果缺失占比很高,就要考虑填充:

df.dropna(inplace=True)

第三是类型统一。虽然上一节已经手动转成了 float,但如果有些价格解析失败,这一列还是对象类型,需要重新强制转换并处理异常:

df["price"] = pd.to_numeric(df["price"], errors="coerce") df.dropna(subset=["price"], inplace=True)

errors="coerce"的意思是转换失败时把该值变成NaN,之后你再统一处理。这一招比直接float()硬转要安全得多,因为后者遇到脏字符串会直接报错中断。

5.2 数据持久化:写 CSV 和 SQLite

清洗完之后要落盘,否则程序一关,所有工作白费。最简单的存储方式是 CSV,pandas 一行搞定:

df.to_csv("books.csv", index=False, encoding="utf-8-sig")

encoding="utf-8-sig"是给 Windows 用户准备的,加了 BOM 头之后用 Excel 打开 CSV 不会乱码。如果你只是给程序自己读,用utf-8也可以。

如果数据量比较大,或者你希望后续能用 SQL 查询,我更推荐存进 SQLite。SQLite 是单文件数据库,不需要装数据库服务端,很适合个人项目。用 pandas 自带的方法可以一口气把 DataFrame 写进去,但需要先安装sqlalchemy:

from sqlalchemy import create_engine engine = create_engine("sqlite:///books.db") df.to_sql("books", engine, if_exists="replace", index=False)

if_exists="replace"会先删掉旧表再新建,适合重复采集的场景。如果你更希望保留历史数据,可以换成if_exists="append"。

如果想避开 SQLAlchemy 依赖,也可以直接用标准库逐行插入,代码稍长但更透明:

import sqlite3 conn = sqlite3.connect("books.db") cur = conn.cursor() cur.execute(""" CREATE TABLE IF NOT EXISTS books ( title TEXT, author TEXT, price REAL, rating REAL ) """) cur.executemany( "INSERT INTO books VALUES (?, ?, ?, ?)", [(b["title"], b["author"], b["price"], b["rating"]) for b in books] ) conn.commit() conn.close()

两种方案选一种就行。我个人的判断是:如果后续还要用 DataFrame 做分析,走to_sql更省事;如果只是要一个长期存储的仓库,原生sqlite3足够了,少一层依赖也少一些潜在坑。

5.3 最小可用的统计分析

数据入库不是终点,能从中提取信息才算有价值。我们来做几个最简单的统计,体验一下“数据到手”的感觉。

# 各作者的平均书价,从高到低排 author_price = df.groupby("author")["price"].mean().sort_values(ascending=False) print(author_price) # 价格的整体描述性统计 print(df["price"].describe()) # 评分高于 8.5 的书有多少本 high_rated = df[df["rating"] >= 8.5].shape[0] print(high_rated)

这几个操作在人工智能项目里非常常见。先说groupby:它是“按某列分组再聚合”的意思,相当于 SQL 里的GROUP BY,是做数据探索的基础操作。describe()一行就能输出数量、均值、标准差、最值、分位数,用来快速了解数据分布很好用。条件筛选df[df["rating"] >= 8.5]本质上是布尔索引,它帮你圈定“感兴趣的子集”。

这还只是冰山一角,但已经能说明数据采集的意义:没有数据管道,后面所有特征工程、模型训练都是空中楼阁。抓取、清洗、入库这三步打通,你手里就有了可以反复使用的数据资产。

6. 实战中的常见问题与排查技巧

6.1 网络请求类问题速查

网络请求是整条链路里最容易出问题的环节。下面这个表格是我在带项目时经常贴给新人的速查表,直接对照着排查就行:

现象常见原因解决办法
ConnectionError目标地址不可达、域名解析失败检查 URL 是否正确,本地环境是否已启动
Timeout服务器响应过慢调大 timeout,或换个时间段再试
状态码 403服务器拒绝请求补充 User-Agent、Referer,检查请求频率
状态码 429请求太频繁被限流降低频率,增加 sleep 时间
状态码 500服务器内部错误多半不是你代码的问题,过段时间再用

遇到ConnectionError别急着改代码,先用浏览器手动访问一次目标地址,确认你自己能正常打开。很多所谓“脚本报错”,其实是网络环境、代理设置或目标站点临时故障导致的,代码本身没毛病。

6.2 解析结果为空或字段缺失

解析结果为空,是第二个高频问题。我见过最多的情况是:select返回了空列表,然后程序在遍历时报AttributeError。遇到这种情况,第一件事是打印原始响应文本,确认请求到的到底是不是你预期的页面。有时候你请求的是移动端页面,结构跟 PC 端完全不同,选择器自然匹配不到。

还有一种情况是内容在 JavaScript 渲染后才出现。requests获取的是原始 HTML,里面可能只有一个空壳,真正的内容是后续接口填充的。这时候你就得回到第 4 节的方法:去找 JSON 接口,或者上 Playwright。

最后检查你的选择器本身。浏览器开发者工具支持直接右键复制选择器,但复制的路径往往很长很脆,页面一改就失效。我更推荐手动写短选择器,比如.title、#main .item这种,定位稳定一些。

6.3 中文乱码与编码处理

如果你发现抓下来的中文变成䏿–‡或??,那基本是编码判断出了问题。requests 会根据响应头猜测编码,但很多网站响应头不写charset,或者写得不准确,导致默认按 ISO-8859-1 解码。

处理办法很直接。先手动查看正确的编码是什么:

print(resp.encoding)

如果看起来不对,就用apparent_encoding让 requests 根据内容去猜:

resp.encoding = resp.apparent_encoding

然后再访问resp.text,中文基本就正常了。另一种情况是存储或输出乱码:Python 3 里字符串本身是 Unicode,乱码一般发生在打印到终端或写入文件时。建议在抓取项目里全程统一使用 UTF-8,写入文件时显式指定encoding="utf-8",这样能避开绝大多数编码坑。

6.4 内存占用过高与程序中断处理

抓取规模一大,“跑着跑着内存涨上去”是很多人忽略的问题。原因通常是你在一个列表里不断append所有详情数据,几万条记录全堆在内存里,自然占用飙升。

我更推荐“抓一批、写一批”的思路:每解析完 100 条数据,就写入一次数据库或 CSV,然后把列表清空,而不是攒到最后一次性写入。配合上一个小节提到的进度记录,就算程序中途崩掉,也只是丢失最近一批数据,而不是全部。

进度记录的方式可以很简单。抓完每一页后,把当前页码写进progress.txt,下次启动时读出来从断点继续:

with open("progress.txt", "r") as f: start_page = int(f.read().strip()) # 抓完当前页后更新进度 with open("progress.txt", "w") as f: f.write(str(current_page))

别小看这个操作,它能让你在大任务里节省大量重复请求的时间,也减轻对方服务器的压力。我在本地调试的时候也会用同样的思路,因为它能帮我快速定位到底崩在了哪一页。

我在实际操作中体会最深的一点是:抓取项目里写代码的时间只占三成,剩下七成时间都花在观察页面结构、复现异常、调整选择器上。越急着写代码,越容易踩坑。先打开浏览器手动访问目标页面,看请求、看响应、看数据结构,再回到代码里把流程翻译出来,效率反而更高。

这一篇用到的工具组合已经能覆盖绝大多数静态页面、JSON 接口和部分动态页面的采集任务。下一步你可以试着往管道里加入定时调度、更复杂的字段清洗规则,甚至把采集结果直接喂给后续的机器学习流程做特征工程。那部分内容,我会在系列后面单独展开。

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

AI编程第一步:用numpy、文件读写与matplotlib搭建数据流水线

学Python的人最容易卡住的地方&#xff0c;不是语法&#xff0c;而是“语法都看懂了&#xff0c;一写AI相关的东西还是懵”。尤其到了“从零到精通”系列的第十篇&#xff0c;咱们已经跨过变量、循环、函数、类这些基础关卡&#xff0c;下一步就该碰真正的AI编程链路了。可这条…

作者头像 李华
网站建设 2026/10/26 4:36:08

李代数与四元数:计算机图形学与机器人学的数学基础

1. 李代数与四元数&#xff1a;从数学基础到工程应用在计算机图形学、机器人学和物理引擎开发领域&#xff0c;李代数(Lie Algebra)和四元数(Quaternion)是两个至关重要的数学工具。作为从业十余年的技术专家&#xff0c;我见证过无数工程师在这两个概念上栽跟头——要么因为理…

作者头像 李华
网站建设 2026/10/26 4:36:06

ADTF与ROS2融合在ADAS测试中的实践与优化

1. 项目概述&#xff1a;ADTF与ROS2在ADAS测试中的融合价值在自动驾驶辅助系统&#xff08;ADAS&#xff09;开发领域&#xff0c;数据采集与回放验证是功能迭代的核心环节。传统测试流程中&#xff0c;我们常面临工具链割裂的问题——数据采集用一套系统&#xff0c;算法验证又…

作者头像 李华
网站建设 2026/10/26 4:34:17

微服务架构中的业务完整性验证设计:分层校验与对账兜底

单库单表的时候&#xff0c;一个BEGIN...COMMIT就能兜住的事&#xff0c;拆成六个服务之后&#xff0c;你得写两套校验、一张对账表、一个补偿任务&#xff0c;半夜还可能被叫起来查为什么订单是"已支付"但库存没扣。我见过太多团队在微服务拆分的第一版设计里&#…

作者头像 李华
网站建设 2026/10/26 4:34:11

AI对话中缺失上下文时的智能响应策略

我这边没看到您提到的“上述要求”——当前对话中并没有收到任何之前的规则或需要重新输出的内容&#xff0c;只有您这一条消息。麻烦您把需要我遵守的完整要求&#xff0c;或者需要重新输出的原始内容直接粘贴过来&#xff0c;我再按您的要求整理后输出。

作者头像 李华
网站建设 2026/10/26 4:33:58

海克斯康PC-DMIS三坐标测量系统核心技术解析与应用

1. 海克斯康三坐标测量系统概述在精密制造领域&#xff0c;三坐标测量机(CMM)作为质量控制的"黄金标准"已经服役超过半个世纪。而海克斯康(Hexagon)旗下的PC-DMIS软件&#xff0c;则是这个领域当之无愧的行业标杆。我使用这套系统进行复杂零件检测已有八年时间&#…

作者头像 李华