从网页中提取特征量
本文主要介绍了Scrapy常用的数据提取的方法,包括seletors、scrapy shell、xpath、css、BeautifulSoup的使用方法及案例。只要掌握xpath或者css任意一种即可。
1、seletors选择器
1)介绍
Scrapy提取数据有自己的一套机制。它们被称作选择器(seletors),构建于lxml库之上,通过特定的XPath或者CSS表达式来等“选择” HTML文件中的某个部分。Selector有四个基本的方法,最常用的还是xpath:
-
XPath(query):传入Xpath表达式queery,返回该表达式对应的所有节点的selector list。Xpath是一门在 XML 文档中查找信息的语言。XPath 可用来在 XML 文档中对元素和属性进行遍历。
-
css(query):传入css表达式query,返回该表达式所对应的所有节点的selector list列表CSS是将HTML文档样式化的语言。选择器由它定义,并与特定的HTML元素的样式相关联。
-
re(): 根据传入的正则表达式对数据进行提取,返回Unicode字符串list列表
-
extract(): 序列化该节点为Unicode字符串并返回list
2)导入seletors
from scrapy.selector import Selector, HtmlXPathSelector
from scrapy.http import HtmlResponse
3)常用xpath使用方法:
常用语法:
-
/:根节点
-
//xxx/zzz:路径
-
//div:去全局的子孙中找所有的div元素
-
.//表示去当前对象的子孙中找
-
/xxx//div:去儿子对象中找xxx节点下所有的div元素
-
*:匹配任意节点元素
-
/html/body/div[1]:选取body下的第一个div节点
-
//div[@class="xxx"]:选取class属性为xxx的div节点
-
//@attr:获取attr对应的值
常用函数:
-
text():提取文本信息,//*[@class='xxx']/text()
-
position():选取第几个节点,//*[position()=1]
-
last():选取最后一个节点,//*[last()]
-
starts-with(@attr,substr):attr属性值开头substr的节点
-
contains(@attr,substr):attr属性值是否包含substr
运算符:
-
|:计算两个节点集合,比如//div|//li
-
比较运算符=,!=,<=...,比如//price[text()>10]
-
算数运算符:+,-,*,div,比如//price[text()+1]
-
逻辑运算符:or,and,[@class='xxxx' or @class='yyyy']
4)xpath语法案例:以取a标签为案例,div、span等都相同
-
xpath('//a'):找到全局中所有的a标签
-
xpath('//a/text() '):获取a标签的文本值
-
xpath('//a/span'):a标签对象儿子下的span标签
-
xpath('//a[2]'):a标签找到后,返回的是一个列表,[n]列表的n位置的值
-
xpath('//a[@id]'):找全局中属性为id的a标签
-
xpath('//a[@id="i1"]'):找全局中属性id为i1的a标签
-
xpath('//a/@id'):取a标签的id的属性值
-
xpath('//a[@href="link.html"][@id="i1"]'):两个[]代表双条件,需要href="link.html“,且id="i1"的a标签
-
xpath('//a[contains(@href, "link")]'):a标签的href需要包含"link"字符
-
xpath('//a[starts-with(@href, "link")]'):a标签的href需要以"link"开头
-
xpath('//a[re:test(@id, "i\d+")]'):正则表达式的写法,需要有re:test
-
xpath('//a[re:test(@id, "i\d+")]/@href').extract(),正则表达式的写法案例
-
xpath(.//)在当前基础上往下找,需要加“.”,比如用在for循环中
-
obj.extract()#列表中的每一个对象转化字符串==>返回一个列表
-
obj.extract_first() #列表中的每一个对象转化字符==>列表中的第一个元素
-
xpath('/html/body/ul/li/a/@href').extract():一层层去找标签
5)获取代码,需要在页面,右键-----检查元素---去看下html结构。
也可以直接选择自己想要的内容,通过copy---copy xpath直接获得源代码,去粘贴即可。
比如选取下面页面的肖申克救赎的信息:但一般这种路径都会特别长。
//*[@id="content"]/div/div[1]/ol/li[1]/div/div[2]/div[1]/a/span[1]
6)css语法
基本语法:
-
.classvalue:选择class属性为classvalue的元素
-
#idvalue:选择id值为idvalue的元素
-
p:选择所有的p元素
-
div,p:选择所有的div和p元素
-
div~p:前面有div元素的每个p元素,div和p为兄弟节点
-
li span :选择li内部所有的span元素
-
div>p:选择父结点为div的所有p元素
-
::text/::attr(src):文本信息/提取某个属性的值,比如a::attr(href),提取超链接
属性的语法:
-
[attr]选择所有带attr属性的元素
-
[attr=val]选择attr值为val的所有元素:[numvalue=num1]
-
[attr^=val]选择attr属性值以val开头的元素:[src^=http]
-
[attr$=val]选择attr属性值以val结尾的元素:[src^=.jpg]
-
[attr*=val]选择attr属性值包含val的元素:[src*=douban]
ele语法:
-
ele:first-of-type:所有子元素为ele的第一个元素
-
ele:first-child:第一个元素为ele的元素
-
ele:only-of-type:类型为ele的元素
-
ele:only-child:唯一子元素的每个ele元素
-
ele:nth-of-type(n):选择第n个ele元素
-
ele:nth-child(n):第n个子元素为ele的元素
-
ele:last-child:最后一个为ele的子元素
2、Scrapy shell交互,测试网页代码
Scrapy终端是一个交互终端shell,我们可以在未启动spider的情况下尝试及调试代码,也可以用来测试XPath或CSS表达式,查看他们的工作方式,方便我们爬取的网页中提取的数据。一旦你习惯使用了Scrapy shell,你将会发现Scrapy shell对于开发爬虫是非常好用的一个测试工具。
1)启动Scrapy shell,有三种方式请求url
1-1直接请求url:
首先需要cmd-----进入项目根目录,然后填写"scrapy shell +url(你需要访问的页面地址) ",如下所示,即可返回信息。
会看到如下的信息
比如输入:response.xpath('//title')可以看到如下信息:
Scrapy Shell根据下载的页面会自动创建一些方便使用的对象,例如Response对象,以及Selector对象(对HTML及XML内容)。当shell载入后,将得到一个包含response数据的本地 response 变量。
-
输入response.body将输出response的包体。
-
输入response.headers可以看到response的包头。
-
输入response.selector时,将获取到一个response初始化的类Selector的对象,此时可以通过使用response.selector.xpath()或response.selector.css()来对response进行查询。
-
Scrapy也提供了一些快捷方式, 例如 response.xpath()或response.css()同样可以生效。
1-2请求url设置请求信息,使用:scrapy shell -s name=value语句
比如直接设置代理USER_AGENT的语句:
1-3 在命令行直接请求url,利用fetch。
req=Request(url,header={})
r=fetch(url)
然后就可以看到response.url的信息了。
2)测试xpath的使用语法:以豆瓣TOP250为例。
-
比如选取html:response.xpath('/html')
-
提取网页的title:response.xpath('/html/head/title')
-
找到所有的a标签,或找到body下所有的a标签:response.xpath('//body//a')
-
找到body下所有的标签
-
text()获取电影名称:response.xpath('//div[@class="hd"]//a/span[1]/text()').extract()
-
获取所有的电影的连接:response.xpath('//div[@class="hd"]//a/@href').extract()
-
提取所有的电影评分:response.xpath('//span[@class="rating_num"]/text()').extract()
-
提取所有的电影评分,且限制评分。response.xpath('//span[@class="rating_num" and text()>9.4]/text()').extract()
-
提取页码:response.xpath('//span[@class="next"]//a/@href').extract()
3)测试CSS的使用案例:以豆瓣TOP250为例。
-
比如选取html:response.css('html')
-
提取网页的title:response.css('html head title')
-
获取body下所有的a :response.css('body a')
-
text()获取所有的电影名称:response.css('.title::text')
根据文档结构,可以写成如下格式:response.css('.title::text')
-
也可以使用ele语法获取电影名称:response.css("[class='hd'] a span:first-of-type").extract()
-
获取所有的电影的连接:response.css('.hd a::attr(href)').extract()
-
提取所有的电影的评分:response.css('.rating_num::text').extract()
-
提取图片:response.css("[src$=jpg]").extract()
附录::
附1、BeautifulSoup
BeautifulSoup是一个模块,该模块用于接收一个HTML或XML字符串,然后将其进行格式化,之后就可以使用他提供的方法进行快速查找指定元素,从而使得在HTML或XML中查找指定元素变得简单。
四大对象种类
Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种:
-
Tag:HTML 中的一个个标签,比如a,div等,有两个属性name和attrs
-
NavigableString:标签内部的文字,可以用 .string获得
-
BeautifulSoup:表示的是一个文档的全部内容
-
Comment:注释
1)模块的安装(需要cmd,进入到python的工作目录)
pip3 install beautifulsoup4
2)导入模块
from bs4 import BeautifulSoup
3)创建对象
soup=BeautifulSoup(response.text,'html.parser')
4)使用语法:
-
soup.find('a'):获取匹配的第一个a标签,a可以改成span、div等
-
soup.find_all('a'):获取所有匹配的a标签,是列表形式
-
soup.find_all(id='xx'):查找属性id为xx的所有节点
-
soup.find_all(div,class_='xx'):查找class属性为xx的所有div节点
-
soup.find_all(a,text='xx'):查找text值为xx的所有a节点
-
soup.find_all(a,text=re.complite('xx')):正则查找text值为xx的所有a节点
-
soup.find('a').has_attr('id'),其中has_attr,检查标签是否具有该属性
-
soup.find('body').children:找到所有子标签
-
soup.find('body').descendants:找到所有的子子孙孙标签
-
soup.find('body').clear(),将标签的所有子标签全部清空(保留标签名)
-
soup.find('body').decompose():decompose,递归的删除所有的标签
-
soup.find('body')..extract():解析标签
-
soup.find('body').decode(),soup.find('body').decode_contents(),decode,转换为字符串(含当前标签);decode_contents(不含当前标签)
-
soup.find_all('a',limit=1),只拿第一个a标签
-
soup.find_all(name='a', attrs={'class': 'sister'}, recursive=True, text='Lacie')限制属性;等同于soup.find(name='a', class_='sister', recursive=True, text='Lacie');recursive=True代表查找所有的节点,recursive=False代表只查找本节点下的东西
-
soup.find_all(name=['a','div']),限制选取全部的a、div标签
-
soup.find_all(href=['link1','link2']),选取href为link11、link2的标签
-
soup.find_all(text=['Tillie']),text为Tillie的所有节点
-
soup.find_all(class_=['sister0', 'sister']),限制class=sister0、sister
-
正则表达式rep = re.compile('sister.*') ,v = soup.find_all(class_=rep)
-
soup.find('a').get_text,其中get_text获取标签内部文本内容
-
soup.find('br').is_empty_element,其中is_empty_element,是否是空标签(是否可以是空)或者自闭合标签,判断是否是如下标签:'br' , 'hr', 'input', 'img', 'meta','spacer', 'link', 'frame'。
-
soup.strings,获取多个标签内容
-
list(soup.stripped_strings),可以去除多余空白内容
-
append在当前标签内部追加一个标签
5)使用soup.select()通过css进行筛选:
可参考css部分知识,仅举部分案例
-
soup.select('a'):获取匹配的a标签
-
soup.select('div a'):按层级获取div下的a标签
-
soup.select('.css1'):查找属性值为css1的标签
-
soup.select('#id1'):查找id属性值为#id1的标签
-
soup.select('div[class]'):查找存在某个属性值的标签
-
soup.select('div>p'):选择父结点为div的所有p元素
-
soup.select('div~p'):前面有div元素的每个p元素,div和p为兄弟节点
-
soup.select('attr^=val'):属性attr以val开头的标签
具体可参考:
http://www.cnblogs.com/wupeiqi/articles/6283017.html
5)使用案例:
from bs4 import BeautifulSoup
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
asdf
<div class="title">
<b>The Dormouse's story总共</b>
<h1>f</h1>
</div>
<div class="story">Once upon a time there were three little sisters; and their names were
<a class="sister0" id="link1">Els<span>f</span>ie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</div>
ad<br/>sf
<p class="story">...</p>
</body>
</html>
"""
#封装使用案例:
soup = BeautifulSoup(html_doc, features="lxml")
tag1 = soup.find(name='a')
print('找到第一个a标签',tag1)
tag2 = soup.find_all(name='a')
print('找到所有的a标签',tag2)
tag3 = soup.select('#link2')
print('找到id=link2的标签',tag3)
执行效果:
附2、XPath
XPath 是一门在 XML 文档中查找信息的语言。XPath用于在 XML文档中通过元素和属性进行导航。
1、节点介绍:
在 XPath 中,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档节点(或称为根节点)。
如下例:
上面的XML文档中的节点例子:
<bookstore> (文档节点)
<author>J K. Rowling</author> (元素节点)
lang="en" (属性节点)
2、节点关系:
-
父(Parent):每个元素以及属性都有一个父。
在下面的例子中,book 元素是 title、author、year 以及 price 元素的父:
-
子(Children):元素节点可有零个、一个或多个子。
在下面的例子中,title、author、year 以及 price 元素都是 book 元素的子:
-
同胞(Sibling):拥有相同的父的节点
在下面的例子中,title、author、year 以及 price 元素都是同胞:
在下面的例子中,title 元素的先辈是 book 元素和 bookstore 元素:
在下面的例子中,bookstore 的后代是 book、title、author、year 以及 price 元素:
3、XPath 语法
1)常用表达式:
以下例看使用案例:
-
xpath(‘bookstore’) 选取 bookstore元素的所有子节点。
-
xpath(‘/bookstore’),选取根元素
-
xpath(‘bookstore/book’),选取属于 bookstore 的子元素的所有 book 元素。
-
xpath(‘//book’),全局中找到所有的book元素,而不管它们在文档中的位置。
-
xpath(‘bookstore//book’) 选择属于bookstore元素的后代的所有 book 元素,而不管它们位于bookstore之下的什么位置。
-
xpath(‘//@lang’) 选取名为 lang 的所有属性。
-
xpath(‘ /bookstore/*’) 选取 bookstore 元素的所有子元素。
-
xpath(‘ //*’) 选取文档中的所有元素。
-
xpath(‘ //title[@*]’) 选取所有带有属性的 title 元素。
通过在路径表达式中使用“|”运算符,您可以选取若干个路径。
-
//book/title | //book/price 选取 book 元素的所有 title 和 price 元素。
-
//title | //price 选取文档中的所有 title 和 price 元素。
-
/bookstore/book/title | //price 选取属于 bookstore元素的book元素的所有title元素,以及文档中所有的price元素。
2)谓语
谓语用来查找某个特定的节点或者包含某个指定的值的节点。谓语被嵌在方括号中。
比如以下用法: