news 2026/10/12 8:56:21

XPath与Parsel实战:告别正则,打造稳定HTML解析方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
XPath与Parsel实战:告别正则,打造稳定HTML解析方案

解析HTML这件事,在你刚开始写爬虫的时候可能觉得特简单:拿到网页源码,正则一瓢,数据就出来了。但等你真正大量跑数据、频繁换网站、或者隔段时间回头维护老脚本时,会发现正则方案又脆又难改。这就是我在整个爬虫入门系列里,把解析这块单独拿出来讲的原因,也是本章想帮你解决的核心问题:如何用XPath和Parsel,写出一套更稳定、更抗网页改版、且对新手友好的解析方式。

做爬虫和写业务代码不一样,同一页面今天能跑、明天就能挂,最常见的原因不是反爬,而是页面结构变了。正则靠的是字符串模式匹配,页面动一个小标签就可能废掉整条规则;而XPath基于HTML文档树形结构定位,哪怕改动一两个属性,通常也只是小修。这篇内容会带你从“为什么要换解析思路”开始,把XPath的核心语法拆透,再把Parsel这个封装好的解析库讲明白,最后用一段真实可跑的代码走通整个流程。没有正则基础完全没关系,只要你能看懂前端页面的结构,哪怕第一次接触XPath,也能跟下来。

1. 为什么要换掉正则:XPath解析的思想转变

1.1 正则解析的三大痛点

正则表达式确实是个好工具,尤其在处理纯文本、日志、字符串匹配的时候,它又快又精准。但放到HTML解析场景里,它的短板会随着页面复杂度无限放大。

第一个痛点是可读性差。一个稍微复杂点的匹配规则,比如要提取一个列表页里所有链接的URL、标题、时间,正则写完几乎是一串“天书”,稍微隔几天自己都难读懂。更别提后期维护,新同事看到这堆规则基本无从下手。

第二个痛点是脆弱。HTML标签嵌套、属性顺序变化、不规范的自闭合标签、JS动态注入的节点……任何一个细微变动都可能导致正则匹配不到预期内容,而且不容易排查到底哪里断了。正则默认匹配是面向“字符串形态”的,它不关心这段字符串在文档结构里是什么位置,所以你没办法说“我只取这个区块里的链接”——除非你把“这个区块”也全部写进正则,那规则会膨胀到失控。

第三个痛点是数据清洗成本高。正则拿到的是带各种空白符、换行、标签残留的裸字符串,你得再叠加一组清理逻辑。做一次两次还可以,做成百上千个规则的时候,你会浪费大量时间在“擦屁股”上。

那有没有一种工具,是直接帮我们“按结构取内容”的?有,就是XPath。它把HTML当成一棵树,你要做的就是告诉它:我要找什么类型的节点、这个节点有什么特征、路径怎么走。思路一旦转过来,后面所有东西都会顺很多。

1.2 XPath是怎么“框”住数据的

XPath(XML Path Language)本身是一门查询语言,它的设计目标是为了在XML文档中定位节点。HTML虽然不是严格的XML,但结构上和XML一样都是标签嵌套的树状结构,所以XPath完全可以作用于HTML。

这个“树”的概念特别重要。你看一个html页面,最外层是<html>,里面分<head>和<body>,<body>里有很多<div>,每个<div>里又有标题、段落、链接……这些元素天然形成父子、兄弟、祖先和后代关系。XPath做的事情,就是沿着这棵树的枝干去走,用路径表达式告诉程序从哪个节点出发,经过什么关系,最终落在哪个节点上。

你可以把它理解成去一个大型仓库里找货:仓库有A区、B区,A区有货架1到20,货架上有不同层,每层有不同货位。你可以不关心货物长什么样,只要按路径走过去,就能精准拿到对应位置的货物。XPath里面的/html/body/div[2]/a就是一个类似的“仓库定位路线”:从根节点出发,一层一层往下走,经过第2个div,最终找到a标签。

理解了这层思想,你就明白为什么它稳定了:只要树的主要骨架没变,哪怕某个节点内部多了一堆东西、属性值顺序变了,定位路线依然有效。而且XPath也允许你“按条件找”,比如“所有class包含‘title’的节点”,这样就算页面属性有小调整,规则也不会立刻失效。

1.3 XPath和CSS选择器的关系

你可能听说过前端里更常用的是CSS选择器,比如div.title a或者#content p。CSS选择器和XPath在功能上有重叠,很多解析库两种都支持。如果你熟悉CSS选择器,上手XPath不难,因为逻辑是共通的,只是语法不同。

在我的实际使用里,两者各有优势。CSS选择器写起来简短,适合结构稳定的页面;XPath表达能力更强,特别是需要按文本内容定位、需要用到轴(比如取父节点、前一个兄弟节点)时,CSS选择器会比较吃力。Parsel这个库两者都支持,我的习惯是优先用XPath,因为它更统一,遇到复杂页面也能应付,不会出现CSS表达式写不了只能混用的情况。

如果你以前只写过正则,那完全不用慌。XPath的语法量很小,核心就几个表达式,配合实践很快就能形成肌肉记忆。下面这一节,就带你过一遍最常用的基础。

2. 先把XPath语法啃明白:节点、路径、谓词

2.1 HTML文档就是一棵树

要让XPath用得好,你得先学会看DOM树。打开浏览器F12,Elements面板里那一层一层的缩进结构,就是树的直观体现。每个标签是一个节点,标签里的文本是文本节点,属性是属性节点。XPath能操作的节点类型大概就这几种:元素节点、属性节点、文本节点,以及少用的命名空间节点。

我建议新手在写XPath之前,先养一个习惯:在开发者工具里先展开目标元素的父级、兄弟级,搞清楚它在树里的实际位置。很多新手一上来就写//div,这样确实能匹配到所有div,但你要的东西通常不在第一个div里,结果写了一长串却拿不到数据,原因就是对树结构不熟。

看树结构时重点关注三样东西:标签名、关键属性(一般看class或id)、文本位置。有了这三个坐标,XPath基本就能定了。

一份典型的HTML结构大概是这样的:

<div class="news-list"> <div class="news-item"> <h3><a href="/news/1001.html">标题一</a></h3> <span class="date">2024-01-01</span> </div> <div class="news-item"> <h3><a href="/news/1002.html">标题二</a></h3> <span class="date">2024-01-02</span> </div> </div>

在这段结构里,news-list是父容器,每个news-item是一条新闻。我们要提取每条新闻的链接和标题,就要学会用XPath来描述“在class为news-item的div里,找h3下的a标签”。

2.2 XPath常用表达式速查表

我整理了一个新手最该记熟的速查表,你写代码时可以先贴这份出来照着用,用多了自然就记住了。

表达式含义示例
/从根节点直接选取/html/body/div
//从任意位置选取,忽略层级//div/a
.当前节点./a
..父节点//a/..
@选取属性//a/@href
text()选取文本//h3/text()
*通配符,匹配任意元素//div/*
[n]谓词,按顺序筛选//div[2]
[@class="x"]按属性值筛选//div[@class="item"]
[contains(text(),"关键词")]按文本内容模糊匹配//p[contains(text(),"Python")]
[contains(@class,"item")]按属性模糊匹配//div[contains(@class,"item")]

这里面的核心规则我展开说一下:

  • //开头的叫相对路径起点,意思是“不管在哪一层,从任意位置向下找”。写爬虫时绝大多数表达式都用//开头,因为绝对路径/html/body/div/...太长,而且页面只要在外面包一层就失效了。
  • @用来取属性值,比如//a/@href取到的是一堆URL字符串。这是最常用的需求,务必记牢。
  • [n]的索引从1开始,不是0。这个坑很多新手踩过://div[1]选的是第一个div,不是第0个。
  • contains()是救命函数,因为网页上很多class带多个值,比如class="title red big",你用[@class="title"]反而匹配不到,改成[contains(@class,"title")]才稳妥。

2.3 谓词与轴:定位的进阶玩法

谓词就是中括号里的筛选条件,上面提到的是最基础的用法。稍微进阶一点的,是组合谓词,比如同时限定标签、属性和位置:

//div[contains(@class,"item")][2]

这个表达式的意思是“在class包含item的div里,找第二个”。注意[2]是先用前面的条件筛出一组节点,再取这一组里的第二个。

轴是XPath里定位关系比较丰富的一部分,但实际爬虫中用得不算太多,我挑两个实用的讲解:

  • following-sibling:取当前节点后面的兄弟节点。典型场景是:某个标题后面跟着日期,但日期没有独立class,你可以用//h3/following-sibling::span拿到它后面的span。
  • ancestor:取所有祖先节点。典型场景是:你找到了一个链接,想回退到它所在的整个容器拿其他数据,就可以用//a[contains(@href,"news")]/ancestor::div[contains(@class,"item")]。

轴的完整列表还有child、parent、preceding、descendant等,但说实话日常用到最多的就是前面的兄弟和祖先这一个来回。懂这两个,配合谓词,绝大多数页面都能覆盖了。

3. Parsel:让XPath更好用的“翻译官”

3.1 为什么选Parsel而不是纯lxml

如果你在搜索引擎里搜XPath解析,会看到两种主流方案:一种直接用lxml库,一种用Parsel。lxml确实底层强大,但它比较“裸”——你拿到HTML后得自己html.fromstring(),然后到处tree.xpath(),每一步都要手动管理。而对新手来说,Parsel的优势在于它对XPath和CSS选择器做了统一封装,接口简洁,返回对象又自带继续筛选的能力,用起来顺手得多。

另外一个很现实的理由是:Parsel的底层本身就是lxml,所以性能上并不吃亏,它只是在上面包了一层更友好的API。你在爬虫里用Parsel,等于既享受了lxml的速度,又不用跟繁琐的底层操作较劲。我在生产脚本里也是更喜欢用Parsel,尤其是要连续在同一个页面上做多轮解析时,返回的Selector对象可以直接再调.xpath(),可以非常自然地“链式取数”。

安装也很简单:

pip install parsel

安装好后,核心的导入和初始化程序如下:

from parsel import Selector html = "<html><body><h1>你好,爬虫</h1></body></html>" sel = Selector(text=html) print(sel.xpath('//h1/text()').get())

这一步做完,你就拿到了一个可用的解析对象。下面看一下它到底能做哪些操作。

3.2 Selector的基本操作

Selector对象有三个高频方法,新手一定要分清楚:

  • .xpath():用XPath语法提取
  • .css():用CSS选择器语法提取
  • .re():用正则表达式对已提取的内容进一步处理

.xpath()返回的仍然是一个Selector列表,所以你可以继续对它调.xpath(),这就是链式操作的基础。什么时候会用到链式?假设列表页里每条新闻是一个div,我先取所有div,再在div内部继续取a链接和标题,这样逻辑清晰,代码也好维护。

items = sel.xpath('//div[contains(@class,"news-item")]') for item in items: title = item.xpath('.//h3/a/text()').get() link = item.xpath('.//h3/a/@href').get() print(title, link)

这里有一个非常关键的小细节:在循环内部再次使用xpath时,路径开头要加上.。.//h3/a/text()表示“从当前节点往下找h3里的a”,如果不加这个点,它会从整个文档根节点重新查找,很可能查到的不是当前这一条数据的内容。这个坑几乎每个新手都会踩,后面问题排查部分我会再强调。

.re()方法适合在已经拿到URL或者文本之后,再做一层精细提取。比如链接里带了参数,你只想要其中的id,就可以这样:

