0
点赞
收藏
分享

微信扫一扫

爬虫学习(7):通宵两万字xpath教程+实战练习


文章目录

  • ​​一、必看内容!!!​​
  • ​​1)简短介绍​​
  • ​​2)必备知识​​
  • ​​3)为什么我要写这篇文章?​​
  • ​​4)强烈推荐教程专栏​​
  • ​​二、开始使用xpath​​
  • ​​2.1 常见的 HTML 操作​​
  • ​​2.2 常见XML操作​​
  • ​​2.2.1 选择一个元素​​
  • ​​2.2.2 选择文字​​
  • ​​2.3 浏览器使用xpath调试​​
  • ​​2.3.1演示案例一​​
  • ​​三、检查节点是否存在​​
  • ​​3.1 案例一​​
  • ​​3.2 案例二​​
  • ​​四、检查节点的文本是否为空​​
  • ​​4.1 案例一​​
  • ​​4.2 案例二​​
  • ​​五、通过属性查询​​
  • ​​5.1 查找具有特定属性的节点​​
  • ​​5.2 通过属性值的子串匹配来查找节点​​
  • ​​5.3 通过属性值的子字符串匹配查找节点(不区分大小写)​​
  • ​​5.4 通过匹配属性值末尾的子字符串查找节点​​
  • ​​5.5 通过匹配属性值开头的子字符串查找节点​​
  • ​​5.6 查找具有特定属性值的节点​​
  • ​​六、查找包含特定属性的元素​​
  • ​​6.1 查找具有特定属性的所有元素(1)​​
  • ​​6.2 查找具有特定属性值的所有元素(2)​​
  • ​​七、查找包含特定文本的元素​​
  • ​​八、多次强调的语法​​
  • ​​8.1 XPath 轴的语法​​
  • ​​8.2 XPath选取节点语法​​
  • ​​8.3 Xpath谓语​​
  • ​​8.4 Xpath选取未知节点​​
  • ​​8.5 Xpath选取若干路径​​
  • ​​九、获取相对于当前节点的节点​​
  • ​​9.1 基本语法​​
  • ​​9.2 寻找祖先节点​​
  • ​​9.4 寻找兄弟节点​​
  • ​​9.5 寻找祖父节点(2)​​
  • ​​9.6 寻找父母节点​​
  • ​​9.7 寻找当前节点之后的所有节点​​
  • ​​9.8 寻找当前节点之前的所有节点​​
  • ​​9.9 实例一​​
  • ​​9.10 实例二​​
  • ​​十、获取节点数​​
  • ​​10.1 实例一​​
  • ​​10.2 实例二​​
  • ​​十一、根据子节点数选择节点​​
  • ​​11.1 实例一​​
  • ​​11.2 实例二​​
  • ​​十二、选择名称等于或包含某个字符串的节点​​
  • ​​12.1 搜索名称包含 Light 的节点​​
  • ​​12.2 搜索名称以 Ball 结尾的节点​​
  • ​​12.3 搜索名称以 Star 开头的节点​​
  • ​​12.4 搜索名称为 Light、Device 或 Sensor 的节点​​
  • ​​12.5 搜索名称为 light 的节点(不区分大小写)​​
  • ​​12.6 搜索名称为 light 的节点(不区分大小写)​​
  • ​​十三、实战演练(城市二手房价爬取)​​
  • ​​十四、实战演练(虎牙美女直播爬取)​​
  • ​​十三、粉丝福利​​
  • ​​十四、总结​​

一、必看内容!!!

1)简短介绍

XPath 是一种用于寻址 XML 文档部分的语言。它在 XSLT 中使用并且是 XQuery 的子集。这个库也可用于大多数其他编程语言。

2)必备知识

  1. 了解基本的html和xml语法和格式
  2. 没有了,如果你不会html和xml,超过2000收藏,我出一篇html详细教程,至于怎么达到2000赞,看各位粉丝了。

3)为什么我要写这篇文章?

在我前面的几十篇文章,写了上百万字把python的所有基础已经讲得很明白了,不管你是不是小白,跟着学都能学会,同时在我的粉丝群,我还会对教程中的问题进行答疑,所以包教包会的口号,我从来不是吹的。
这里是我的基础教程专栏:​​python全栈基础详细教程专栏系列​​ 当然,如果你对qq机器人制作感兴趣请查看专栏:qq机器人制作详细教程专栏
这两个专栏,我为什么放在一起?第一个专栏是基础教程,第二个专栏是进阶,所以你在不会基础之前,请不要冒然学习机器人制作。
说了半天,我还没说为什么写这一篇的原因,前面的基础我已经差不多写完了,基础不会的自己去看我专栏,上百万字写基础,我已经很用心教大家了。基础过后,我们即将开始学爬虫,因此xpath你不得不掌握。认真跟着我学,多看几天就会了。

