2.3-Scrapy爬虫框架-使用Selector提取数据【XPath和CSS选择器】

一、Selector对象

在Python中常用于提取数据的模块:

  • BeautifulSoup 是非常流行的HTTP解析库,API简洁易用,但是速度较慢
  • lxml 解析数据速度快,API相对复杂

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

1、创建对象

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

from scrapy.selector import Selector
text="""

    
        

Hello World

Hello Scrapy

  • C++
  • Java
  • Python
""" selector=Selector(text=text) print(selector) #

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

from scrapy.selector import Selector
from scrapy.http import HtmlResponse
body="""

    
        

Hello World

Hello Scrapy

  • C++
  • Java
  • Python
""" response=HtmlResponse(url='http://www.example.com',body=body,encoding='utf-8') selector=Selector(response=response) print(selector) #

2、选中数据

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

selector_list=selector.xpath('//h1')  #选中文档中所有的

print(selector_list) #其中包含两个

对应的Selector对象 """结果显示: [, """

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

for sel in selector_list:
    print(sel.xpath('./text()'))
"""结果显示:
[]
[]
"""

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

res=selector_list.xpath('./text()')
print(res)
"""结果显示:
[, ]
"""

3、提取数据

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

  • extract()
  • re()
  • extract_first()          (SelectorList专有)  针对第一个
  • re_first()                  (SelectorList专有)  针对第一个

3.1 使用extract()方法提取选中的内容,extract_first()同理

s1=selector.xpath('.//li')
print(s1)
"""结果显示:
[, , ]
"""

