使用scrapy中xpath选择器的一个坑点

情景如下:

一个网页下有一个ul,这个ur下有125个li标签,每个li标签下有我们想要的 url 字段(每个 url 是唯一的)和 price 字段,我们现在要访问每个li下的url并在生成的请求中携带该请求的price字段

毫无疑问,这里是要用到scrapy项目内meta传参的,那么我们思路可能是这样:

1)start_requests访问初始网页

2)定义一个 parse 方法,通过xpath选择器获取所有的li标签,遍历每个 li 标签,获取 url 和 price 字段,生成目标地址为 url 的 scrapy.Request对 象,将 price 打包到 Request 对象的 meta 中,分别yield新地址为 url 的 scrapy.Request 对象

3)对新的 response 进行处理

现在问题出在第2)步骤中:

我们可能发现遍历 li 标签获取的 url 和 price 对象都是一样的,如

In [20]: url_item = response.xpath('//ul[contains(@class, "house-list")]/li')

In [21]: for item in url_item:
    ...:     url = item.xpath('//h2[@class="title"]/a/@href').extract_first()
    ...:     print(url)
    ...: 
https://bj.58.com/ershoufang/37822311633030x.shtml
https://bj.58.com/ershoufang/37822311633030x.shtml
https://bj.58.com/ershoufang/37822311633030x.shtml
......省略122个相同url
View Code

可以猜想scrapy中scrapy.selector.unified.SelectorList对象在进行遍历时对子元素操作时,事实上并不是对子元素的操作,而是仍然在对这个SelectorList对象进行操作

In [24]: for item in url_item:
    ...:     url = url_item.xpath('//h2[@class="title"]/a/@href').extract_first()
    ...:     print(url)
    ...: 
https://bj.58.com/ershoufang/37822311633030x.shtml
https://bj.58.com/ershoufang/37822311633030x.shtml
https://bj.58.com/ershoufang/37822311633030x.shtml
......省略122个相同url
View Code

以上两种结果完全一致,为了证明我的猜想,我这次在遍历时不用extract_first(),而使用extract(),结果如下:

In [34]: for item in url_item:
    ...:     urls = url_item[2].xpath('//h2[@class="title"]/a/@href').extract()
    ...:     print(urls)
    ...: 
['https://bj.58.com/ershoufang/37822311633030x.shtml', 'https://bj.58.com/ershoufang/37834554715403x.shtml', 'https://bj.58.com/ershoufang/37769196098828x.shtml',
.....省略121个不同url
'https://bj.58.com/ershoufang/37398992001320x.shtml']
......省略和上面相同的123个列表
['https://bj.58.com/ershoufang/37822311633030x.shtml', 'https://bj.58.com/ershoufang/37834554715403x.shtml', 'https://bj.58.com/ershoufang/37769196098828x.shtml',
.....
'https://bj.58.com/ershoufang/37398992001320x.shtml']
View Code

分析:遍历的每个item里面只有自己唯一的url,即使extract(),打印的也应该是含自己唯一的url的列表,并且每个item打印的url列表各不相同,

但实际每个item打印的列表包含了所有的url,且每个item打印的url列表完全一致,并且每个item中这个一致的url列表与item的父元素url_item的url列表一致:

In [37]: response.xpath('//ul[contains(@class, "house-list")]/li//h2[@class="title"]/a/@href').extract()
Out[37]: 
['https://bj.58.com/ershoufang/37822311633030x.shtml',
 'https://bj.58.com/ershoufang/37834554715403x.shtml',
 'https://bj.58.com/ershoufang/37769196098828x.shtml',
......省略121个不同url
 'https://bj.58.com/ershoufang/37398992001320x.shtml']
View Code

结果证实了我的猜想,这也就是我说的scapy中xpath选择器的坑,那么还是面对我最开始提出的情景,该如何解决呢?

在这里提供两种思路:

1)不要使用scrapy中xpath选择器的链式解析,在拿到scrapy.selector.unified.SelectorList对象后,不要通过遍历直接链式解析,直接提取出html文本列表,并对这个列表进行遍历,对每个子元素再生成 scrapy.selector.unified.Selector 对象,然后通过 xpath 提取数据,如下

In [52]: url_item = response.xpath('//ul[contains(@class, "house-list")]/li')

In [53]: items = url_item.extract()

In [55]: for item in items:
    ...:     sele_obj = scrapy.Selector(text=item)
    ...:     url = sele_obj.xpath('//h2[@class="title"]/a/@href').extract_first()
    ...:     print(url)
    ...: 
https://bj.58.com/ershoufang/37822311633030x.shtml
https://bj.58.com/ershoufang/37834554715403x.shtml
https://bj.58.com/ershoufang/37769196098828x.shtml
......省略121个不同url
'https://bj.58.com/ershoufang/37398992001320x.shtml'
方法一

成功拿到每个 li 下的url

2)使用scrapy中xpath选择器的链式解析,在拿到scrapy.selector.unified.SelectorList对象后,开始改用 css 选择器解析:

In [60]: url_item = response.css('ul[class *= "house-list"]>li')

In [61]: for item in url_item:
    ...:     url = item.css('h2.title>a::attr(href)').extract_first()
    ...:     print(url)
    ...: 
https://bj.58.com/ershoufang/37822311633030x.shtml
https://bj.58.com/ershoufang/37834554715403x.shtml
https://bj.58.com/ershoufang/37769196098828x.shtml
......省略121个不同url
https://bj.58.com/ershoufang/37398992001320x.shtml
方法二

猜你喜欢

转载自www.cnblogs.com/tarantino/p/10744824.html