第3章
使用Selector提取数据

在第2章中,我们讲解了Spider开发的大体流程,让大家对Scrapy爬虫开发有了更加清晰的理解,接下来继续讲解编写爬虫时使用的一些具体技术,从页面中提取数据是Spider最重要的工作之一,这一章我们来学习相关内容。

3.1 Selector对象

从页面中提取数据的核心技术是HTTP文本解析,在Python中常用以下模块处理此类问题:

● BeautifulSoup

广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元

BeautifulSoup是非常流行的HTTP解析库,API简洁易用,但解析速度较慢。

● lxml

lxml是一套由C语言编写的xml解析库(libxml2),解析速度更快,API相对复杂。

Scrapy综合上述两者优点实现了Selector类,它是基于lxml库构建的,并简化了API接口。在Scrapy中使用Selector对象提取页面中的数据,使用时先通过XPath或CSS选择器选中页面中要提取的数据,然后进行提取。

下面详细介绍Selector对象的使用。

3.1.1 创建对象

Selector类的实现位于scrapy.selector模块,创建Selector对象时,可将页面的HTML文档字符串传递给Selector构造器方法的text参数:

     >>> from scrapy.selector import Selector
     >>> text = '''
     ... <html>
     ...    <body>
     ...         <h1>Hello World</h1>
     ...         <h1>Hello Scrapy</h1>
     ...         <b>Hello python</b>
     ...         <ul>
     ...             <li>C++</li>
     ...             <li>Java</li>
     ...             <li>Python</li>
     ...         </ul>
     ...    </body>
     ... </html>
     ... '''
     ...
     >>> selector = Selector(text=text)
     <Selector xpath=None data='<html>\n       <body>\n  <h1>He'>

也可以使用一个Response对象构造Selector对象,将其传递给Selector构造器方法的response参数:

     >>> from scrapy.selector import Selector
     >>> from scrapy.http import HtmlResponse
     >>> body = '''
     ... <html>
     ...    <body>
     ...         <h1>Hello World</h1>
     ...         <h1>Hello Scrapy</h1>
     ...         <b>Hello python</b>
     ...         <ul>
     ...             <li>C++</li>
     ...             <li>Java</li>
     ...             <li>Python</li>
     ...         </ul>
     ...    </body>
     ... </html>
     ... '''
     ...
     >>> response = HtmlResponse(url='http://www.example.com', body=body, encoding='utf8')
     >>> selector = Selector(response=response)
     >>> selector
     <Selector xpath=None data='<html>\n        <body>\n               <h1>He'>

3.1.2 选中数据

调用Selector对象的xpath方法或css方法(传入XPath或CSS选择器表达式),可以选中文档中的某个或某些部分:

     >>> selector_list = selector.xpath('//h1')  # 选中文档中所有的<h1>
     >>> selector_list                           # 其中包含两个<h1>对应的Selector 对象
     [<Selector xpath='.//h1' data='<h1>Hello World</h1>'>,
     <Selector xpath='.//h1' data='<h1>Hello Scrapy</h1>'>]

xpath和css方法返回一个SelectorList对象,其中包含每个被选中部分对应的Selector对象,SelectorList支持列表接口,可使用for语句迭代访问其中的每一个Selector对象:

     >>> for sel in selector_list:
     ...   print(sel.xpath('./text()'))
     ...
     [<Selector xpath='./text()' data='Hello World'>]
     [<Selector xpath='./text()' data='Hello Scrapy'>]

SelectorList对象也有xpath和css方法,调用它们的行为是:以接收到的参数分别调用其中每一个Selector对象的xpath或css方法,并将所有结果收集到一个新的SelectorList对象返回给用户。请看下面的示例:

     >>> selector_list.xpath('./text()')
     [<Selector xpath='./text()' data='Hello World'>,
      <Selector xpath='./text()' data='Hello Scrapy'>]
     >>> selector.xpath('.//ul').css('li').xpath('./text()')
     [<Selector xpath='./text()' data='C++'>,
      <Selector xpath='./text()' data='Java'>,
      <Selector xpath='./text()' data='Python'>]

3.1.3 提取数据

调用Selector或SelectorLis对象的以下方法可将选中的内容提取:

● extract()

● re()

