XPath 简介XPath (XML Path Language) 最初是为了在 XML 文档中进行导航而设计的语言后来被广泛应用于 HTML 文档的解析。与 BeautifulSoup 相比XPath 有以下特点语法强大可以通过简洁的表达式精确定位元素跨平台性几乎所有编程语言都有 XPath 的实现灵活性高可以通过各种轴、谓词和函数构建复杂的选择条件在 Python 中我们主要通过 lxml 或者 selenium 库来使用 XPath 功能。可以通过 pip 安装下面的依赖包。pip install lxml pip install seleniumXPath 测试工具浏览器用浏览器测试验证 XPath 最直观最方便优先推荐这种方式。比如在百度热搜 百度热搜 测试 XPath。在浏览器开发者工具的 Elements 中按Ctrl F在搜索框中输入 XPath 。比如查找百度热搜标题XPath 正确的话会在页面高亮显示对应的元素。lxmlPython 的 lxml 库提供了强大的 XPath 支持。比如以下用来解析提取热搜新闻标题。from lxml import etree import requests # 获取HTML内容 url https://top.baidu.com/board?tabrealtime response requests.get(url) html_text response.text # 解析HTML # html etree.HTML(html_text) # 或者从文件加载HTML html etree.parse(百度热搜.html, etree.HTMLParser()) # 使用XPath提取数据 titles html.xpath(//div/a/div[classc-single-text-ellipsis]/text()) # 获取标题文本 for index, title in enumerate(titles): print(f第 {index 1} 条新闻新闻标题{title}) print(热搜标题提取正常)第 1 条新闻新闻标题去“三好”邻邦家做客 第 2 条新闻新闻标题拜登特朗普太掉价了 第 3 条新闻新闻标题央行1万亿元大红包对普通人影响多大 ... 第 48 条新闻新闻标题世界人形机器人运动会将在北京举办 第 49 条新闻新闻标题苹果探索在浏览器中加入AI搜索功能 第 50 条新闻新闻标题专家解读为何央行此时宣布降准降息 第 51 条新闻新闻标题骑士G2冤死 裁判报告公布三次漏判 热搜标题提取正常seleniumPython 的 selenium 库提也供了强大的 XPath 支持。比如使用 RPA 流程自动化爬取数据的时候总避免不了通过 XPath 去定位和查询元素。以下用来解析提取热搜新闻标题。from selenium import webdriver from selenium.webdriver.common.by import By def init_driver(): option webdriver.ChromeOptions() driver webdriver.Chrome(r./driver/chromedriver.exe, optionsoption) driver.maximize_window() return driver def main(): driver init_driver() url rfile:///E:\lky_project\tmp_project\百度热搜.html driver.get(url) try: xpath_titles //div/a/div[classc-single-text-ellipsis] titles driver.find_elements(By.XPATH, xpath_titles) for index, title in enumerate(titles): print(f第 {index 1} 条新闻新闻标题{title.text}) print(热搜标题提取正常) except Exception as e: print(热搜标题提取报错, e) return if __name__ __main__: main()第 1 条新闻新闻标题去“三好”邻邦家做客 第 2 条新闻新闻标题拜登特朗普太掉价了 第 3 条新闻新闻标题央行1万亿元大红包对普通人影响多大 ... 第 48 条新闻新闻标题世界人形机器人运动会将在北京举办 第 49 条新闻新闻标题苹果探索在浏览器中加入AI搜索功能 第 50 条新闻新闻标题专家解读为何央行此时宣布降准降息 第 51 条新闻新闻标题骑士G2冤死 裁判报告公布三次漏判 热搜标题提取正常XPath 常用函数contains()contains()函数用于判断某个属性的取值中是否包含指定的字符串。其语法格式如下contains(attribute, substring)attribute表示要过滤的属性名称属性名称前需加上符号。substring表示要判断是否包含的字符串。在自动化测试中contains函数特别适用于定位那些属性值不固定的元素。例如某个元素的id属性值在每次页面刷新时都会发生变化但其中某些字符是固定的。通过contains函数可以基于这些固定的字符进行定位。比如页面中查找name属性中包含 username 的input元素//input[contains(name, username)]上面这种语法格式在有些有注释等复杂样式下可能会不生效可以使用下面的格式//div[text()[contains(., 指定的关键词)]]starts-with()与 contains() 类似只不过是限定指定开头的属性或者文本。//div/a/div[starts-with(text(), 国防部)]ends-with()限定指定结尾的属性或者文本部分浏览器不一定支持要想兼容性更强的话建议最好少用。//div/a/div[ends-with(text(), 热烈欢迎 )]position()position()函数用于选取指定位置的元素。其语法格式如下。//element[position() number] //element[position() number] //element[position() number] //element[position() number] //element[position() number]position()表示元素的序号从1开始计数。number表示设定的阈值。比如//element[1]等价于//element[position() 1]比如页面中有多个input元素我们希望选取前两个input元素。//input[position()3]last()last()函数用于选取从后往前数的元素。其语法格式如下//element[last() - number]last()表示匹配元素的总数。number表示从后往前数的元素位置。假设页面中有多个input元素我们希望选取最后一个input元素//input[last()]如果希望选取倒数第二个input元素//input[last() - 1]count()count() 函数通过对子元素指定类型节点进行统计来限定父元素的选取。语法格式如下//element[count(sub_element) number] //element[count(sub_element) number] //element[count(sub_element) number] //element[count(sub_element) number] //element[count(sub_element) number]count(sub_element)表示 element 元素 下 sub_element 的数目。number表示设定的阈值。比如我们选取子元素中 div 的个数为 2 的 a 元素//a[count(div)2]text()text() 函数用于获取元素的直接文本内容。一般与 contains() 结合使用来选取文本内容包含指定字符串的元素。比如查找文本中包含 热烈欢迎 的 div 元素。//div[contains(text(), 热烈欢迎)]也可以用文本的精确匹配。//div[text()热烈欢迎]node()node()函数用于选取所有节点。其语法格式如下//node()node()函数的功能与* 通配符号类似用于选取所有节点。例如//* 或 //node()上述两种表达式的效果相同均用于选取页面中的所有节点但通配符 * 不包括文本注释指令等节点如果也要包含这些节点请用 node() 函数。XPath 基础语法路径操作符操作符描述示例/从根节点选取元素/html/body//递归步进下降选择文档中符合条件的所有元素//a.选取当前节点./div..选取当前节点的父节点../选取属性//div[id]*通配符选择任意元素//*条件谓词XPath 允许我们使用方括号[]来添加条件谓词进行筛选限定 。//div[1] # 第一个div元素 //div[last()] # 最后一个div元素 //div[position()3] # 前两个div元素 //div[class] # 所有有class属性的div元素 //div[classmain] # class属性值为main的div元素 //div[contains(class, content)] # class属性包含content的div元素 //a[text()click] # 文本内容为click的a元素 //a[count(div)2] # 包含2个div元素的a元素轴操作以 百度热搜 首页为例。ancestorancestor 用以获取元素的祖先节点。比如 热搜 对应 xpath 为//div[2]/a/span[text()热搜]则下面的 xpath 用以获取 热搜 元素对应的所有祖先节点//div[2]/a/span[text()热搜]/ancestor::*包含当前节点自身则需要用 ancestor-or-self//div[2]/a/span[text()热搜]/ancestor-or-self::*则下面的 xpath 用以获取 热搜 元素对应的所有 div 祖先节点//div[2]/a/span[text()热搜]/ancestor::divdescendantdescendant 用以获取元素的后代节点。比如获取 //div[idsanRoot] 的所有后代 div 节点不限定节点类型则使用 *//div[idsanRoot]/descendant::div包含当前节点自身则需要用 decendant-or-self//div[idsanRoot]/descendant-or-self::divfollowing 和 following-siblingfollowing 用以选取文档中当前节点结束标签之后的所有节点。//div/div[classbg-wrapper]/following::*如果只选取当前节点之后的所有兄弟节点则使用 following-sibling。//div/div[classbg-wrapper]/following-sibling::*preceding 和 preceding-siblingpreceding 用于选取文档中当前节点开始标签之前的所有节点不包含当前节点的父辈节点。//div[last()]/div[classcontent_1YWBm]/preceding::*如果选取当前节点之前的所有同级节点则使用 preceding-sibling。//div[last()]/div[classcontent_1YWBm]/preceding-sibling::*parentparent 用以获取元素的父节点。//div/div[classbg-wrapper]/parent::*childchild 用以获取元素的子节点。//div/div[idsanRoot]/child::*selfself 用以获取当前元素节点自己本身。//div/div[idsanRoot]/self::*attribute通过属性来获取元素。比如获取所有包含 class 属性的节点。//attribute::class 或 //class获取所有包含 class 属性的 div 节点。//div[class]逻辑运算符and逻辑与比如查询热搜的前三个热搜。//div/div[classcategory-wrap_iQLoo horizontal_1eKyQ and position()4]or逻辑或//div/div[classcategory-wrap_iQLoo horizontal_1eKyQ or position()last()]//div/div[classcategory-wrap_iQLoo horizontal_1eKyQ | position()last()]not非//div/a/div[not(classc-single-text-ellipsis)]| 和 or 的区别 xpath 中这两类运算符我们都可以用那他们有什么区别呢又有哪些要注意的呢|联合运算符用途合并两个节点集node-set。结果返回两个节点集的并集按文档顺序排列自动去重。示例//div | //span表示选择文档中所有div元素和所有span元素。特点操作对象是节点集如元素、属性等。不能用于条件判断。or逻辑或运算符用途在布尔表达式中进行逻辑“或”判断。结果返回布尔值true或false。示例//input[typetext or typepassword]表示选择所有type属性为text或password的input元素。特点操作对象是布尔值通常用于条件表达式。需与其他条件组合使用如and,not。一般来说需要选择多种元素→ 用|需要组合多个条件→ 用or。XPath 优化一般可以在浏览器中直接复制 XPath但是浏览器复制的 XPath 一般是用绝对路径写的能唯一定位这个元素的 XPath。 如果页面结构稍微发生变动可能导致 XPath 不可用。比如复制的 XPath 如下//*[idsanRoot]/main/div[2]/div/div[2]/div[1]/div[2]/a/div[1]优化后可以为//div[1]/div/a/div[classc-single-text-ellipsis]XPath 常用的优化思路包括找到元素附近的独特标识如 id、特定的 class 等尽量使用相对路径而非绝对路径使用contains()等函数处理动态变化的属性值急冻元素调试那些鼠标一移开就消失的元素如下拉菜单、悬浮提示可以用快捷键急冻元素。这是最快、最通用的方法适合调试动态生成的元素。打开工具按下F12打开开发者工具。触发元素用鼠标让那个“顽固”的元素显示出来比如悬停到菜单上此时鼠标保持不动。按下暂停紧接着按一下键盘上的F8键或者在Sources面板点击暂停图标 。开始调试此时整个页面就像被施了定身术动效和 JS 都停了。你可以随意移动鼠标去 Elements 面板里安心检查和修改样式了。原理F8 会触发“暂停脚本执行”相当于把时间停在了元素显示的那一瞬间。元素滑倒最顶端最近遇到一个 RPA 流程自动化的一个场景在页面获取一个数据表的时候数据表每页有 50 行但是由于页面大小的原因能直接展示出来看得见的每次只有 20 条左右其他行需要滑动滚动条才能看见看不见的行使用 find_element 虽然可以定位到但是数据获取时却为空。这个时候就可以每获取一行数据的时候就将当前行往上进行滑动到顶端然后再获取下一行的数据从而实现每次获取的数据行都可见这样就实现了单行自动滑动的功能。ele_order_num table_row.find_element(By.XPATH, f./td[1]/div) self.driver.execute_script(arguments[0].scrollIntoView();, ele_order_num)