item.xpath('.//h3/a/@href').re(r'(\d+)')

它会返回所有匹配到的数字。用.re()做二次加工能让解析规则更短,不用专门写一个清理函数。

3.3 get()、getall()、extract()到底啥区别

这部分是Parsel新手最容易混淆的。先看传统的extract()和extract_first(),它们返回的是字符串列表或单个字符串,但Parsel官方现在已经不太推荐这种写法了,更推荐用get()和getall()。

三者的对应关系如下:

方法作用替换旧写法
.get()取第一个匹配到的字符串,没有则返回Noneextract_first()
.getall()取所有匹配到的字符串,返回列表extract()
.extract()取所有匹配到的字符串(旧写法)不推荐

它们在日常写代码里的区别体现在两个方面。第一是空值时:get()拿不到内容时返回None,而extract_first()在旧版本里可能报错或返回None,写法上get()更安全。第二是返回类型:getall()一定返回列表,哪怕只有一个结果也是列表,这让你后续的循环处理更统一。

我给你一个写代码的默认规范,可以少踩很多坑:

# 推荐 title = item.xpath('.//h3/a/text()').get() links = item.xpath('//a/@href').getall() # 不推荐 title = item.xpath('.//h3/a/text()').extract_first() links = item.xpath('//a/@href').extract()

另外,从SelectorList对象里取元素还有一个[0]的方式,比如sel.xpath('//title/text()')[0]。这种方式在你明确结果只有一个时可用,但它不会做边界处理,一旦xpath没匹配到就会报IndexError。我用get()就是为了让脚本在页面结构有小变动时不至于崩掉,最多返回None,方便下一步做空值判断。

4. 实操:用XPath/Parsel写一个新闻列表爬虫

4.1 先看目标:定义任务和解析需求

纸上谈兵没有用,我们直接跑一个贴近生产的例子。假设要抓取一个新闻列表页,目标数据是每条新闻的标题、链接和发布日期。我准备一段示例HTML来模拟这个页面结构,然后把XPath一步一步写出来。

<div class="container"> <h2>今日要闻</h2> <ul class="list"> <li class="item"> <a href="/news/101.html" class="title-link">Python 3.12发布</a> <span class="time">2024-02-01</span> </li> <li class="item"> <a href="/news/102.html" class="title-link">爬虫框架对比</a> <span class="time">2024-02-02</span> </li> <li class="item"> <a href="/news/103.html" class="title-link">Parsel使用技巧</a> <span class="time">2024-02-03</span> </li> </ul> </div>

在这个页面里,每条新闻都包在li.item里,标题是a.title-link的文本,链接是它的href属性,时间是span.time的文本。解析需求很清晰,接下来就按照“先定位容器、再取具体字段”的顺序来设计XPath。

4.2 三步写出稳定XPath

第一步,定位外层容器。很多页面外层容器没有语义化,可能一个div套一个div,所以我会先找有没有含关键词的class。这里容器是ul.list,那第一步的XPath就是//ul[contains(@class,"list")]或者//ul[@class="list"]。用contains是考虑到这个class可能还带了别的值,稳妥一点。

第二步,定位每条数据的父级。继续往下一层,每条新闻在li.item里,所以在容器内部找li[contains(@class,"item")]。如果要写成一个表达式,那就是//ul[contains(@class,"list")]/li[contains(@class,"item")]。

第三步,分别提取字段。在父级内部,标题是./a[contains(@class,"title-link")]/text(),链接是./a[contains(@class,"title-link")]/@href,时间是./span[contains(@class,"time")]/text()。

把这三步组合起来,就跑出完整的取数逻辑了。你可以看到整个过程是有迹可循的:容器 → 数据行 → 字段,每层之间的关系都是明确的,后期维护时只要对照页面结构,很快就能定位哪里需要改。

4.3 完整代码和效果演示