● extract_first() (SelectorList专有)

● re_first() (SelectorList专有)

首先来看extract方法,调用Selector对象的extract方法将返回选中内容的Unicode字符串:

     >>> sl = selector.xpath('.//li')
     >>> sl
     [<Selector xpath='.//li' data='<li>C++</li>'>,
     <Selector xpath='.//li' data='<li>Java</li>'>,
     <Selector xpath='.//li' data='<li>Python</li>'>]
     >>> sl[0].extract()
     '<li>C++</li>'
     >>> sl = selector.xpath('.//li/text()')
     >>> sl
     [<Selector xpath='.//li/text()' data='C++'>,
     <Selector xpath='.//li/text()' data='Java'>,
     <Selector xpath='.//li/text()' data='Python'>]
     >>> sl[1].extract()
     'Java'

与SelectorList对象的xpath和css方法类似,SelectorList对象的extract方法内部会调用其中每个Selector对象的extract方法,并把所有结果收集到一个列表返回给用户:

     >>> sl = selector.xpath('.//li/text()')
     >>> sl
     [<Selector xpath='.//li/text()' data='C++'>,
      <Selector xpath='.//li/text()' data='Java'>,
      <Selector xpath='.//li/text()' data='Python'>]
     >>> sl.extract()
     ['C++', 'Java', 'Python']

SelectorList对象还有一个extract_first方法,该方法返回其中第一个Selector对象调用extract方法的结果。通常,在SelectorList对象中只包含一个Selector对象时调用该方法,直接提取出Unicode字符串而不是列表:

     >>> sl = selector.xpath('.//b')
     >>> sl
     [<Selector xpath='.//b' data='<b>Hello Python</b>'>]
     >>> sl.extract()
     ['<b>Hello Python</b>']
     >>> sl.extract_first()
     '<b>Hello Python</b>'

有些时候,我们想使用正则表达式提取选中内容中的某部分,可以使用re方法(两个对象都有该方法):

     >>> text = '''
     ... <ul>
     ...    <li>Python 学习手册 <b>价格: 99.00 元</b></li>
     ...    <li>Python 核心编程 <b>价格: 88.00 元</b></li>
     ...    <li>Python 基础教程 <b>价格: 80.00 元</b></li>
     ... </ul>
     ... '''
     ...
     >>> selector = Selector(text=text)
     >>> selector.xpath('.//li/b/text()')
     [<Selector xpath='.//li/b/text()' data='价格: 99.00 元'>,
      <Selector xpath='.//li/b/text()' data='价格: 88.00 元'>,
      <Selector xpath='.//li/b/text()' data='价格: 80.00 元'>]
     >>> selector.xpath('.//li/b/text()').extract()
     ['价格: 99.00 元', '价格: 88.00 元', '价格: 80.00 元']
     >>> selector.xpath('.//li/b/text()').re('\d+\.\d+') #只提取价格的数字部分
     ['99.00', '88.00', '80.00']

SelectorList对象的re_first方法同样返回其中的第一个Selector对象调用re方法的结果:

     >>> selector.xpath('.//li/b/text()').re_first('\d+\.\d+')
     '99.00'

3.2 Response内置Selector

在实际开发中,几乎不需要手动创建Selector对象,在第一次访问一个Response对象的selector属性时,Response对象内部会以自身为参数自动创建Selector对象,并将该Selector对象缓存,以便下次使用。Scrapy源码中的相关实现如下:

     class TextResponse(Response):


        def __init__(self, *args, **kwargs):
            ...
            self._cached_selector = None
            ...


        @property
        def selector(self):
            from scrapy.selector import Selector
            if self._cached_selector is None:
                self._cached_selector = Selector(self)
            return self._cached_selector
        ...

通常,我们直接使用Response对象内置的Selector对象即可:

     >>> from scrapy.http import HtmlResponse
     >>> body = '''
     ... <html>
     ...   <body>
     ...       <h1>Hello World</h1>
     ...       <h1>Hello Scrapy</h1>
     ...       <b>Hello python</b>
     ...       <ul>
     ...           <li>C++</li>
     ...           <li>Java</li>
     ...           <li>Python</li>
     ...       </ul>
     ...   </body>
     ... </html>
     ... '''
     ...
     >>> response = HtmlResponse(url='http://www.example.com', body=body, encoding='utf8')
     >>> response.selector
     <Selector xpath=None data='<html>\n        <body>\n               <h1>He'>

