摘要:对提取数据的三种方法,xpath,css,正则进行介绍。

在这一节中我们将学习如何从网页数据中匹配数据,同时这一节也是对Scrapy文档Selectors

的学习。

数据提取的方法1.jpg

抛开爬虫所有繁琐的流程,我们单单从匹配数据出发来简单学习使用xpath/css/re的一些简单方法,然后再发散思维。

另外本节内容相对来说较为枯燥,主要是为了能让大家掌握一些基本的xpath和css的一些方法。具体大家还是再看看我的另外两篇笔记,现在暂时放在简书上,最近我会将其迁移过来。

传送门:

1 构造选择器(Constructing selectors)

我们预先构造一个选择器(Selectors)对象,然后在这个Selectors基础上进行操作。这个方法同样适用在我们单独编写的爬虫中,这样可以将Scrapy中的这些匹配方法迅速应用。另外我们也可以在Scrapy shell中进行匹配练习。

1.1 从文本(text)中构建选择器(selectors)

>>> from scrapy.selector import Selector
>>> body = '<html><body><span>good</span></body></html>' 
>>> Selector(text=body).xpath('//span/text()').extract()
[u'good']

1.2 从响应(response)中构建选择器(selectors)

>>> from scrapy.http import HtmlResponse
>>> body = '<html><body><span>good</span></body></html>'.encode("utf-8")
>>> response = HtmlResponse(url='http://example.com', body=body)
>>> Selector(response=response).xpath('//span/text()').extract()
[u'good']

1.3 在shell中调试

我们同样也可以在Pycharm中打开命令窗口并打开shell。需要

scrapy shell "http://quotes.toscrape.com/page/1/"

数据提取的方法2.jpg

2 选择器的使用(Using selectors)

2.1 在shell中的简单使用方法

在shell中输入命令后,尝试如下语句:

view(response)  # 自动打开浏览器查看当前操作的网页

如果我们要获取这个网页的标题该如何操作呢?

数据提取的方法3.jpg

使用css()方法或者xpath()方法如下:

>>> response.css('title::text').extract()
['Quotes to Scrape']
>>> response.xpath('//title/text()').extract_first()
'Quotes to Scrape'

extract返回的是选择器列表,为了防止后续通过索引的方式取值发生IndexError 应该使用下面的方法extract_first(),这等价于

>>> response.css('title::text')[0].extract()
'Quotes to Scrape'

这样我们就完成了第一个数据提取操作。

2.2 选择器的方法

在1.1中我们构建了一个response

>>> from scrapy.http import HtmlResponse
>>> body = '<html><body><span>good</span></body></html>'.encode("utf-8")
>>> response = HtmlResponse(url='http://example.com', body=body)

response对象的.selector属性可以用来访问选择器(selectors),也就是:

>>> response.selector.xpath('//span/text()').extract()
[u'good']

它和原来的等价:

Selector(response=response).xpath('//span/text()').extract()

同时也等价与:

>>> response.xpath('//span/text()').extract()  # 重点

好了下面我们就用最后一句来展开本次的核心内容。

2.3 练习使用CSS和XPath

本次的文本内容如下,在此之前请通过上面的方法先构建一个response。

<html>
 <head>
  <base href='http://example.com/' />
  <title>Example website</title>
 </head>
 <body>
  <div id='images'>
   <a href='image1.html'>Name: My image 1 <br /><img src='image1_thumb.jpg' /></a>
   <a href='image2.html'>Name: My image 2 <br /><img src='image2_thumb.jpg' /></a>
   <a href='image3.html'>Name: My image 3 <br /><img src='image3_thumb.jpg' /></a>
   <a href='image4.html'>Name: My image 4 <br /><img src='image4_thumb.jpg' /></a>
   <a href='image5.html'>Name: My image 5 <br /><img src='image5_thumb.jpg' /></a>
  </div>
 </body>
</html>

这个例子也可以在shell中进行操作。

scrapy shell "http://doc.scrapy.org/en/latest/_static/selectors-sample1.html"

2.3.1 从根节点访问标题文字

>>> response.xpath('//title/text()').extract_first()
>>> response.css('title::text').extract_first()
Example website

2.3.2 提取图片资源的地址

>>> response.css('img').xpath('@src').extract()
[u'image1_thumb.jpg',  
 u'image2_thumb.jpg',  
 u'image3_thumb.jpg',  
 u'image4_thumb.jpg',  
 u'image5_thumb.jpg']

2.3.3 提取文字

>>> response.css('a::text').extract()
>>> response.xpath('//a/text()').extract()
>>> response.xpath('//div[@id="images"]/a/text()').extract()
[u'Name: My image 1 ', 
u'Name: My image 2 ',
u'Name: My image 3 ', 
u'Name: My image 4 ', 
u'Name: My image 5 ']

如果没有匹配到任何东西返回None,同时可以为extract_first设置默认值

>>> response.xpath('//div[@id="not-exists"]/text()').extract_first() is None:
True
>>> response.xpath('//div[@id="not-exists"]/text()').extract_first(default='not-found')
'not-found'