下面给出一个可以直接跑的脚本。这里我用了parsel的Selector,然后用requests库获取真实页面时会加上headers,但演示时直接用这段HTML字符串,避免请求失败干扰解析学习:

import requests from parsel import Selector # 模拟实际请求时的headers headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" } # 实际项目里这里用 requests.get(url, headers=headers).text html = """<div class="container"> <h2>今日要闻</h2> <ul class="list"> <li class="item"> <a href="/news/101.html" class="title-link">Python 3.12发布</a> <span class="time">2024-02-01</span> </li> <li class="item"> <a href="/news/102.html" class="title-link">爬虫框架对比</a> <span class="time">2024-02-02</span> </li> <li class="item"> <a href="/news/103.html" class="title-link">Parsel使用技巧</a> <span class="time">2024-02-03</span> </li> </ul> </div>""" selector = Selector(text=html) items = selector.xpath('//ul[contains(@class,"list")]/li[contains(@class,"item")]') for item in items: title = item.xpath('./a[contains(@class,"title-link")]/text()').get() link = item.xpath('./a[contains(@class,"title-link")]/@href').get() publish_time = item.xpath('./span[contains(@class,"time")]/text()').get() print(f"标题: {title} | 链接: {link} | 日期: {publish_time}")

运行后你会在终端看到三条清晰的数据记录:

标题: Python 3.12发布 | 链接: /news/101.html | 日期: 2024-02-01 标题: 爬虫框架对比 | 链接: /news/102.html | 日期: 2024-02-02 标题: Parsel使用技巧 | 链接: /news/103.html | 日期: 2024-02-03

到这里,一个小而全的解析流程就跑通了。实际使用中,你只需要把Selector(text=html)换成Selector(text=requests.get(url, headers=headers).text)就可以应对动态请求到的页面。但要注意一点,如果页面内容是JavaScript动态渲染的,requests拿到的HTML里可能并没有这些节点,这时候你需要先确认页面数据是否直接写在源码中,或者另用浏览器自动化方案,这属于爬虫进阶范畴,这里先不提。

5. 常见问题与排查技巧

5.1 XPath明明没错但抓不到数据

这是我被问得最多的问题。表达式在浏览器Console里测试能选中,但到了Python里用Parsel却拿不到结果。排查思路按顺序来:

第一步,打印你的响应内容,确认拿到的确实是你想解析的HTML。有可能是你请求到了一个重定向页面、登录页、或者反爬验证页。你要解析的节点根本不在这个响应里,那XPath写得再好也没用。

第二步,检查你用的是不是Selector对象而不是字符串。如果你直接对字符串调.xpath(),是会报AttributeError的。确认你已经初始化了Selector(text=html)。

第三步,注意text()的写法。很多人写//a/text()去取链接文本,结果返回空,原因可能是标签内部不只一层,比如<a><span>标题</span></a>,这时/text()只能拿到直接子文本节点,拿不到span里面的内容。解决办法是改用//a//text()(双斜杠表示取所有后代文本节点),或者直接//a/string(),后者是XPath 2.0的特性,Parsel也能用,会返回当前节点内拼接后的全部文本。

第四步,查看有没有命名空间或者HTML解析异常。这种情况比较少,但如果你直接解析XML格式的页面,可能需要在Selector里指定type="xml"。HTML页面一般不需要。

5.2 网页改版后瞬间失效

爬虫脚本维护过程中,最烦的不是反爬,而是页面结构改版。假设原来新闻标题在h3 > a,后来变成了div.title > a,你的XPath就失效了。这不是bug,而是页面变了,解析规则必须跟着变。

为了减少这类问题带来的维护成本,我的实践有两条。第一,XPath尽量用语义和属性定位,不用绝对层级。比如优先//a[contains(@class,"title")],而不要写/html/body/div[3]/div[1]/h3/a。前者是“我叫什么、我有哪些特征”,后者是“我住在哪条街第几个路口”。页面改版时,特征通常变化不大,纯路径则往往夭折。

