• scrapy实战,使用内置的xpath,re和css提取值


    以伯乐在线文章为爬取目标blog.jobbole.com,发现在"最新文章"选项中可看到所有文章

     
    一般来说,可以用scrapy中自带的xpath或者css来提取数据,定义在spiders/jobbole.py中的def parse(self, response)
    复制代码
    import scrapy
     
     
    class JobboleSpider(scrapy.Spider):
        name = 'jobbole'
        allowed_domains = ['blog.jobbole.com']
        start_urls = ['http://blog.jobbole.com/']
     
        def parse(self, response):
            re_selector = response.xpath('//*[@id="post-110287"]/div[1]/h1/text()')
    复制代码
     
    注意:因为jqury会生成额外的代码,我们在源码看到的代码和页面加载之后显示的代码可能不同,所以不要按层级一步步找,最好找到id,或者class来定位
     
    小技巧:
    1)当我们使用class来定位标签时,可以在F12中用ctrl+F 查看这个class名字是否唯一
    2)Xpath路径可右键直接复制
     
     
     
     
    一. Xpath常用方法
     
    1. 常用规则如下
     
    //           从当前节点选取子孙节点,如果符号前面没路径,表示整个文档
    /            从当前节点选取直接子节点
    .             选取当前节点
    ..            选取当前节点父节点
    @            选取属性
    //*            整个HTML文本中的所有节点
     
     
    例子1
    复制代码
    <html><body><div>
    <ul>
    <li class="item-0"><a href="link1.html"><span>first item</span></a></li>
    <li class="item-1"><a href="link2.html">second item</a></li>
    <li class="item-inactive"><a href="link3.html">third item</a></li>
    <li class="item-1"><a href="link4.html">fourth item</a></li>
    <li class="item-0"><a href="link5.html">fifth item</a></li>
    </ul>
    </div></body></html>
     
    复制代码
    1. 获取父节点属性
    首先选中href属性为link4.html的a节点,然后再获取其父节点,然后再获取其class属性
    result1 = response.xpath('//a[@href="link4.html"]/../@class')
    我们也可以通过parent::来获取父节点
    result2 = response.xpath('//a[@href="link4.html"]/parent::*/@class')
    注意:
    //a表示html中的所有a节点,他们的href属性有多个,这里[]的作用是属性匹配,找到a的href属性为link4.html的节点
     
     
    2. 获取节点内部文本
    获取class为item-1的li节点文本,
    result3 = response.xpath('//li[@class="item-0"]/a/text()')
    返回结果为['first item', 'fifth item']
     
    3. 属性获取
    获取所有li节点下的所有a节点的href属性
    result4 = response.xpath('//li/a/@href')
    返回结果为['link1.html', 'link2.html', 'link3.html', 'link4.html', 'link5.html']
     
    4. 按序选择
    result = response.xpath('//li[1]/a/text()')   #选取第一个li节点
    result = response.xpath('//li[last()]/a/text()')   #选取最后一个li节点
    result = response.xpath('//li[position()<3]/a/text()')   #选取位置小于3的li节点,也就是1和2的节点
    result = response.xpath('//li[last()-2]/a/text()')  #选取倒数第三个节点
     
    5. 节点轴选择
    复制代码
    1)返回第一个li节点的所有祖先节点,包括html,body,div和ul
    result = response.xpath('//li[1]/ancestor::*')
     
    2)返回第一个li节点的<div>祖先节点
    result = response.xpath('//li[1]/ancestor::div')
     
    3)返回第一个li节点的所有属性值
    result = response.xpath('//li[1]/attribute::*')
     
    4)首先返回第一个li节点的所有子节点,然后加上限定条件,选组href属性为link1.html的a节点
    result = response.xpath('//li[1]/child::a[@href="link1.html"]')
     
    5)返回第一个li节点的所有子孙节点,然后加上只要span节点的条件
    result = response.xpath('//li[1]/descendant::span')
     
    6)following轴可获得当前节点之后的所有节点,虽然使用了*匹配,但是又加了索引选择,所以只获取第2个后续节点,也就是第2个<li>节点中的<a>节点
    result = response.xpath('//li[1]/following::*[2]')
     
    7)following-sibling可获取当前节点之后的所有同级节点,也就是后面所有的<li>节点
    result = response.xpath('//li[1]/following-sibling::*')
    复制代码
     
    6. 属性多值匹配
    复制代码
    <li class="li li-first"><a href="link.html">first item</a></li>
     
    result5 = response.xpath('//li[@class="li"]/a/text()')
    返回值为空,因为这里HTML文本中li节点为class属性有2个值li和li-first,如果还用之前的属性匹配就不行了,需要用contain()函数
     
    正确方法如下
    result5 = response.xpath('//li[contains(@class, "li")]/a/text()')
    contains()方法中,第一个参数为属性名,第二个参数传入属性值,只要此属性名包含所传入的属性值就可完成匹配
    复制代码
     
     7. 多属性匹配,这里说一下不用框架的时候,xpath的常规用法
    有时候我们需要多个属性来确定一个节点,那么就需要同时匹配多个属性,可用and来连接
    复制代码
    from lxml import etree
    text = '''
    <li class = "li li-first" name="item"><a href="link.html">first item</a></li>
    '''
    html = etree.HTML(text)
    result6 = html.xpath('//li[contains(@class, "li") and @name="item"]/a/text()')
    print(result)
    复制代码
    这里的li节点有class和name两个属性,需要用and操作符相连,然后置于中括号内进行条件筛选
     
     
     
     
     
    二. 调试命令
    cmd中执行如下代码,即可进入调试命令行,这个命令已经取得了页面中的原代码,命令测试成功后即可放在def parse函数中
     
    开始调试,
    1. 取得文章标题
    复制代码
    >>> title = response.xpath('//div[@class="entry-header"]/h1/text()')
    >>> title
    [<Selector xpath='//div[@class="entry-header"]/h1/text()' data='2016 腾讯软件开发面试题(部分)'>]
    >>> title.extract()
    ['2016 腾讯软件开发面试题(部分)']
    >>> title.extract()[0]
    '2016 腾讯软件开发面试题(部分)'
    >>> title.extract_first()
    '2016 腾讯软件开发面试题(部分)'
    复制代码
    说明
    1)extract()方法会把原数据的selector类型转变为列表类型
    2)extract()会得到多个值,extract()[1]取第2个值
    3)extract_first()得到第一个值,类型为字符串。extract_first(default='')如果没取到返回默认值
     
     
    2. 取得发表日期
    >>> response.xpath("//p[@class='entry-meta-hide-on-mobile']/text()").extract()[0].strip().replace("·","").strip()
    '2017/02/18'
     
    3. 点赞数,span标签里有很多class名,选一个看起来像唯一的,测试一下,然后用contains()函数简化操作
    >>> response.xpath("//span[contains(@class, 'vote-post-up')]/h10/text()").extract()
    ['2']
    >>> response.xpath("//span[contains(@class, 'vote-post-up')]/h10/text()").extract()[0]
    '2'
    >>> int(response.xpath("//span[contains(@class, 'vote-post-up')]/h10/text()").extract()[0])
    2
     
    4. 收藏数,要用正则,re模块也是scrapy的内置模块,注意要用非贪婪匹配,否则只会取到8
    复制代码
    >>> response.xpath("//span[contains(@class, 'bookmark-btn')]/text()").extract()[0]
    ' 28 收藏'
    >>> string = response.xpath("//span[contains(@class, 'bookmark-btn')]/text()").extract()[0]
    >>> import re
    >>> pattern = re.match(".*?(d+).*", string)
    >>> pattern.group(1)
    '28'
    复制代码

     可以简写为

    >>> response.xpath("//span[contains(@class, 'bookmark-btn')]/text()").re('.*?(d+).*')
    ['28']
    >>> response.xpath("//span[contains(@class, 'bookmark-btn')]/text()").re('.*?(d+).*')[0]
    '28'
    5. 使用列表推导式取得一个标签中的部分元素,如下取得职场和面试字样。适用于有些文章没评论标签的情况
    复制代码
    找到不是以"评论"结尾的元素
    >>> response.xpath("//p[@class='entry-meta-hide-on-mobile']/a/text()").extract()
    ['职场', ' 9 评论 ', '面试']
    >>> tag_list = response.xpath("//p[@class='entry-meta-hide-on-mobile']/a/text()").extract()
    >>> [element for element in tag_list if not element.strip().endswith("评论")]
    ['职场', '面试']
    >>> tag_choose=[element for element in tag_list if not element.strip().endswith("评论")]
    >>> tags=",".join(tag_choose)
    >>> tags
    '职场,面试'
    复制代码
    join()函数基本语法: 'sep'.join(seq)。表示以sep为分隔符,将seq中所有的元素合并成一个新的字符串
    sep表示分隔符,可以为空;
    seq表示要连接的数据,数据类型可以是列表,字符串,元组或者字典
     
     
     
     
    三. css提取方式
     
    1. css的几个选择器
     
    li a 
    选取所有li下的所有a节点
    ul + p
    选择ul后面的第一个p元素,ul和p是兄弟节点
    div#container>ul
    选取id为container的div标签,下边的第一个ul子元素
    ul ~ p 
    选取与ul相邻的所有p元素
    a[title] 
    选取所有含有title属性的a元素
    a::attr(href)
    获取所有a元素的href属性值
    a[href="http://jobbole.com"] 
    选取所有href属性为http://jobbole.com值的a元素
    a[href*="jobble"]  
    选取所有href属性包含jobbole的a元素
    a[href^="http"]
    选取所有href属性值以http开头的a元素
    a[href$=".jpg"]  
    选取所有href属性值以.jpg结尾的a元素
    input[type=radio]:checked      
    选择选中的radio的元素
    div:not(#container) 
    选取所有id不等于container的div元素
    li:nth-child(3)     
    选取第三个li元素
    tr:nth-child(2n)      
    选取偶数位的tr元素
     
     
     
    2. scrapy shell中使用css来提取数据
     
    1)提取标题,需要用到css的伪类 ::text
    >>> response.css(".entry-header h1").extract()
    ['<h1>2016 腾讯软件开发面试题(部分)</h1>']
    >>> response.css(".entry-header h1::text").extract()[0]
    '2016 腾讯软件开发面试题(部分)'
     
    2)文章创建时间
    >>> response.css("p.entry-meta-hide-on-mobile::text").extract()[0].strip().replace(" ·","")
    '2017/02/18'
    注意:这里p和类名之间没空格,表示类名为entry-meta-hide-on-mobile的p元素
     
     
    3)点赞数,对于属性多值匹配用css会很方便
    >>> response.css(".vote-post-up h10::text").extract()[0]
    '2'
     
    4) 收藏数,注意转义字符的方向
    >>> response.css(".bookmark-btn::text").extract()[0]
    ' 28 收藏'
    >>> string = response.css(".bookmark-btn::text").extract()[0]
    >>> tag=re.match(".*?(d+).*", string)
    >>> tag.group(1)
    '28'

    其实正则re也是scrapy的内置模块,可以简写为如下

    >>> response.css(".bookmark-btn::text").re('.*?(d+).*')
    ['28']
    >>> response.css(".bookmark-btn::text").re('.*?(d+).*')[0]
    '28'
     
    5) 提取正文内容,一般把格式也取出来
    response.css("div.entry").extract()[0]
     
    6)  取得职场,评论,面试字样
    >>> response.css("p.entry-meta-hide-on-mobile a::text").extract()
    ['职场', ' 9 评论 ', '面试']
  • 相关阅读:
    【动植物研究动态】20220403文献解读
    【工具】Web Scraper 网页爬取全国4000家种子企业信息
    Codeforces Round #777 (Div. 2)
    Codeforces Round #775 (Div. 1)
    Codeforces Round #779 (Div. 2)
    Educational Codeforces Round 125
    Codeforces Round #781 (Div. 2)
    Codeforces Round #778 (Div. 1 + Div. 2)
    Codeforces 参赛题解集合
    线程同步Condition
  • 原文地址:https://www.cnblogs.com/yunlongaimeng/p/11526418.html
Copyright © 2020-2023  润新知