2.3.4 提取base标签href属性的属性值

>>> response.css('base::attr(href)').extract()
>>> response.xpath('//base/@href').extract()
[u'http://example.com/']

2.3.5 XPath的starts-with和contains

  • xpath中contains用于匹配一个属性中包含的字符串;
  • starts-with 匹配一个属性开始位置的关键字;

    >>> response.xpath('//a[starts-with(@href, "image1")]/@href').extract()
    [u'image1.html']
    >>> response.css('a[href^="image1"]::attr(href)').extract()
    [u'image1.html']

匹配图片所指向的网页

>>> response.xpath('//a[contains(@href, "image")]/@href').extract()
>>> response.css('a[href*=image]::attr(href)').extract()
[u'image1.html',  
 u'image2.html',  
 u'image3.html',  
 u'image4.html',  
 u'image5.html']

匹配图片资源所在地址

>>> response.xpath('//a[contains(@href, "image")]/img/@src').extract()
>>> response.css('a[href*=image] img::attr(src)').extract()
[u'image1_thumb.jpg',  
 u'image2_thumb.jpg',  
 u'image3_thumb.jpg',  
 u'image4_thumb.jpg',  
 u'image5_thumb.jpg']

2.3.6 提取id="xxx"的div标签class属性的属性值

>>> response.css('div#1234::attr(class)').extract()
['quote']

2.3.7 提取div下面的子级或者后辈的文本

提取子级span的文本

>>> response.css('div.quote > span::text').extract()
['erzi']

数据提取的方法4.jpg

提取所有后辈span的文本

>>> response.css('div.quote span::text').extract()
['sunzi', 'erzi']

数据提取的方法5.jpg

2.3.8 提取和div平级的span的文本
提取和id="56"同级,位置在div标签下的所有<span>的文本

>>> response.css('div#56 ~ span::text').extract()
['erzi']

数据提取的方法6.jpg

2.4 选择器的re()方法

选择器对象的css()和xpath()返回的是与原来相同类型的选择器对象列表,而选择器对象的re()方法返回的是unicode 字符串。

>>> response.xpath('//a[contains(@href, "image")]/text()').re(r'Name:\s*(.*)')
[u'My image 1',  
 u'My image 2',  
 u'My image 3',  
 u'My image 4',  
 u'My image 5']
>>> response.xpath('//a[contains(@href, "image")]/text()').re_first(r'Name:\s*(.*)')
u'My image 1'

简单解释下正则语句的意思:

re(r'Name:\s*(.*)')表示:

匹配开始字符为Name:
\s 表示空白字符,*表示零次或多次,那么\s*表示匹配空白字符零次或多次
() 表示对正则表达式进行嵌套
.(点)表示任意字符,那么(.*)便是匹配任意字符多次并返回

2.5 xpath的相对(relative XPaths)

  • 从当前div标签中再探索p标签
>>> divs = response.xpath('//div')
>>> for p in divs.xpath('.//p'):  # extracts all <p> inside 
...  print p.extract()

错误的做法:

  • 从整个文档中获p标签
>>> for p in divs.xpath('//p'):  # extracts all <p> inside
 ...  print p.extract()

附上一张效果图:

数据提取的方法7.jpg

数据提取的方法8.png

2.6 在XPath中使用变量

2.6.1 匹配div标签属性值为images的元素

>>> response.xpath('//div[@id=$val]/a/text()', val='images').extract_first()
u'Name: My image 1 '

2.6.2 找到某个包含5个a标签的div标签

>>> response.xpath('//div[count(a)=$cnt]/@id', cnt=5).extract_first()
u'images'

2.6.3 返回全部文本

  • 当使用文本内容作为匹配参数是不要使用text(),应该使用.(点)

    >>> from scrapy import Selector 
    >>> sel = Selector(text='<a href="#">Click here to go to the <strong>NextPage</strong></a>')
    >>> sel.xpath("//a[contains(.//text(), 'Next Page')]").extract()
    []
    >>> sel.xpath("//a[contains(., 'Next Page')]").extract()
    [u'<a href="#">Click here to go to the <strong>Next Page</strong></a>']
  • 提取文本内容

    >>> sel.xpath("string(//a[1])").extract()
    >>> a_text = sel.xpath("//a")
    >>> content = a_text.xpath("string(.)").extract()
    [u'Click here to go to the Next Page']

2.7 XPath中节点表达式的区别

//node[1]:选择每块node第一个出现的node
(//node)[1]:选择全文的所有node节点中的第一个

>>> from scrapy import Selector
>>> sel = Selector(text="""
....:     <ul class="list">
....:         <li>1</li>
....:         <li>2</li>
....:         <li>3</li>
....:     </ul>
....:     <ul class="list">
....:         <li>4</li>
....:         <li>5</li>
....:         <li>6</li>
....:     </ul>""")
>>> xp = lambda x: sel.xpath(x).extract()
>>> xp("//li[1]")
[u'<li>1</li>', u'<li>4</li>']
>>> xp("(//li)[1]")
[u'<li>1</li>']