为了方便用户使用,Response对象还提供了xpath和css方法,它们在内部分别调用内置Selector对象的xpath和css方法。Scrapy源码中的相关实现如下:

     class TextResponse(Response):
        ...
        def xpath(self, query, **kwargs):
           return self.selector.xpath(query, **kwargs)


        def css(self, query):
           return self.selector.css(query)
        ...

使用这两个快捷方式可使代码更加简洁:

     >>> response.xpath('.//h1/text()').extract()
     ['Hello World', 'Hello Scrapy']
     >>> response.css('li::text').extract()
     ['C++', 'Java', 'Python']

3.3 XPath

XPath即XML路径语言(XML Path Language),它是一种用来确定xml文档中某部分位置的语言。

xml文档(html属于xml)是由一系列节点构成的树,例如:

     <html>
        <body>
           <div >
              <p>Hello world<p>
              <a href="/home">Click here</a>
           </div>
        </body>
     </html>

xml文档的节点有多种类型,其中最常用的有以下几种:

● 根节点 整个文档树的根。

● 元素节点 html、body、div、p、a。

● 属性节点 href。

● 文本节点 Hello world、Click here。

节点间的关系有以下几种:

● 父子 body是html的子节点,p和a是div的子节点。反过来,div是p和a的父节点。

● 兄弟 p和a为兄弟节点。

● 祖先/后裔 body、div、p、a都是html的后裔节点;反过来html是body、div、p、a的祖先节点。

3.3.1 基础语法

表3-1列出了XPath常用的基本语法。

表3-1 XPath常用的基本语法

阅读 ‧ 电子书库

接下来,我们通过一些例子展示XPath的使用。

首先创建一个用于演示的html文档,并用其构造一个HtmlResponse对象:

     >>> from scrapy.selector import Selector
     >>> from scrapy.http import HtmlResponse
     >>> body = '''
     ... <html>
     ...    <head>
     ...         <base href='http://example.com/' />
     ...         <title>Example website</title>
     ...    </head>
     ...    <body>
     ...         <div id='images'>
     ...             <a href='image1.html'>Name: Image 1 <br/><img src='image1.jpg' /></a>
     ...             <a href='image2.html'>Name: Image 2 <br/><img src='image2.jpg' /></a>
     ...             <a href='image3.html'>Name: Image 3 <br/><img src='image3.jpg' /></a>
     ...             <a href='image4.html'>Name: Image 4 <br/><img src='image4.jpg' /></a>
     ...             <a href='image5.html'>Name: Image 5 <br/><img src='image5.jpg' /></a>
     ...         </div>
     ...    </body>
     ... </html>
     ... '''
     ...
     >>> response = HtmlResponse(url='http://www.example.com', body=body, encoding='utf8')

● /:描述一个从根开始的绝对路径。

     >>> response.xpath('/html')
     [<Selector xpath='/html' data='<html>\n\t<head>\n\t\t<base href="http://exam'>]
     >>> response.xpath('/html/head')
     [<Selector xpath='/html/head' data='<head>\n\t\t<base href="http://example.com/'>]

● E1/E2:选中E1子节点中的所有E2。

     # 选中div子节点中的所有a
     >>> response.xpath('/html/body/div/a')
     [<Selector xpath='/html/body/div/a' data='<a href="image1.html">Name: My image 1 <'>,
     <Selector xpath='/html/body/div/a' data='<a href="image2.html">Name: My image 2 <'>,
     <Selector xpath='/html/body/div/a' data='<a href="image3.html">Name: My image 3 <'>,
     <Selector xpath='/html/body/div/a' data='<a href="image4.html">Name: My image 4 <'>,
     <Selector xpath='/html/body/div/a' data='<a href="image5.html">Name: My image 5 <'>]

● //E:选中文档中的所有E,无论在什么位置。

     # 选中文档中的所有a
     >>> response.xpath('//a')
     [<Selector xpath='//a' data='<a href="image1.html">Name: My image 1 <'>,
     <Selector xpath='//a' data='<a href="image2.html">Name: My image 2 <'>,
     <Selector xpath='//a' data='<a href="image3.html">Name: My image 3 <'>,
     <Selector xpath='//a' data='<a href="image4.html">Name: My image 4 <'>,
     <Selector xpath='//a' data='<a href="image5.html">Name: My image 5 <'>]

