在学习本教程之前,您需要具备八爪鱼基础操作和XPath相关知识,如果还未掌握,请先学习以下课程。

自定义模式入门:https://www.bazhuayu.com/tutorial8/xsrm/81zdyrm

XPath 系统学习与实例:https://www.bazhuayu.com/tutorial8/81xpath

 

一、相对XPath

相对Xpath,即相对于循环框的Xpath,有两个典型特征:跟随循环联动;与循环框的XPath合并成一条完整的定位XPath。

有两种常见应用场景:提取循环内的数据;提取循环外的数据。

以下将具体说明。

 

二、使用相对XPath,提取循环内的数据

 

示例网址:http://deal.ggzy.gov.cn/ds/deal/dealList.jsp

需求:采集这个网址列表的 项目标题,省份,来源平台,业务类型,时间等字段

 

 

Step1:按照之前学过的列表采集的方法,创建循环框。或者使用自动识别,自动创建循环框。

这里使用自动识别,可以看到,八爪鱼自动创建了循环,提取了列表数据。

 

 

Step2:八爪鱼自动创建的循环列表,会为我们自动生成循环框的XPath和相对XPath。我们来看一下:

 

循环框的XPath

循环框的XPath为://div[@class="publicont"]。通过这条XPath,找到20个列表项,对应着页面中的20个数据列表。

 

 

相对XPath

每个循环项中,都有 项目标题,省份,来源平台,业务类型,时间等字段。查看每个字段的定位XPath,查看路径见下图。

可以看到,每个字段使用的都是相对XPath,以 项目标题 和 时间 这2个字段为例。

【项目标题】字段的相对XPath:/DIV[1]/H4[1]/A[1]

【时间】字段的相对XPath:/descendant-or-self::SPAN[@class="span_o"]

 

 

循环框的XPath和相对XPath的关系

循环框的XPath:定位到列表项。

相对XPath:字段相对于循环框(列表项)的位置。

循环框的XPath + 相对XPath = 每一个循环项中字段的XPath

 

 

正是因为对XPath的存在,我们能够实现循环和字段的联动采集:

循环框当前项为列表1 - 定位到列表1中的字段(项目标题,省份,来源平台,业务类型,时间)

循环框当前项切换为列表2 - 定位到列表2中的字段

循环框当前项切换为列表3 - 定位到列表3中的字段

循环框当前项切换为列表4 - 定位到列表4中的字段

......

最后1个列表 - 采集最后1个列表中的字段

 

 

如果没有相对XPath会怎样?

无法实现循环项与字段联动的效果,只能固定采集到第一个循环项中的字段。

 

根据需求,修改相对XPath

通过上面的学习,我们已经知道了相对XPath的原理。在大多数情况下,八爪鱼会为我们自动生成相对XPath,正确提取到循环项中的字段。此时无需修改。少数情况下,自动生成的相对XPath,无法适配所有网页,存在部分字段提取不到,或者字段错位的情况,此时就需要我们自行修改。

以下看1个实例。

 

修改相对XPath实例

上文中的网站,【时间】这个字段的相对XPath为:/descendant-or-self::SPAN[@class="span_o"],比较长且难以理解。我们可以写一条短的、好理解的。如何修改?

 

Step1:将循环框的 XPath //div[@class="publicont"] ,复制到火狐浏览器中。

 

 

Step2:然后找到【时间】字段对应的网页源码。可以看到它是一个span标签,有一个class属性,属性值是 pan_o,根据这个特性可以写一条定位XPath://div[@class="publicont"]//span[@class="span_o"]

可以看到,网页上所有【时间】字段都被虚线框圈起来了,说明都被定位到了。

 

 

Step3:根据前面学到的知识,循环框的XPath是 //div[@class="publicont"] ,【时间】字段的相对XPath 则是://span[@class="span_o"] 。

//span[@class="span_o"] ,复制粘贴到八爪鱼中【时间】字段的相对XPath输入框中。然后点击【应用】保存。

 

 

这样我们就完成了【时间】字段的相对Xpath的改写。启动【本地采集】,看一下采集结果,循环项中的数据都被正常采集下来了。

 

 

 

三、使用相对XPath,提取循环外的数据

 

示例网址:http://zu.wuhan.fang.com/cities.aspx

需求:采集整个城市列表和其所对应的省份。

 

Step1:先按列表数据采集的方法,选中所有【城市】,创建一个循环。

同时,我们还想将每一个城市对应的省份采集下来。所以选中第1个省份,在操作提示框中选择【采集该元素的文本】。

手动执行一下规则,我们发现2个问题:

① 不管是哪个城市,采集下来的省份都是【安徽】。如选择福州,提取到的还是安徽省,而不是福建省。

② 直辖市的4个城市都没有采集下来。

 

 

Step2:先来解决没定位到直辖市的4个城市的问题。