s2=s1[0].extract()  #使用extract()方法提取选中的内容
print(s2)
"""结果显示:
  • C++
  • """ p1=selector.xpath('.//li/text()') print(p1) """结果显示: [, , ] """ res1=p1.extract() res2=p1.extract_first() print(res1) #['C++', 'Java', 'Python'] print(res2) #C++

    3.2使用正则表达式提取选中内容的某部分,re()方法,re_first()同理

    text="""
    
    • Python学习手册价格:99.99
    • Python基础编程价格:88.88
    • Python核心知识价格:77.77
    """ selector=Selector(text=text) res=selector.xpath('.//li/b/text()') print(res) """结果显示: [, , ] """ res1=selector.xpath('.//li/b/text()').extract() print(res1) #['价格:99.99', '价格:88.88', '价格:77.77'] res2=selector.xpath('.//li/b/text()').re('\d+\.\d+') print(res2) #['99.99', '88.88', '77.77'] res3=selector.xpath('.//li/b/text()').re_first('\d+\.\d+') print(res3) #99.99

    二、Response内置Selector

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

    class TextResponse(Response):
        def __index__(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)   #自动创建Selector对象
            return self._cached_selector
        ...

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

    from scrapy.http import HtmlResponse
    body="""
    
        
            

    Hello World

    Hello Scrapy

    • C++
    • Java
    • Python
    """ response=HtmlResponse(url='http://www.example.com',body=body,encoding='utf-8') res=response.selector print(res) #

    为了方便使用,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)
        ...

    使用这两种方法,使得代码更加简洁!

    res1=response.xpath('.//h1/text()').extract()
    print(res1)  #['Hello World', 'Hello Scrapy']
    res2=response.css('li::text').extract()
    print(res2)  #['C++', 'Java', 'Python']

    三、XPath

    xpath方法最常用的匹配规则:   //title[@lang='eng']    指:它代表所有名称为title,属性lang的值为eng的节点

    XPath常用的基本语法

    表达式 描述
    / 选取文档的根(root)
    . 选中当前节点
    .. 选中当前节点的父节点
    ELEMENT 选中子节点总所有ELEMENT元素节点
    //ELEMENT 选中后代节点中所有ELEMENT元素节点
    * 选中所有元素子节点
    text() 选中所有元素子节点
    @ATTR 选中名为ATTR的属性节点
    @* 选中所有属性节点
    [谓语]

    谓语用来查找某个特定的节点或者包含某个特定值的节点

    1、通过例子展示XPath的强大!

    首先创建一个html文档,并构造一个HtmlResponse对象:

    from scrapy.selector import Selector
    from scrapy.http import HtmlResponse
    body="""
    
        
            
            Example website
        
        
            
        
    """
    response=HtmlResponse(url='www.example.com',body=body,encoding='utf-8')
    • /    描述一个从根开始的绝对路径
    res1=response.xpath('/html')
    print(res1)  #[]
    res2=response.xpath('/html/head')
    print(res2)  #[]
    • E1/E2   选中E1子节点中的所有E2
    #选取div子节点中的所有a
    res3=response.xpath('/html/body/div/a')
    print(res3)
    """结果显示:
    [, , , , ]
    """
    • //E   选中文档中的所有E,无论在什么位置
    #选取文档中的所有a
    res4=response.xpath('//a')
    print(res4)
    """结果显示:
    [, , , , ]
    """
    • E1//E2   选中E1后代节点中的所有E2,无论在后代中的什么位置
    #选取body后代中的所有img
    res5=response.xpath('/html/body//img')
    print(res5)
    """结果显示:
    [, , , , ]
    """
    • E/text()   选中E的文本子节点
    #选中所有a的文本
    res6=response.xpath('//a/text()')
    print(res6)
    """结果显示:
    [, , , , ]
    """
    print(res6.extract())
    """结果显示:
    ['Name:Image 1', 'Name:Image 2', 'Name:Image 3', 'Name:Image 4', 'Name:Image 5']
    """
    • E/*    选中E的所有元素子节点
    res7=response.xpath('/html/*')   #选取html中所有元素子节点
    print(res7)
    """结果显示:
    [, ]
    """
    res8=response.xpath('/html/body/div//*')  #选取div的所有后代元素节点
    print(res8)
    """结果显示:
    [, , , , , , , , , , , , , , ]
    """
    • */E  选中孙节点中的所有E
    #选取div孙节点中的所有img
    res9=response.xpath('//div/*/img')
    print(res9)
    """结果显示:
    [, , , , ]
    """
    • E/@ATTR  选中E的ATTR属性
    #选中所有immg的src属性
    res10=response.xpath('//img/@src')
    print(res10)
    """结果显示:
    [, , , , ]
    """
    • //@ATTR   选中文档中所有ATTR属性
    #选取所有的href属性
    res11=response.xpath('//@href')
    print(res11)
    """结果显示:
    [, , , , , ]
    """
    • E/@*   选中E的所有属性
    #选取第一个a下img的所有属性(这里只有src一个属性)
    res12=response.xpath('//a[1]/img/@*')
    print(res12)
    """结果显示:[]
    """
    • .   选中当前节点,用来描述相对路径
    res13=response.xpath('//a')[0] #获取第一个a的选择器对象
    print(res13)  #
    #假设我们想选中当前这个a后代中的所有img,下面的做法是错误的!---->会找到文档中所有的img
    #因为//img是绝对路径,会从文档的根开始搜索,而不是从当前的a 开始
    res14=res13.xpath('//img')
    print(res14)
    """结果显示:
    [, , , , ]
    """
    #需要使用.//img来描述当前节点后代中的所有img
    res15=res13.xpath('.//img')
    print(res15)  #[]
    • ..    选中当前节点的父节点,用来描述相对路径
    #选取所有img的父节点
    res16=response.xpath('//img/..')
    print(res16)
    """结果显示:
    [, , , , ]
    """
    • node[谓语]    谓语用来查找某个特定的节点或者包含某个特定值的节点
    res17=response.xpath('//a[3]')  #选取所有a中的第3个
    print(res17) #[]
    res18=response.xpath('//a[last()]')  #使用last函数,选中最后一个
    print(res18) #[]
    res19=response.xpath('//a[position()<=3]') #使用position函数,选中前3个
    print(res19) #[, , ]
    res20=response.xpath('//div[@id]') #选中所有含有id属性的div
    print(res20) #[]
    res21=response.xpath('//div[@id="images"]') #选中所有含有id属性且值为images的div
    print(res21) #[]

    2、常用函数   position()  last()  string()  contains()

    • string(arg)  返回参数的字符串值
    from scrapy.selector import Selector
    text='Click here to go to the Next Page'
    sel=Selector(text=text)
    print(sel) #
    #以下做法和sel.xapth('/html/body/a/strong/text()') 得到相同结果
    res22=sel.xpath('string(/html/body/a/strong)').extract()
    print(res22)  #['Next Page']
    #如果想得到a中的整个字符串‘Click here to go to the Next Page’
    #使用text()就不行,因为Click here to go to the和Next Page不在同一个元素下
    #以下做法将得到两个子串
    res23=sel.xpath('/html/body/a//text()').extract()
    print(res23) #['Click here to go to the ', 'Next Page']​​​​​​​
    #这话情况下使用string()函数
    res24=sel.xpath('string(/html/body/a)').extract()
    print(res24) #['Click here to go to the Next Page']
    • Contains(str1,str2)  判断str1中是否包含str2,返回布尔值
    text="""
    

    hello world

    hello scrapy

    """ sel=Selector(text=text) res25=sel.xpath('//p[contains(@class,"small")]') #选择class属性中包含“small”的p元素 print(res25) """结果显示:[] """ res26=sel.xpath('//p[contains(@class,"info")]') #选择class属性中包含“info”的p元素 print(res26) """结果显示:[, ] """

    四、CSS选择器

    css选择器比xpath简单一点,但是功能没有那么强大

    css选择器

    表达式 描述 例子
    * 选中所有元素 *
    E 选中E元素 P
    E1,E2  选中E1和E2元素 div,pre
    E1 E2 选中E1后代元素中的E2元素 div p
    E1>E2 选中E1子元素中的E2元素 div>p
    E1+E2 选中E1兄弟元素中的E2元素 p+strong
    .CLASS 选中class属性为CLASS的元素 .info
    #ID 选中id属性为ID的元素 #main
    [ATTR] 选中包含ATTR属性的元素

    [href]

    [ATTR=VALUE] 选中包含ATTR属性且值为VALUE的元素 [method=post]
    [ATTR~=VALUE] 选中包含ATTR属性且值包含VALUE的元素 [method~=clearfix]

    E:nth-child(n)

    E:nth-last-child(n)

    选中E元素,且该元素必须是其父元素的(倒数)第n个子元素

    a:nth-last(1)

    a:nth-last-child(2)

    E:first-child

    E:last-child

    选中E元素,且该元素必须是其父元素的(倒数)第一个子元素

    a:first-child

    a:last-child

    E:empty 选中没有子元素的E元素 div:empty
    E::text 选中E元素的文本节点 p::text

    根据例子,来感受css选择器的强大!

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

    from scrapy.selector import Selector
    from scrapy.http import HtmlResponse
    body="""
    
        
            
            Example website
        
        
            
            
        
    """
    response=HtmlResponse(url='www.example.com',body=body,encoding='utf-8')
    • E  选中E元素
    #选择所有img
    res1=response.css('img')
    print(res)
    """结果显示:
    [, , , , ]
    """
    • E1,E2  选中E1和E2元素
    #选择所有base和title
    res2=response.css('base,title')
    print(res2)
    """结果显示:
    [, ]
    """
    • E1 E2  选中E1后代元素中的E2元素
    #div后代中的img
    res3=response.css('div img')
    print(res3)
    """结果显示:
    [, , , , ]
    """
    • E1>E2   选中E1子元素中的E2元素
    #body子元素中的div
    res4=response.css('body>div')
    print(res4)
    """结果显示:
    [, ]
    """
    • [ATTR]  选中包含ATTR属性的元素
    #选中保安style属性的元素
    res5=response.css('[style]')
    print(res5) #[]
    • [ATTR=VALUE]  选中包含ATTR属性且值为VALUE的元素
    #选择属性id值为images-1的元素
    res6=response.css('[id=images-1]')
    print(res6)  #[]
    • E:nth-child(n)  选中E元素,且该元素必须是其父元素的第n个子元素
    #选择每个div的第一个a
    res7=response.css('div>a:nth-child(1)')
    print(res7)
    """结果显示:
    [, ]
    """
    
    #选择第二个div的第一个a
    res8=response.css('div:nth-child(2)>a:nth-child(1)')
    print(res8)
    """结果显示:
    []
    """
    • E:first-child  选中E元素,该元素必须是其父元素的第一个子元素
    • E:last-child   选中E元素,该元素必须是其父元素的倒数第一个子元素
    #选择最后一个div的最后一个a 
    res9=response.css('div:first-child>a:last-child')
    print(res9)
    """结果显示:
    []
    """
    • E::text    选中E元素的文本节点
    #选取所有a的文本
    res10=response.css('a::text')
    print(res10)
    """结果显示:
    [, , , , ]
    """
    res11=response.css('a::text').extract()
    print(res11)
    """结果显示:
    ['Name:Image 1', 'Name:Image 2', 'Name:Image 3', 'Name:Image 4', 'Name:Image 5']
    """

    转载于:https://my.oschina.net/pansy0425/blog/2999724

    你可能感兴趣的:(爬虫,python)