● E1//E2:选中E1后代节点中的所有E2,无论在后代中的什么位置。

     # 选中body后代中的所有img
     >>> response.xpath('/html/body//img')
     [<Selector xpath='/html/body//img' data='<img src="image1.jpg">'>,
      <Selector xpath='/html/body//img' data='<img src="image2.jpg">'>,
      <Selector xpath='/html/body//img' data='<img src="image3.jpg">'>,
      <Selector xpath='/html/body//img' data='<img src="image4.jpg">'>,
      <Selector xpath='/html/body//img' data='<img src="image5.jpg">'>]

● E/text():选中E的文本子节点。

     # 选中所有a的文本
     >>> sel = response.xpath('//a/text()')
     >>> sel
     [<Selector xpath='//a/text()' data='Name: My image 1 '>,
     <Selector xpath='//a/text()' data='Name: My image 2 '>,
     <Selector xpath='//a/text()' data='Name: My image 3 '>,
     <Selector xpath='//a/text()' data='Name: My image 4 '>,
     <Selector xpath='//a/text()' data='Name: My image 5 '>]
     >>> sel.extract()
     ['Name: My image 1 ',
     'Name: My image 2 ',
     'Name: My image 3 ',
     'Name: My image 4 ',
     'Name: My image 5 ']

● E/*:选中E的所有元素子节点。

     # 选中html的所有元素子节点
     >>> response.xpath('/html/*')
     [<Selector xpath='/html/*' data='<head>\n\t\t<base href="http://example.com/'>,
      <Selector xpath='/html/*' data='<body>\n\t\t<div id="images">\n\t\t\t<a href="i'>]


     # 选中div的所有后代元素节点
     >>> response.xpath('/html/body/div//*')
     [<Selector xpath='/html/body/div//*' data='<a href="image1.html">Name: My image 1 <'>,
      <Selector xpath='/html/body/div//*' data='<br>'>,
      <Selector xpath='/html/body/div//*' data='<img src="image1.jpg">'>,
      <Selector xpath='/html/body/div//*' data='<a href="image2.html">Name: My image 2 <'>,
      <Selector xpath='/html/body/div//*' data='<br>'>,
      <Selector xpath='/html/body/div//*' data='<img src="image2.jpg">'>,
      <Selector xpath='/html/body/div//*' data='<a href="image3.html">Name: My image 3 <'>,
      <Selector xpath='/html/body/div//*' data='<br>'>,
      <Selector xpath='/html/body/div//*' data='<img src="image3.jpg">'>,
      <Selector xpath='/html/body/div//*' data='<a href="image4.html">Name: My image 4 <'>,
      <Selector xpath='/html/body/div//*' data='<br>'>,
      <Selector xpath='/html/body/div//*' data='<img src="image4.jpg">'>,
      <Selector xpath='/html/body/div//*' data='<a href="image5.html">Name: My image 5 <'>,
      <Selector xpath='/html/body/div//*' data='<br>'>,
      <Selector xpath='/html/body/div//*' data='<img src="image5.jpg">'>]

● */E:选中孙节点中的所有E。

     # 选中div孙节点中的所有img
     >>> response.xpath('//div/*/img')
     [<Selector xpath='//div/*/img' data='<img src="image1.jpg">'>,
      <Selector xpath='//div/*/img' data='<img src="image2.jpg">'>,
      <Selector xpath='//div/*/img' data='<img src="image3.jpg">'>,
      <Selector xpath='//div/*/img' data='<img src="image4.jpg">'>,
      <Selector xpath='//div/*/img' data='<img src="image5.jpg">'>]

● E/@ATTR:选中E的ATTR属性。

     # 选中所有img的src 属性
     >>> response.xpath('//img/@src')
     [<Selector xpath='//img/@src' data='image1.jpg'>,
     <Selector xpath='//img/@src' data='image2.jpg'>,
     <Selector xpath='//img/@src' data='image3.jpg'>,
     <Selector xpath='//img/@src' data='image4.jpg'>,
     <Selector xpath='//img/@src' data='image5.jpg'>]