第二,给解析写一个独立的配置函数。比如把每条数据的XPath集中放在一个字典里,改版时只动配置不动主逻辑。我自己的项目里通常是这样的结构:

xpath_config = { "item": '//ul[contains(@class,"list")]/li[contains(@class,"item")]', "title": './a[contains(@class,"title-link")]/text()', "link": './a[contains(@class,"title-link")]/@href', "time": './span[contains(@class,"time")]/text()', }

这样维护起来非常省事,页面小改动时改一行配置就行,不会因为一个XPath变动导致整个脚本重写。

5.3 数据带空白和换行的清理

网页里的文本节点经常会夹杂着换行、缩进空格和HTML实体,比如&nbsp;。你提取出来的标题可能是"\n Python 3.12发布 \n",用print看不出来,但入库或存文件时会很脏。

Parsel本身不负责清洗,这里我的经验是给提取后的字符串统一做一步标准化处理,而不是逐个改。我会写一个小函数:

def clean_text(value): if not value: return "" return " ".join(value.split())

value.split()会按任意空白字符切分,再重新用单个空格拼接,这样能把换行、缩进、多空格一次性清理干净。日期字段还可以进一步用strip()去首尾空白。

如果你的提取结果用了.getall()返回列表,那可以对列表里的每个元素map一下这个函数。这样做的好处是逻辑统一,不管哪个字段需要清洗,都走同一套规则,不会出现“标题清了时间没清”的遗漏。

5.4 如何提升解析性能

爬虫如果跑的是一个小脚本,解析性能无所谓;但当你的请求频率高、页面大、字段多时,解析速度会成为瓶颈。Parsel底层是lxml,性能本身就够用,但我在代码层面还有几个优化习惯。

其一,尽量减少重复解析。如果你要对同一个页面的多个字段做提取,不要反复Selector(text=html)初始化,初始化一次就够了,之后在同一个Selector上反复调用.xpath()。我见过有人每取一个字段就重新请求一次页面,这是极大的浪费。

其二,用getall()批量提取,减少循环。假如只需要链接列表,不要写一个循环然后逐个get(),直接在初始Selector上xpath('//a/@href').getall()一次性拿到列表,速度快得多。循环交给后续的数据处理,解析阶段尽量“一步到位”。

其三,避免在XPath里写过于宽泛的通配符。//div//a//text()这种表达式会扫描大量节点,如果页面几千个节点,数量级还好;但几万个节点时,影响就比较明显了。尽量缩小范围,先定位到容器,再在容器内做提取。

这些优化看起来只是小改动,但在跑大规模数据采集时会显著降低整体耗时和CPU占用。

6. 一些经验和心得

这一节算是我个人在写大量爬虫脚本之后沉淀下来的几条体会,不一定在教材里能看到,但对新手挺有帮助。

第一,不要盲信浏览器复制的XPath。F12里右键复制XPath很方便,那段路径通常是以/html/body/...开头的绝对路径,又长又脆。它可以用在调试上,但别直接扔进生产代码。我会拿它当作定位参考,然后自己改写成一个短小、带语义特征、用//开头的表达式。改写的过程也是加深对结构理解的过程。

第二,解析之前先看robots和网站条款。做技术归做技术,但爬虫不是只要代码能跑就行。我在实际做采集项目时,一般都会先确认目标网站是否允许爬取,注意请求频率。这个习惯会帮你避开很多麻烦。

第三,调试XPath时用一个临时脚本或在线工具。我常用的是把一段HTML保存成文件,然后在本地反复跑Selector测试,改表达式刷新结果,比每次都发请求要快很多。Parsel也支持直接对字符串操作,这正好方便本地调试。等到表达式稳定了,再集成进正式脚本。

第四,始终给自己留后路。解析结果里总会存在脏数据、空值、异常值,我在写代码时习惯在每个字段提取后做一个if not title: continue之类的保护判断,保证单条数据出问题时不影响整个任务。这个习惯在批量采集里非常重要,否则一条脏数据可能导致整个任务中断。

