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函数中

scrapy shell http://blog.jobbole.com/110287

开始调试,

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来提取数据

scrapy shell http://blog.jobbole.com/110287

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 评论 ‘, ‘面试‘]

原文地址:https://www.cnblogs.com/yunlongaimeng/p/11526418.html

时间: 2024-08-02 16:40:41

scrapy实战,使用内置的xpath,re和css提取值的相关文章

vue.js实战学习——内置指令(一)

注:此内容摘抄自:梁灏的<Vue.js实战> 注:记得要引入vue.js才能运行哦,文章中贴出的代码直接复制是不行的,html css js 都放在了一起,而且也没有引用vue.js.   1.v-cloak v-cloak不需要表达式,它会在Vue实例结束编译时从绑定的HTML元素上移除,经常和CSS的display:none: 配合使用. <div class="app1" v-cloak> {{message}} </div> var app1

shell实战:内置(built-in)变量

shell领域内,变量的作用不言而喻. 内置变量,是不需要在程序中声明,就自动存在的,随时可以访问. Shell 的环境变量分为 set, env 两种,其中 set 变量可以通过 export 工具导入到 env 变量中.其中,set 是显示设置shell变量,仅在本 shell 中有效:env 是显示设置用户环境变量 ,仅在当前会话中有效.换句话说,set 变量里包含了 env 变量,但 set 变量不一定都是 env 变量.这两种变量不同之处在于变量的作用域不同.显然,env 变量的作用域

linux shell字符串内置的常用操作(获取长度、查找、替换)

在编写shell程序时,经常会涉及到字符串相关操作.有许多命令语句,如awk,sed都能够做字符串各种操作. 事实上shell内置一系列操作符号,能够达到相似效果,使用内部操作符会省略启动外部程序等时间,因此速度会很的快.如果内置的操作符能够做到的,优先使用内置的. 1 推断读取字符串值                                        

内置函数(二)

1.4.2和数据结构相关(24) 列表和元祖(2) list:将一个可迭代对象转化成列表(如果是字典,默认将key作为列表的元素). 1 dic ={1:2,3:4} 2 print(list(dic)) tuple:将一个可迭代对象转化成元祖(如果是字典,默认将key作为元祖的元素)字典转换成元祖 1 dic1={'a':2,3:4} 2 tu1=tuple(dic1) 3 print(tu1) 4 dic2 = dict([tu1]) 5 print(dic2) 字符串转换成元祖 1 str

内置函数2

# 元祖和列表# list:将一个可迭代对象转化成列表,(如果是字典,默认将key作为列表的元素)# tuple:将一个可迭代对象转化成元祖,(如果是字典,默认将key作为元祖的元素)# reversed:将一个序列翻转,并返回此翻转序列的迭代器. # li=[1,6,2,9,4,5] # reversed(li) # print(reversed(li)) # for i in reversed(li): # print(i) # # s1='sgdshh' # for i in revers

字符串,列表,元祖,字典,集合的内置方法

一. 数字类型 1.1 整型int   1.用途: 记录年龄\等级\各种号码 2.定义方式: age=18 age=int(18) x=int('123') #只能将纯数字的字符串转换成整型 print(type(x)) print(int(3.7)) #这个小数部分没有了 3.常用操作+内置的方法           ( 赋值\比较\算术) 该类型总结:   存一个值  ;  不可变(1.可变:值变,id不变.可变==不可hash 2.不可变:值变,id就变.不可变==可hash) 判断是否哈

str类型内置方法

目录 str类型内置方法 用途 定义方式 常用操作和内置方法 优先掌握 需要掌握 了解 存一个值or多个值 有序or无序 可变or不可变 强化训练 str类型内置方法 用途 字符串数字.字母.下划线组成的一串字符,主要是用来描述一些类似于名字,爱好-- 定义方式 在单引号.双引号.三单/双引号之间 name1 = 'hades' # 单引号 name2 = "bonnie" # 双引号 name3 = '''hades and bonnie''' # 三单引号 name4 = &quo

字符串类型内置方法

# 字符串类型内置方法 # 字符串类型 ## 用途:名字,兴趣,爱好 ## 定义方式 ```pythonname1 = 'nick'name2 = "nick"name3 = """nick"""name4 = '''nick'''``` # 常用操作和内置方法 ## 优先掌握 ### 按索引取值 ```python#1 按索引取值zhuping_info_list = ['zhuping',190,140,["pi

数据类型内置方法:列表/元组/集合

list列表类型内置方法 作用:多个装备.多个爱好.多门课程,甚至是多个女朋友 定义方式:[]内用逗号隔开,可以有多个任意数据类型的元素 friends_list = ['longzeluola','canglaoshi','qiaobenai','nick'] lis = list('abcd') 方法: 优先掌握 索引取值(正向取值+反向取值) 即可取又可存 #list之索引取值 name_list={'nick','jason','tank','sean'} name_list[0]='n