● //@ATTR:选中文档中所有ATTR属性。

     # 选中所有的href 属性
     >>> response.xpath('//@href')
     [<Selector xpath='//@href' data='http://example.com/'>,
     <Selector xpath='//@href' data='image1.html'>,
     <Selector xpath='//@href' data='image2.html'>,
     <Selector xpath='//@href' data='image3.html'>,
     <Selector xpath='//@href' data='image4.html'>,
     <Selector xpath='//@href' data='image5.html'>]

● E/@*:选中E的所有属性。

     # 获取第一个a 下img的所有属性(这里只有src 一个属性)
     >>> response.xpath('//a[1]/img/@*')
     [<Selector xpath='//a[1]/img/@*' data='image1.jpg'>]

● .:选中当前节点,用来描述相对路径。

     # 获取第1个a的选择器对象
     >>> sel = response.xpath('//a')[0]
     >>> sel
     <Selector xpath='//a' data='<a href="image1.html">Name: My image 1 <'>


     # 假设我们想选中当前这个a 后代中的所有img,下面的做法是错误的,
     # 会找到文档中所有的img
     # 因为//img是绝对路径,会从文档的根开始搜索,而不是从当前的a 开始
     >>> sel.xpath('//img')
     [<Selector xpath='//img' data='<img src="image1.jpg">'>,
     <Selector xpath='//img' data='<img src="image2.jpg">'>,
     <Selector xpath='//img' data='<img src="image3.jpg">'>,
     <Selector xpath='//img' data='<img src="image4.jpg">'>,
     <Selector xpath='//img' data='<img src="image5.jpg">'>]
     # 需要使用.//img 来描述当前节点后代中的所有img
     >>> sel.xpath('.//img')
     [<Selector xpath='.//img' data='<img src="image1.jpg">'>]

● ..:选中当前节点的父节点,用来描述相对路径。

     # 选中所有img的父节点
     >>> response.xpath('//img/..')
     [<Selector xpath='//img/..' data='<a href="image1.html">Name: My image 1 <'>,
      <Selector xpath='//img/..' data='<a href="image2.html">Name: My image 2 <'>,
      <Selector xpath='//img/..' data='<a href="image3.html">Name: My image 3 <'>,
      <Selector xpath='//img/..' data='<a href="image4.html">Name: My image 4 <'>,
      <Selector xpath='//img/..' data='<a href="image5.html">Name: My image 5 <'>]

● node[谓语]:谓语用来查找某个特定的节点或者包含某个特定值的节点。

     # 选中所有a 中的第3 个
     >>> response.xpath('//a[3]')
     [<Selector xpath='//a[3]' data='<a href="image3.html">Name: My image 3 <'>]


     # 使用last函数,选中最后1 个
     >>> response.xpath('//a[last()]')
     [<Selector xpath='//a[last()]' data='<a href="image5.html">Name: My image 5 <'>]


     # 使用position函数,选中前3 个
     >>> response.xpath('//a[position()<=3]')
     [<Selector xpath='//a[position()<=3]' data='<a href="image1.html">Name: My image 1 <'>,
      <Selector xpath='//a[position()<=3]' data='<a href="image2.html">Name: My image 2 <'>,
      <Selector xpath='//a[position()<=3]' data='<a href="image3.html">Name: My image 3 <'>]


     # 选中所有含有id属性的div
     >>> response.xpath('//div[@id]')
     [<Selector xpath='//div[@id]' data='<div id="images">\n\t\t\t<a href="image1.htm'>]


     # 选中所有含有id属性且值为"images"的div
     >>> response.xpath('//div[@id="images"]')
     [<Selector xpath='//div[@id="images"]' data='<div id="images">\n\t\t\t<a href="image1.htm'>]

3.3.2 常用函数

XPath还提供许多函数,如数字、字符串、时间、日期、统计等。在上面的例子中,我们已经使用了函数position()、last()。由于篇幅有限,下面仅介绍两个十分常用的字符串函数。