将循环列表的 XPath //HTML/BODY[1]/DIV[4]/DIV[4]/UL[1]/LI[position()>1]/A  ,复制到火狐浏览器中。可以看到,除第一行4个直辖市外,其他城市都定位到了。

XPath课程 中,我们讲过position函数,它可以限制定位到的列表数。这里的[position()>1]就将第一个li列表(4个直辖市的列表)筛掉了。我们想将4个直辖市也定位到,去掉[position()>1]即可://HTML/BODY[1]/DIV[4]/DIV[4]/UL[1]/LI/A。

 

 

然后在八爪鱼中对循环框做相应修改。修改后可以看到,循环列表里面包含了第一行的4个直辖市,如下图。

 

 

Step3:再来解决始终定位到【安徽】这个省份的问题。

在提取【省份】时,八爪鱼自动生成的定位XPath为 //DIV[@class="outCont"]/UL[1]/LI[2]/STRONG[1]。将这条XPath复制到火狐浏览器中,可以看到只能固定提取【安徽】,不能与循环列表中的城市联动。所以当提取的城市发生变化时,省份不会随着城市的变化而变化。

 

 

我们来观察省份和城市对应的源码。发现省份均为strong标签,城市均为a标签。直辖市为b标签,直辖市的4个城市均为a标签。

strong标签和b标签与a标签为同级标签,并都在a标签之前。

 

 

再回忆一下,我们是以城市,也就是a标签来建立循环列表的。如果需要省份与城市一一对应起来(包括直辖市与具体城市一一对应起来),需要用到上面学到的相对XPath知识。在这个示例中:

循环框的XPath:定位到列表项(所有城市)
相对XPath:字段相对于循环框的位置(省份相对于城市的位置)
循环框的XPath + 相对XPath = 每一个循环项外字段的XPath(省份的定位XPath

 

根据源码特征,写出根据城市列表定位到省份(含直辖市)的定位XPath。城市列表定位XPath为/HTML/BODY[1]/DIV[4]/DIV[4]/UL[1]/LI/A,要定位a标签之前的同级元素,需用到preceding-sibling这个函数,在 XPath课程 中有详细讲到。故:

 

(1) 【省份】相对于【城市】的XPath:

/HTML/BODY[1]/DIV[4]/DIV[4]/UL[1]/LI/A/preceding-sibling::strong[1]

(2) 【直辖市】相对于【城市】的XPath:

/HTML/BODY[1]/DIV[4]/DIV[4]/UL[1]/LI/A/preceding-sibling::b[1]

 

我们还可以进一步将这两条合并成一条,合并之后的结果是:

/HTML/BODY[1]/DIV[4]/DIV[4]/UL[1]/LI/A/preceding-sibling::*[last()]

 

* 代表任意标签的意思,也就是strong标签或者b标签都可以用*代替

last() 的意思是最后一个位置的标签。

整条XPath解释起来的意思就是,取城市列表(a标签)之前的最后一个同级标签(strong标签或者b标签),这样就适用于2种情况。

 

根据上面讲的知识,/HTML/BODY[1]/DIV[4]/DIV[4]/UL[1]/LI/A 为循环框XPath,/preceding-sibling::*[last()]为相对XPath。

 

在火狐里面验证下,定位到了所有省份(含直辖市)。

 

 

最后,在八爪鱼中做相应修改。

点击字段【省份】,选择【自定义定位元素方式】,勾选【相对于当前循环里的Xpath】,输入我们写好的相对XPath:/preceding-sibling::*[last()]  ,最后点击【应用】保存。

 

 

手动执行规则,循环列表分别切换成省份和直辖市,检查验证,已能联动,正确提取到相应数据。

 

直辖市-上海,直辖市-天津:

 

 

福建省-泉州,福建省-厦门:

 

 

启动【本地采集】,看一下采集结果,可以看到,省份(含直辖市)与城市一一对应。

 

 

特别注意:

当要用相对XPath采集循环外的数据时,需特别注意,只适用于提取循环外面单个的数据(只有一个数据)。不适用于循环外也是列表,有多行数据的情况。

 

实例1:循环外为单个数据,适用

网址:https://www.24zbw.com/playback/nba/

日期星期是写在一起的单个数据,以所有的赛事,也就是li标签做循环,然后提取循环外,li标签前面一个的h6标签,下面一个层级的time标签即可。

 

 

实例2:循环外为也为列表,多个数据,不适用

网址:http://www.ccgp-tianjin.gov.cn/portal/documentView.do?method=view&id=144080253&ver=2

上下2个表格,下面表格的数据和上面表格的数据是按顺序一一对应。要把上下2个表并且按对应关系采集下来,是做不到的。因为上面一个表采集需要创建一个循环,下面一个表采集也需要创建一个循环,就会有2个循环,但是循环与循环之间是没办法产生联动效果,八爪鱼暂时还不支持循环与循环的联动。