第五,解析和存储解耦。这一章节虽然只讲解析,但好的爬虫设计里,解析模块的输出应该是干净、结构化的列表或字典,与后续的数据库写入、CSV导出完全解耦。我一般会在解析完成后先打印或保存几行结果看看,确认无误再进入存储环节。先肉眼验证再落库,能省下后期大量清洗工作。

结尾碎碎念

折腾完这一章,你应该能感受到XPath和正则之间完全不同的思维方式了。正则是在字符串里“找形状”,而XPath是在树里“找位置”,这种结构化思维一旦建立起来,再看复杂页面就不会发怵了。我在自己带新人时总会说一句话:解析代码写得稳不稳,不是看语法记了多少,而是看遇到页面变动时能不能快速定位修改点。把XPath的常用语法练熟,配合Parsel的简单接口,你日常90%以上的解析需求都可以轻松覆盖。

最后再分享一个小习惯:每次写完一个解析脚本,我都会顺手把页面的结构截图或者Markdown注释放在代码头部,标注好关键节点的位置和当时的XPath。这样哪怕过了半年再回来看这个脚本,也能一眼知道当初是怎么设计解析规则的。爬虫这条路,前期拼的是会用工具,中期拼的是设计思路,后期拼的是维护能力。XPath和Parsel只是起点,但打好这部分基础,后面学Scrapy、做分布式采集都会轻松很多。

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

Diff算法深度解析:从文本到虚拟DOM的底层原理

git diff 是我每天用得最多的命令之一。不管是代码审查、找回归、还是看别人提交里到底改了什么&#xff0c;本质上都在做同一件事&#xff1a;把两串内容并排摆在一起&#xff0c;告诉你“哪里变了”。但你真的想过这个“哪里变了”背后是怎么算出来的吗&#xff1f;前几年面 …

作者头像 李华
网站建设 2026/10/12 8:51:37

LLM驱动移动端GUI自动化:ARTEMIS双模式架构解析

1. 移动端自动化测试的现状与ARTEMIS的切入点移动端自动化测试这件事&#xff0c;做过的人都懂——它不像后端接口测试那样干净利落&#xff0c;也不像Web端那样有成熟的DOM结构可以依赖。一台真机摆在面前&#xff0c;屏幕分辨率、系统版本、厂商定制ROM、输入法弹窗、权限弹窗…

作者头像 李华
网站建设 2026/10/12 8:51:06

Python人工智能美颜系统:人脸检测与磨皮滤波实战

简介&#xff1a;这份资源是一套基于Python的人工智能美颜系统完整工程包&#xff0c;面向具备一定Python基础、希望入门图像处理与深度学习美颜应用的开发者与学习者。它通过整合图像处理库与卷积神经网络&#xff0c;解决人像照片肤色增强、面部特征优化等自然美化问题&#…

作者头像 李华
网站建设 2026/10/12 8:50:33

Python读取WAV文件:从RIFF结构到采样数据解析

做过音频处理的朋友都知道&#xff0c;wav文件是入门门槛最低、水也最深的一种格式。说它门槛低&#xff0c;是因为python里随便一个库就能把数据读出来&#xff1b;说它水深&#xff0c;是因为一旦你开始认真处理多声道、24bit、浮点编码这些变种&#xff0c;或者要分析采样点…

作者头像 李华
网站建设 2026/10/12 8:47:55

企业级Agent落地:Harness控制面设计与稳定性实践

1. 从"模型崇拜"到"控制面焦虑"&#xff1a;企业 Agent 落地的真实转折点过去一年多&#xff0c;我参与过好几个企业级 Agent 项目的评审和落地陪跑&#xff0c;有一个现象反复出现&#xff1a;团队把 80% 的精力砸在模型选型、提示词调优、微调数据准备上…

作者头像 李华