● string(arg):返回参数的字符串值。

     >>> from scrapy.selector import Selector
     >>> text='<a href="#">Click here to go to the <strong>Next Page</strong></a>'
     >>> sel = Selector(text=text)
     >>> sel
     <Selector xpath=None data='<html><body><a href="#">Click here to go'>
     # 以下做法和sel.xpath('/html/body/a/strong/text()')得到相同结果
     >>> sel.xpath('string(/html/body/a/strong)').extract()
     ['Next Page']
     # 如果想得到a 中的整个字符串'Click here to go to the Next Page',
     # 使用text()就不行了,因为Click here to go to the和Next Page 在不同元素下
     # 以下做法将得到两个子串
     >>> sel.xpath('/html/body/a//text()').extract()
     ['Click here to go to the ', 'Next Page']
     # 这种情况下可以使用string()函数
     >>> sel.xpath('string(/html/body/a)').extract()
     ['Click here to go to the Next Page']

● contains(str1, str2):判断str1中是否包含str2,返回布尔值。

     >>> text = '''
     ... <div>
     ...    <p class="small info">hello world</p>
     ...    <p class="normal info">hello scrapy</p>
     ... </div>
     ... '''
     >>> sel = Selector(text=text)
     >>> sel.xpath('//p[contains(@class, "small")]') # 选择class 属性中包含"small"的p 元素
     [<Selector xpath='//p[contains(@class, "small")]' data='<p class="small info">hello world</p>'>]
     >>> sel.xpath('//p[contains(@class, "info")]') # 选择class 属性中包含"info"的p 元素
     [<Selector xpath='//p[contains(@class, "info")]' data='<p class="small info">hello world</p>'>,
      <Selector xpath='//p[contains(@class, "info")]' data='<p class="normal info">hello scrapy</p>'>]

关于XPath的使用先介绍到这里,更多详细内容可以参看XPath文档:https://www.w3.org/TR/xpath/。

3.4 CSS选择器

CSS即层叠样式表,其选择器是一种用来确定HTML文档中某部分位置的语言。

CSS选择器的语法比XPath更简单一些,但功能不如XPath强大。实际上,当我们调用Selector对象的CSS方法时,在其内部会使用Python库cssselect将CSS选择器表达式翻译成XPath表达式,然后调用Selector对象的XPATH方法。

表3-2列出了CSS选择器的一些基本语法。

表3-2 CSS选择器

阅读 ‧ 电子书库

和学习XPath一样,通过一些例子展示CSS选择器的使用。

先创建一个HTML文档并构造一个HtmlResponse对象:

     >>> from scrapy.selector import Selector
     >>> from scrapy.http import HtmlResponse
     >>> body = '''
     ... <html>
     ...    <head>
     ...         <base href='http://example.com/' />
     ...         <title>Example website</title>
     ...    </head>
     ...    <body>
     ...         <div id='images-1' style="width: 1230px;">
     ...             <a href='image1.html'>Name: Image 1 <br/><img src='image1.jpg' /></a>
     ...             <a href='image2.html'>Name: Image 2 <br/><img src='image2.jpg' /></a>
     ...             <a href='image3.html'>Name: Image 3 <br/><img src='image3.jpg' /></a>
     ...         </div>
     ...
     ...         <div id='images-2' class='small'>
     ...             <a href='image4.html'>Name: Image 4 <br/><img src='image4.jpg' /></a>
     ...             <a href='image5.html'>Name: Image 5 <br/><img src='image5.jpg' /></a>
     ...         </div>
     ...    </body>
     ... </html>
     ... '''
     ...
     >>> response = HtmlResponse(url='http://www.example.com', body=body, encoding='utf8')

● E:选中E元素。

     # 选中所有的img
     >>> response.css('img')
     [<Selector xpath='descendant-or-self::img' data='<img src="image1.jpg">'>,
      <Selector xpath='descendant-or-self::img' data='<img src="image2.jpg">'>,
      <Selector xpath='descendant-or-self::img' data='<img src="image3.jpg">'>,
      <Selector xpath='descendant-or-self::img' data='<img src="image4.jpg">'>,
      <Selector xpath='descendant-or-self::img' data='<img src="image5.jpg">'>]