4)强烈推荐教程专栏

  1. ​​python全栈基础教程系列​​
  2. ​​qq机器人小白教程系列​​
  3. ​​matlab数学建模小白到精通系列​​
  4. ​​Linux操作系统教程​​
  5. ​​SQL入门到精通教程系列​​

其它专栏,看你自己个人兴趣,这五个专栏我是主打,并是我强烈推荐。

二、开始使用xpath

2.1 常见的 HTML 操作

如果有一段html如下:

<html>
<body>
<a>link</a>
<div class='container' id='divone'>
<p class='common' id='enclosedone'>Element One</p>
<p class='common' id='enclosedtwo'>Element Two</p>
</div>
</body>
</html>

在整个页面中查找具有特定 id 的元素:

//*[@id='divone'] # 返回 <div class='container' id='divone'>

在特定路径中查找具有特定 id 的元素:

/html/body/div/p[@id='enclosedone'] # 返回 <p class='common' id='enclosedone'>Element One</p>

选择具有特定 id 和 class 的元素:

//p[@id='enclosedone' and @class='common'] #返回 <p class='common' id='enclosedone'>Element One</p>

选择特定元素的文本:

//*[@id='enclosedone']/text() # 返回 Element One

2.2 常见XML操作

比如有如下xml:

<r>
<e a="1"/>
<f a="2" b="1">Text 1</f>
<f/>
<g>
<i c="2">Text 2</i>
Text 3
<j>Text 4</j>
</g>
</r>

2.2.1 选择一个元素

用xpath

/r/e

将选择此元素:

<e a="1"/>

2.2.2 选择文字

用xpath:

/r/f/text()

将选择具有此字符串值的文本节点:

"Text 1"

而这个 XPath:

string(/r/f)

返回同样是:

"Text 1"

2.3 浏览器使用xpath调试

步骤如下:

  1. 按F12进入控制台
  2. 按ctrl+F进入搜索框
  3. 将自己写的xpath输入进去,回车看看能不能匹配到

2.3.1演示案例一

以我自己的主页网址为例:

分析:

爬虫学习(7):通宵两万字xpath教程+实战练习_xml


锁定定位为:

user-profile-statistics-num

则xpath写为:

//div[@class="user-profile-statistics-num"]

爬虫学习(7):通宵两万字xpath教程+实战练习_python_02


以上就是一种简单的调试xpath方法,难的我就不介绍了,没必要吧,如果大家觉得有必要,评论区留言,人多我就重新编辑补充。

三、检查节点是否存在

我们使用布尔来检查我们写的xpath是否存在,布尔真是一个很不错的东西。

3.1 案例一

这里我就构造一个xml如下:

<House>
<LivingRoom>
<plant name="rose"/>
</LivingRoom>
<TerraceGarden>
<plant name="passion fruit"/>
<plant name="lily"/>
<plant name="golden duranta"/>
</TerraceGarden>
</House>

使用布尔来判断:

boolean(/House//plant)

输出:

true

说明该路径正确。

3.2 案例二

假设有这样一个xml:

<Animal>
<legs>4</legs>
<eyes>2</eyes>
<horns>2</horns>
<tail>1</tail>
</Animal>

使用布尔判断:

boolean(/Animal/tusks)

输出:

false

说明这个路径是错的。

四、检查节点的文本是否为空

语法:

  • 布尔(路径到节点/文本())
  • 字符串(路径节点)!= ‘’ ”

其他用途:

  • 检查节点是否存在
  • 检查参数是否不是数字 (NaN) 或 0

4.1 案例一

假设我构造这样一个xml:

<Deborah>
<address>Dark world</address>
<master>Babadi</master>
<ID>#0</ID>
<colour>red</colour>
<side>evil</side>
</Deborah>

用布尔判断:

boolean(/Deborah/master/text())

或者用string判断:

string(/Deborah/master) != ''

输出都为:

true

说明文本不为空。

4.2 案例二

假设我构造这样一个xml:

<Dobby>
<address>Hogwartz</address>
<master></master>
<colour>wheatish</colour>
<side>all good</side>
</Dobby>

用布尔判断:

boolean(/Dobby/master/text())

或者用string判断:

string(/Dobby/master) != ''

输出:

false

说明文本为空。

五、通过属性查询

说一些比较常见的语法:

  1. /从当前节点选取直接子节点
  2. //从当前节点选取子孙节点
  3. .选取当前节点
  4. …选取当前节点的父节点
  5. @选取属性
  6. *代表所有

例如:

//title[@lang=’chuan’]

这就是一个 XPath 规则,它就代表选择所有名称为 title,同时属性 lang 的值为 chuan的节点。

5.1 查找具有特定属性的节点

假设有这样一个xml:

<Galaxy>
<name>Milky Way</name>
<CelestialObject name="Earth" type="planet"/>
<CelestialObject name="Sun" type="star"/>
</Galaxy>

路径匹配如下:

/Galaxy/*[@name]

或者:

//*[@name]

输出:

<CelestialObject name="Earth" type="planet" />
<CelestialObject name="Sun" type="star" />

5.2 通过属性值的子串匹配来查找节点

假设有如下例子:

<Galaxy>
<name>Milky Way</name>
<CelestialObject name="Earth" type="planet"/>
<CelestialObject name="Sun" type="star"/>
</Galaxy>

路径:

/Galaxy/*[contains(@name,'Ear')]

值得补充的是,这里的contains函数就是代表包含的意思,这里就是查找Galaxy路径下,所有name属性中含有Ear的节点。
如上,我们也可以如下方式匹配:

//*[contains(@name,'Ear')]

双引号也可以用来代替单引号:

/Galaxy/*[contains(@name, "Ear")]

输出:

<CelestialObject name="Earth" type="planet" />

5.3 通过属性值的子字符串匹配查找节点(不区分大小写)

假设有xml如下:

<Galaxy>
<name>Milky Way</name>
<CelestialObject name="Earth" type="planet"/>
<CelestialObject name="Sun" type="star"/>
</Galaxy>

路径

/Galaxy/*[contains(lower-case(@name),'ear')]

这里又出现了新的东西,加入 lower-case() 函数就是来保证我们可以包括所有的大小写情况。
路径

/Galaxy/*[contains(lower-case(@name),'ear')]

或者

//*[contains(lower-case(@name),'ear')]

或者,使用双引号中的字符串:

//*[contains(lower-case(@name), "ear")]

输出

<CelestialObject name="Earth" type="planet" />

5.4 通过匹配属性值末尾的子字符串查找节点

假设有xml如下:

<Galaxy>
<name>Milky Way</name>
<CelestialObject name="Earth" type="planet"/>
<CelestialObject name="Sun" type="star"/>
</Galaxy>

路径

/Galaxy/*[ends-with(lower-case(@type),'tar')]

补充:这里又出现了新的函数,ends-with就是匹配以xx结尾。

或者

//*[ends-with(lower-case(@type),'tar')]

输出

<CelestialObject name="Sun" type="star" />

5.5 通过匹配属性值开头的子字符串查找节点

假设有这个xml:

<Galaxy>
<name>Milky Way</name>
<CelestialObject name="Earth" type="planet"/>
<CelestialObject name="Sun" type="star"/>
</Galaxy>

路径

/Galaxy/*[starts-with(lower-case(@name),'ear')]

补充:这里又出现了新的函数,starts-with就是匹配以什么开头。

或者

//*[starts-with(lower-case(@name),'ear')]

输出

<CelestialObject name="Earth" type="planet" />

5.6 查找具有特定属性值的节点

假设有这个xml:

<Galaxy>
<name>Milky Way</name>
<CelestialObject name="Earth" type="planet"/>
<CelestialObject name="Sun" type="star"/>
</Galaxy>

路径

/Galaxy/*[@name='Sun']

补充:这里就是我开头说到的,星号代表所有,@用来选取属性

或者

//*[@name='Sun']

输出

<CelestialObject name="Sun" type="star" />

六、查找包含特定属性的元素

6.1 查找具有特定属性的所有元素(1)

假设有xml如下:

<root>
<element foobar="hello_world" />
<element example="this is one!" />
</root>

xpath匹配:

/root/element[@foobar]

返回:

<element foobar="hello_world" />

6.2 查找具有特定属性值的所有元素(2)

假设有xml如下:

<root>
<element foobar="hello_world" />
<element example="this is one!" />
</root>

以下 XPath 表达式:

/root/element[@foobar = 'hello_world']

将返回
​​​<element foobar="hello_world" />​

也可以使用双引号:

/root/element[@foobar="hello_world"]

粉丝群:970353786

七、查找包含特定文本的元素

假设有xml如下:

<root>
<element>hello</element>
<another>
hello
</another>
<example>Hello, <nested> I am an example </nested>.</example>
</root>

以下 XPath 表达式:

//*[text() = 'hello']

将返回​​<element>hello</element>​​​元素,但不返回元素。这是因为该​​<another>​​元素包含hello文本周围的空格。

要同时检索​​<element>and <another>​​,可以使用:

//*[normalize-space(text()) = 'hello']

补充:这里又多了新的函数,normalize-space作用就是去除空白的意思。
要查找包含特定文本的元素,您可以使用该contains函数。以下表达式将返回​​​<example>​​元素:

//example[contains(text(), 'Hello')]

如果要查找跨越多个子/文本节点的文本,则可以使用.代替text()。.指元素及其子元素的整个文本内容。
例如:

//example[. = 'Hello,  I am an example .']

要查看多个文本节点,您可以使用:

//example//text()

这将返回:

  • “hello, ”
  • “I am an example”
  • “.”

为了更清楚地看到一个元素的整个文本内容,可以使用该string函数:

string(//example[1])

要不就

string(//example)

依然返回:

Hello,  I am an example .

八、多次强调的语法

8.1 XPath 轴的语法

现在我们要补充新的东西,又要开始记住了:

ancestor  选取当前节点的所有先辈(父、祖父等)。
ancestor-or-self 选取当前节点的所有先辈(父、祖父等)以及当前节点本身。
attribute 选取当前节点的所有属性。
child 选取当前节点的所有子元素。
descendant 选取当前节点的所有后代元素(子、孙等)。
descendant-or-self 选取当前节点的所有后代元素(子、孙等)以及当前节点本身。
following 选取文档中当前节点的结束标签之后的所有节点。
namespace 选取当前节点的所有命名空间节点。
parent 选取当前节点的父节点。
preceding 选取文档中当前节点的开始标签之前的所有节点。
preceding-sibling 选取当前节点之前的所有同级节点。
self 选取当前节点。

8.2 XPath选取节点语法

为什么我在这里又来强调一下?因为很重要!

nodename  选取此节点的所有子节点。
/ 从根节点选取。
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
. 选取当前节点。
.. 选取当前节点的父节点。
@ 选取属性。

在下面的表格中,列出了一些路径表达式以及表达式的结果:

bookstore 选取 bookstore 元素的所有子节点。
/bookstore 选取根元素 bookstore。 注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!

bookstore/book 选取属于 bookstore 的子元素的所有 book 元素。

//book 选取所有 book 子元素,而不管它们在文档中的位置。

bookstore//book 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。

//@lang 选取名为 lang 的所有属性。

8.3 Xpath谓语

谓语用来查找某个特定的节点或者包含某个指定的值的节点。谓语被嵌在方括号中。
看一些例子就知道了:

路径表达式             结果
/bookstore/book[1] 选取属于 bookstore 子元素的第一个 book 元素。

/bookstore/book[last()] 选取属于 bookstore 子元素的最后一个 book 元素。

/bookstore/book[last()-1] 选取属于 bookstore 子元素的倒数第二个 book 元素。

/bookstore/book[position()<3] 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。

//title[@lang] 选取所有拥有名为 lang 的属性的 title 元素。

//title[@lang='eng'] 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。

/bookstore/book[price>35.00] 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。

/bookstore/book[price>35.00]/title 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。

8.4 Xpath选取未知节点

XPath 通配符可用来选取未知的 XML 元素。

通配符   描述
* 匹配任何元素节点。
@* 匹配任何属性节点。
node() 匹配任何类型的节点。

在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:

路径表达式     结果
/bookstore/* 选取 bookstore 元素的所有子元素。
//* 选取文档中的所有元素。
//title[@*] 选取所有带有属性的 title 元素。

8.5 Xpath选取若干路径

通过在路径表达式中使用“|”运算符,您可以选取若干个路径。

在下面的表格中,列出了一些路径表达式,以及这些表达式的结果:

路径表达式    结果
//book/title | //book/price 选取 book 元素的所有 title 和 price 元素。
//title | //price 选取文档中的所有 title 和 price 元素。
/bookstore/book/title | //price 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。

九、获取相对于当前节点的节点

假设我们有xml如下:

<?xml version="1.0" encoding="ISO-8859-1"?>

<bookstore>

<book>
<title lang="eng">Harry Potter</title>
<price>29.99</price>
</book>

<book>
<title lang="eng">Learning XML</title>
<price>39.95</price>
</book>

</bookstore>

9.1 基本语法

爬虫学习(7):通宵两万字xpath教程+实战练习_爬虫_03


其实这些内容,大可不必都掌握,但是你一定要知道,你想用的时候,再来本文查一下会用就行。

这是相关实例:

爬虫学习(7):通宵两万字xpath教程+实战练习_爬虫_04


问题是:这里提到的祖先,孩子,兄弟,父母节点,大家知道吗?如果你会html的话,你应该知道。超过2000赞我可以出一篇html的教程,本篇我就暂时默认大家知道了。

9.2 寻找祖先节点

假设有xml如下:(这里已经很形象说明了祖先,孩子,兄弟,父母节点的关系了,仔细看看)

<GrandFather name="Bardock" gender="male" spouse="Gine">
<Dad name="Goku" gender="male" spouse="Chi Chi">
<Me name="Gohan" gender="male"/>
<brother name="Goten" gender="male"/>
</Dad>
</GrandFather>

路径

//Me/ancestor::node()

输出:

<GrandFather name="Bardock" gender="male" spouse="Gine">
<Dad name="Goku" gender="male" spouse="Chi Chi">
<Me name="Gohan" gender="male" />
<brother name="Goten" gender="male" />
</Dad>
</GrandFather>
<Dad name="Goku" gender="male" spouse="Chi Chi">
<Me name="Gohan" gender="male" />
<brother name="Goten" gender="male" />
</Dad>

9.4 寻找兄弟节点

假设有xml如下:

<GrandFather name="Bardock" gender="male" spouse="Gine">
<Dad name="Goku" gender="male" spouse="Chi Chi">
<brother name="Goten" gender="male" />
<Me name="Gohan" gender="male" />
<brother name="Goten" gender="male" />
</Dad>
</GrandFather>

路径:

//Me/following-sibling::brother

输出:

<brother name="Goten" gender="male" />

9.5 寻找祖父节点(2)

假设有xml如下:

<GrandFather name="Bardock" gender="male" spouse="Gine">
<Dad name="Goku" gender="male" spouse="Chi Chi">
<Me name="Gohan" gender="male" />
<brother name="Goten" gender="male" />
</Dad>
</GrandFather>

路径

//Me/ancestor::GrandFather

或者

//Me/parent::node()/parent::node()

输出:

<GrandFather name="Bardock" gender="male" spouse="Gine">
<Dad name="Goku" gender="male" spouse="Chi Chi">
<Me name="Gohan" gender="male" />
<brother name="Goten" gender="male" />
</Dad>
</GrandFather>

9.6 寻找父母节点

还是假设xml如下:

<GrandFather name="Bardock" gender="male" spouse="Gine">
<Dad name="Goku" gender="male" spouse="Chi Chi">
<Me name="Gohan" gender="male"/>
<brother name="Goten" gender="male"/>
</Dad>
</GrandFather>

路径

//Me/ancestor::Dad

或者

//Me/parent::node()

输出:

<Dad name="Goku" gender="male" spouse="Chi Chi">
<Me name="Gohan" gender="male" />
<brother name="Goten" gender="male" />
</Dad>

9.7 寻找当前节点之后的所有节点

假设有xml如下:

<Dashavatar>
<Avatar name="Matsya"/>
<Avatar name="Kurma"/>
<Avatar name="Varaha"/>
<Avatar name="Narasimha"/>
<Avatar name="Vamana"/>
<Avatar name="Balabhadra"/>
<Avatar name="Parashurama"/>
<Avatar name="Rama"/>
<Avatar name="Krishna"/>
<Avatar name="Kalki"/>
</Dashavatar>

路径

//Avatar[@name='Parashurama']/following-sibling::node()

输出:

<Avatar name="Rama" />
<Avatar name="Krishna" />
<Avatar name="Kalki" />

9.8 寻找当前节点之前的所有节点

假设有xml如下:

<Dashavatar>
<Avatar name="Matsya"/>
<Avatar name="Kurma"/>
<Avatar name="Varaha"/>
<Avatar name="Narasimha"/>
<Avatar name="Vamana"/>
<Avatar name="Balabhadra"/>
<Avatar name="Parashurama"/>
<Avatar name="Rama"/>
<Avatar name="Krishna"/>
<Avatar name="Kalki"/>
</Dashavatar>

路径

//Avatar[@name='Parashurama']/preceding-sibling::node()

输出:

<Avatar name="Matsya"/>
<Avatar name="Kurma"/>
<Avatar name="Varaha"/>
<Avatar name="Narasimha"/>
<Avatar name="Vamana"/>
<Avatar name="Balabhadra"/>

9.9 实例一

获取 House 中的所有房间名为 Room 的孩子。
假设有xml如下:

<House>
<numRooms>4</numRooms>
<Room name="living"/>
<Room name="master bedroom"/>
<Room name="kids' bedroom"/>
<Room name="kitchen"/>
</House>

路径

/House/child::Room

或者

/House/*[local-name()='Room']

输出:

<Room name="living" />
<Room name="master bedroom" />
<Room name="kids' bedroom" />
<Room name="kitchen" />

9.10 实例二

获得 House 中的所有房间(不考虑位置)。
假设有xml如下:

<House>
<numRooms>4</numRooms>
<Floor number="1">
<Room name="living"/>
<Room name="kitchen"/>
</Floor>
<Floor number="2">
<Room name="master bedroom"/>
<Room name="kids' bedroom"/>
</Floor>
</House>

路径

/House/descendant::Room

输出

<Room name="living" />
<Room name="kitchen" />
<Room name="master bedroom" />
<Room name="kids' bedroom" />

十、获取节点数

我们主要用到count函数,实战中我们来感悟。

10.1 实例一

假设有xml如下:

<Goku>
<child name="Gohan"/>
<child name="Goten"/>
</Goku>

路径

count(/Goku/child)

输出

2.0

10.2 实例二

假设有如下xml

<House>
<LivingRoom>
<plant name="rose"/>
</LivingRoom>
<TerraceGarden>
<plant name="passion fruit"/>
<plant name="lily"/>
<plant name="golden duranta"/>
</TerraceGarden>
</House>

路径

count(/House//plant)

输出

4.0

十一、根据子节点数选择节点

11.1 实例一

假设有xml如下:

<Students>
<Student>
<Name>
<First>Ashley</First>
<Last>Smith</Last>
</Name>
<Grades>
<Exam1>A</Exam1>
<Exam2>B</Exam2>
<Final>A</Final>
</Grades>
</Student>
<Student>
<Name>
<First>Bill</First>
<Last>Edwards</Last>
</Name>
<Grades>
<Exam1>A</Exam1>
</Grades>
</Student>
</Students>

选择至少记录了 2 个成绩的所有学生

//Student[count(./Grades/*) > 1]

输出

<Student>
<Name>
<First>Ashley</First>
<Last>Smith</Last>
</Name>
<Grades>
<Exam1>A</Exam1>
<Exam2>B</Exam2>
<Final>A</Final>
</Grades>
</Student>

11.2 实例二

假设有xml如下:

<Students>
<Student>
<Name>
<First>Ashley</First>
<Last>Smith</Last>
</Name>
<Grades>
<Exam1>A</Exam1>
<Exam2>B</Exam2>
<Final>A</Final>
</Grades>
</Student>
<Student>
<Name>
<First>Bill</First>
<Last>Edwards</Last>
</Name>
<Grades>
<Exam1>A</Exam1>
</Grades>
</Student>
</Students>

选择所有记录了 Exam2 分数的学生

//Student[./Grades/Exam2]

或者

//Student[.//Exam2]

输出

<Student>
<Name>
<First>Ashley</First>
<Last>Smith</Last>
</Name>
<Grades>
<Exam1>A</Exam1>
<Exam2>B</Exam2>
<Final>A</Final>
</Grades>
</Student>

十二、选择名称等于或包含某个字符串的节点

语法如下:
1.在特定节点内:

{path-to-parent}/name()='搜索字符串']

2.文档中的任何位置:

//*[name()='搜索字符串']

12.1 搜索名称包含 Light 的节点

假设有xml如下:

<Data>
<BioLight>
<name>Firefly</name>
<model>Insect</model>
</BioLight>
<ArtificialLight>
<name>Fire</name>
<model>Natural element</model>
<source>flint</source>
</ArtificialLight>
<SolarLight>
<name>Sun</name>
<model>Star</model>
<source>helium</source>
</SolarLight>
</Data>

路径

/Data/*[contains(local-name(),"Light")]

或者

//*[contains(local-name(),"Light")]

输出:

<BioLight>
<name>Firefly</name>
<model>Insect</model>
</BioLight>
<ArtificialLight>
<name>Fire</name>
<model>Natural element</model>
<source>flint</source>
</ArtificialLight>
<SolarLight>
<name>Sun</name>
<model>Star</model>
<source>helium</source>
</SolarLight>

12.2 搜索名称以 Ball 结尾的节点

假设xml如下:

<College>
<FootBall>
<Members>20</Members>
<Coach>Archie Theron</Coach>
<Name>Wild cats</Name>
<StarPlayer>David Perry</StarPlayer>
</FootBall>
<VolleyBall>
<Members>24</Members>
<Coach>Tim Jose</Coach>
<Name>Avengers</Name>
<StarPlayer>Lindsay Rowen</StarPlayer>
</VolleyBall>
<FoosBall>
<Members>22</Members>
<Coach>Rahul Mehra</Coach>
<Name>Playerz</Name>
<StarPlayer>Amanda Ren</StarPlayer>
</FoosBall>
</College>

路径

/College/*[ends-with(local-name(),"Ball")]

或者

//*[ends-with(local-name(),"Ball")]

输出:

<FootBall>
<Members>20</Members>
<Coach>Archie Theron</Coach>
<Name>Wild cats</Name>
<StarPlayer>David Perry</StarPlayer>
</FootBall>
<VolleyBall>
<Members>24</Members>
<Coach>Tim Jose</Coach>
<Name>Avengers</Name>
<StarPlayer>Lindsay Rowen</StarPlayer>
</VolleyBall>
<FoosBall>
<Members>22</Members>
<Coach>Rahul Mehra</Coach>
<Name>Playerz</Name>
<StarPlayer>Amanda Ren</StarPlayer>
</FoosBall>

12.3 搜索名称以 Star 开头的节点

假设xml如下:

<College>
<FootBall>
<Members>20</Members>
<Coach>Archie Theron</Coach>
<Name>Wild cats</Name>
<StarFootballer>David Perry</StarFootballer>
</FootBall>
<Academics>
<Members>100</Members>
<Teacher>Tim Jose</Teacher>
<Class>VII</Class>
<StarPerformer>Lindsay Rowen</StarPerformer>
</Academics>
</College>

路径

/College/*/*[starts-with(local-name(),"Star")]

或者

//*[starts-with(local-name(),"Star")]

输出

<StarFootballer>David Perry</StarFootballer>
<StarPerformer>Lindsay Rowen</StarPerformer>

12.4 搜索名称为 Light、Device 或 Sensor 的节点

假设xml如下:

<Galaxy>
<Light>sun</Light>
<Device>satellite</Device>
<Sensor>human</Sensor>
<Name>Milky Way</Name>
</Galaxy>

路径

/Galaxy/*[local-name()='Light' or local-name()='Device' or local-name()='Sensor']

说白了就是多了几个or而已。
或者

//*[local-name()='Light' or local-name()='Device' or local-name()='Sensor']

输出

<Light>sun</Light>
<Device>satellite</Device>
<Sensor>human</Sensor>

12.5 搜索名称为 light 的节点(不区分大小写)

假设xml如下:

<Galaxy>
<Light>sun</Light>
<Device>satellite</Device>
<Sensor>human</Sensor>
<Name>Milky Way</Name>
</Galaxy>

路径

/Galaxy/*[lower-case(local-name())="light"]

或者

//*[lower-case(local-name())="light"]

输出

<Light>sun</Light>

12.6 搜索名称为 light 的节点(不区分大小写)

假设xml如下:

<Galaxy>
<Light>sun</Light>
<Device>satellite</Device>
<Sensor>human</Sensor>
<Name>Milky Way</Name>
</Galaxy>

路径

/Galaxy/*[lower-case(local-name())="light"]

或者

//*[lower-case(local-name())="light"]

输出

<Light>sun</Light>

十三、实战演练(城市二手房价爬取)

有部分内容比如requests模块和lxml模块,我还没有细讲,如果你有一点基础,能够看懂。后续我会讲解模块的具体使用方法,在此如果你实在看不懂,可以三连支持我文章一下,有动力我才会继续出新文讲解。

分析如下:

(第一步找总体)

爬虫学习(7):通宵两万字xpath教程+实战练习_matlab_05


第二步看单个:

爬虫学习(7):通宵两万字xpath教程+实战练习_xml_06


爬虫学习(7):通宵两万字xpath教程+实战练习_搜索_07

(找规律)可遍历:

爬虫学习(7):通宵两万字xpath教程+实战练习_搜索_08

#coding=utf-8
"""
作者:川川
时间:2021/6/26
"""
from lxml import etree
import requests

if __name__ == '__main__':
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36'
}
url = 'https://xa.58.com/ershoufang/?q=%E4%B8%8A%E6%B5%B7'
page_text = requests.get(url=url,headers=headers).text
tree = etree.HTML(page_text)
div_list = tree.xpath('//section[@class="list"]/div')
print(div_list)
fp = open('./上海二手房.txt','w',encoding='utf-8')
for div in div_list:
title = div.xpath('.//div[@class="property-content-title"]/h3/text()')[0]
print(title)
price=str('总价格为'+div.xpath('.//div[@class="property-price"]/p/span[@class="property-price-total-num"]/text()')[0])+'万元'
print(price)
fp.write(title+'\t'+price+'\n'+'\n')

结果:

爬虫学习(7):通宵两万字xpath教程+实战练习_xml_09


但是呢?这样会不会还是显得麻烦了呢?难不成每个城市都写一份这样的代码吗?不是的,请看如下分析:

上面这部分代码是爬取的上海的二手房价

然而网址却是这样的:

爬虫学习(7):通宵两万字xpath教程+实战练习_搜索_10


这样很容易想到,如果切换城市,仅仅只需要把上海换成别的城市就可以了。经过我分析,换个城市,网页结构并不用变化,所以唯一变动就是这个城市。

因此修改后代码:

#coding=utf-8
"""
作者:川川
时间:2021/10/10
"""
from lxml import etree
import requests

if __name__ == '__main__':
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36'
}
c=input('这里是二手房价爬取,请输入你想要爬取的城市:\n')
url = 'https://xa.58.com/ershoufang/?q=%s'%c
page_text = requests.get(url=url,headers=headers).text
tree = etree.HTML(page_text)
div_list = tree.xpath('//section[@class="list"]/div')
print(div_list)
fp = open('./上海二手房.txt','w',encoding='utf-8')
for div in div_list:
title = div.xpath('.//div[@class="property-content-title"]/h3/text()')[0]
print(title)
price=str('总价格为'+div.xpath('.//div[@class="property-price"]/p/span[@class="property-price-total-num"]/text()')[0])+'万元'
print(price)
fp.write(title+'\t'+price+'\n'+'\n')

效果如下:

想爬取哪个城市就输入哪个城市即可

爬虫学习(7):通宵两万字xpath教程+实战练习_matlab_11


结果如下:

爬虫学习(7):通宵两万字xpath教程+实战练习_搜索_12


可是就算这样能爬取到每个城市的二手价,每次创建的文件名字没有变动啊,所以还得继续修改一点点,需要每次爬取就自动创建对应城市的文档,所以再次修改后如下:

#coding=utf-8
"""
作者:川川
时间:2021/10/10
"""
from lxml import etree
import requests

if __name__ == '__main__':
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36'
}
c=input('这里是二手房价爬取,请输入你想要爬取的城市:\n')
url = 'https://xa.58.com/ershoufang/?q=%s'%c
page_text = requests.get(url=url,headers=headers).text
tree = etree.HTML(page_text)
div_list = tree.xpath('//section[@class="list"]/div')
print(div_list)
wen=c+'二手房价.txt'
fp = open(wen,'w',encoding='utf-8')
for div in div_list:
title = div.xpath('.//div[@class="property-content-title"]/h3/text()')[0]
print(title)
price=str('总价格为'+div.xpath('.//div[@class="property-price"]/p/span[@class="property-price-total-num"]/text()')[0])+'万元'
print(price)
fp.write(title+'\t'+price+'\n'+'\n')

输入城市:

爬虫学习(7):通宵两万字xpath教程+实战练习_搜索_13


结果:

爬虫学习(7):通宵两万字xpath教程+实战练习_python_14


运行如下:

爬虫学习(7):通宵两万字xpath教程+实战练习_python_15

十四、实战演练(虎牙美女直播爬取)

可以自己去跑一下,我就不分析了,这里我们用到了requests,但是我还没讲这个模块,可以来体验一下。
注意,你需要改如下路径:

path='E://photo//'

完整代码

import requests
from lxml import etree
import time
url='https://www.huya.com/g/4079/'
header={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36'
}
response=requests.get(url=url,headers=header)#发送请求
# print(response.text)
data=etree.HTML(response.text)#转化为html格式
image_url=data.xpath('//a//img//@data-original')
image_name=data.xpath('//a//img[@class="pic"]//@alt')
# print(image_url)
path='E://photo//'
for ur,name in zip(image_url,image_name):
url=ur.replace('?imageview/4/0/w/338/h/190/blur/1','')
title=name+'.jpg'
response = requests.get(url=url, headers=header) # 在此发送新的请求
with open(path+title,'wb') as f:
f.write(response.content)
print("下载成功" + name)
time.sleep(2)

我最近才学到xpath吧,所以就用的xpath,跟着我学爬虫,一定会发现很简单,很有趣。

部分结果:

爬虫学习(7):通宵两万字xpath教程+实战练习_python_16

十三、粉丝福利

前期已经送出很多基础书和数据分析书籍,本次送人工智能的书籍如下:

爬虫学习(7):通宵两万字xpath教程+实战练习_python_17

送书仅在于个人心意,以此鼓励大家学习,只随机送两本

十四、总结

这位大佬也写了一篇xpath:​​十五分钟掌握python爬虫XPath库​​ 感兴趣可以对照我的看看。
python全栈基础专栏我已经讲完大部分基础,现在我们进军爬虫,本篇内容希望大家一定掌握。超过2000收藏,我补一篇html网页基础。写了我周末两个通宵,希望大家这次支持,谢谢。至于我送书活动,货真价实,仅代表个人心意,鼓励大家学习。
公众号 发送:xpath 即可领取本篇文章的电子版。


举报

相关推荐

0 条评论