● E1,E2:选中E1和E2元素。

         # 选中所有base和title
         >>> response.css('base,title')
         [<Selector xpath='descendant-or-self::base | descendant-or-self::title' data='<base
     href="http://example.com/">'>,
          <Selector xpath='descendant-or-self::base | descendant-or-self::title' data='<title>Example
     website</title>'>]

● E1 E2:选中E1后代元素中的E2元素。

     # div 后代中的img
     >>> response.css('div img')
     [<Selector xpath='descendant-or-self::div/descendant-or-self::*/img' data='<img src="image1.jpg">'>,
      <Selector xpath='descendant-or-self::div/descendant-or-self::*/img' data='<img src="image2.jpg">'>,
      <Selector xpath='descendant-or-self::div/descendant-or-self::*/img' data='<img src="image3.jpg">'>,
      <Selector xpath='descendant-or-self::div/descendant-or-self::*/img' data='<img src="image4.jpg">'>,
      <Selector xpath='descendant-or-self::div/descendant-or-self::*/img' data='<img src="image5.jpg">'>]

● E1>E2:选中E1子元素中的E2元素。

     # body 子元素中的div
     >>> response.css('body>div')
     [<Selector xpath='descendant-or-self::body/div' data='<div id="images-1" style="width: 1230px;'>,
      <Selector xpath='descendant-or-self::body/div' data='<div id="images-2" class="small">\n '>]

● [ATTR]:选中包含ATTR属性的元素。

     # 选中包含style属性的元素
     >>> response.css('[style]')
     [<Selector xpath='descendant-or-self::*[@style]' data='<div id="images-1" style="width: 1230px;'>]

● [ATTR=VALUE]:选中包含ATTR属性且值为VALUE的元素。

          # 选中属性id值为images-1的元素
          >>> response.css('[id=images-1]')
          [<Selector xpath="descendant-or-self::*[@id = 'images-1']" data='<div id="images-1" style="width:
     1230px;'>]

● E:nth-child(n):选中E元素,且该元素必须是其父元素的第n个子元素。

         # 选中每个div的第一个a
         >>> response.css('div>a:nth-child(1)')
         [<Selector xpath="descendant-or-self::div/*[name() = 'a' and (position() = 1)]" data='<a
     href="image1.html">Name: Image 1 <br>'>,
          <Selector xpath="descendant-or-self::div/*[name() = 'a' and (position() = 1)]" data='<a
     href="image4.html">Name: Image 4 <br>'>]


         # 选中第二个div的第一个a
         >>> response.css('div:nth-child(2)>a:nth-child(1)')
         [<Selector xpath="descendant-or-self::*/*[name() = 'div' and (position() = 2)]/*[name() = 'a' and
     (position() = 1)]" data='<a href="image4.html">Name: Image 4 <br>'>]

● E:first-child:选中E元素,该元素必须是其父元素的第一个子元素。

● E:last-child:选中E元素,该元素必须是其父元素的倒数第一个子元素。

          # 选中第一个div的最后一个a
          >>> response.css('div:first-child>a:last-child')
          [<Selector xpath="descendant-or-self::*/*[name() = 'div' and (position() = 1)]/*[name() = 'a' and
     (position() = last())]" data='<a href="image3.html">Name: Image 3 <br>'>]

● E::text:选中E元素的文本节点。

     # 选中所有a的文本
     >>> sel = response.css('a::text')
     >>> sel
     [<Selector xpath='descendant-or-self::a/text()' data='Name: Image 1 '>,
      <Selector xpath='descendant-or-self::a/text()' data='Name: Image 2 '>,
      <Selector xpath='descendant-or-self::a/text()' data='Name: Image 3 '>,
      <Selector xpath='descendant-or-self::a/text()' data='Name: Image 4 '>,
      <Selector xpath='descendant-or-self::a/text()' data='Name: Image 5 '>]
     >>> sel.extract()
     ['Name: Image 1 ',
      'Name: Image 2 ',
      'Name: Image 3 ',
      'Name: Image 4 ',
      'Name: Image 5 ']

关于CSS选择器的使用先介绍到这里,更多详细内容可以参看CSS选择器文档:https://www.w3.org/TR/css3-selectors/。

3.5 本章小结

本章学习了从页面中提取数据的相关内容,首先带大家了解了Scrapy中的Selector对象,然后学习如何使用Selector对象在页面中选中并提取数据,最后通过一系列例子讲解了XPath和CSS选择器的用法。