大数据(二)数据扒取 1
每日一骚:
第一想问题要有目的性,紧紧盯着目标,如果没目标先想清楚目标,记不住就写下来。
第二把简单的东西,说清楚作用和用法。
第三把复杂的东西,原理说清楚 用法记清楚。概括地说清给别人,具体的步骤你自己记住就好。
通过玩剧本杀的游戏,我发现自己缺乏概括能力。总是喜欢复制别人的话,接下来我都用我自己的话。
简单来说,概括就是把修辞去掉 把不是核心的东西去掉,让别人听个最主要的大概就好了。
比如说优势就是性能高 功能丰富,这就够了,不要啰里啰嗦一大堆。
——————————————————————————————————————————————————————
数据爬取的学习目标:
1.学会用python爬数据,包括它的原理和方法
2.学会用LXML Python库来进行Web数据爬取
3.学会用Scrapy进行Web数据爬取(最重点)
4.了解使用PySpider服务进行Web数据爬取
2 3 4三种方法 各有优势
——————————————————————————————————————————————————————
1.安装
pip install lxml
或者anaconda安装
conda install -c anaconda lxml
2.lxml树状结构
(1)lxml是什么
是什么:lxml是python的一个xml解析库,支持HTML和XML的解析,更支持XPath解析方式,效率极高。
优势:比Python自带的xml库性能高、功能丰富,第三方库 lxml 是用 Cython 实现的,可谓爬虫处理网页数据的一件利器。lxml 大部分功能都存在 lxml.etree
中,所以下文都假定已经执行了
from lxml import etree
XPath(XML Path Language):XML路径语言,用于在XML和H搜索TML中信息,它的一切表达式和函数都是为了帮助我们定位想要的节点。
(2)lxml结构介绍
etree:基类树,所有方法几乎都在这个类里。
ElementTree:元素树,可以直接操作元素
Element :元素
先扫盲一下:
字符串在Python内部的表示是unicode编码,因此,在做编码转换时,通常需要以unicode作为中间编码,即先将其他编码的字符串解码(decode)成unicode,再从unicode编码(encode)成另一种编码。
encode:从unicode变成其他编码
decode:从其他编码解码成unicode
机器用的都是unicode编码,但是传送起来utf-8编码效率更高。pretty_print = True指的是以人类可读的树状结构方式打印
拿到一个网页之后,我们需要把网页字符串转换一下,拿到它的根节点。
可以用etree的这三种方法转换:
(1) html = etree.fromstring("<html>...<html>") //解析字符串
(2)html = etree.HTML("<html> ... <html>") //解析HTML对象
(3) html = etree.parse(file_name) //解析文件对象
这块一定要去敲代码,并且找官网英文文档来看,否则会看的云里雾里。因为你不知道每个方法究竟是干嘛的。
我就不想说官网写的有多好了,一定看官方英文教程,对着敲一遍然后再总结。
去官方教程:https://lxml.de/tutorial.html
下面我对官方的教程做一下解释:
1. Element类
Element是ElmentTree API里面最重要的一个容器对象了,大部分的XML树的功能
都是这个类来提供。
添加一个节点非常简单:
>>> root = etree.Element("root")
打印一下它自己本身的tag:
>>> print(root.tag)
root
添加一个子节点(2种方法):
(1) 父节点 append子节点
>>> root.append( etree.Element("child1") )
(2)etree提供的方法更直接简单
参数和前者一样,都是子节点,但还需要以父节点作为第一个参数。
>>> child2 = etree.SubElement(root, "child2")
>>> child3 = etree.SubElement(root, "child3")
以树形结构来浏览一个Element
>>> print(etree.tostring(root, pretty_print=True))
<root>
<child1/>
<child2/>
<child3/>
</root>
2.Elements数组
为了能快速直接的访问子元素,应该尽可能以python数组的方式去访问。
以下例子基本和python的list没区别。
>>> child = root[0]
>>> print(child.tag)
child1
>>> print(len(root))
3
>>> root.index(root[1]) # lxml.etree only! //获取root第一个子元素在root里的索引
1
>>> children = list(root) //不转list也是可以遍历的
>>> for child in root:
... print(child.tag)
child1
child2
child3
>>> root.insert(0, etree.Element("child0")) //为第一个子元素插入child0
>>> start = root[:1]
>>> end = root[-1:]
>>> print(start[0].tag)
child0
>>> print(end[0].tag)
child3
查看root是否为xml文件中的一个节点:
>>> print(etree.iselement(root)) # test if it's some kind of Element
True
查看是否有子节点:
>>> if len(root): # test if it has children
... print("The root element has children")
The root element has children
3.lxml赋值和拷贝(新版本不会以拷贝的方式来赋值)
现在的新版本如果你赋值,比如 root[0] = root[3],root[3]会直接移动到0位置,直接替换。
而不是拷贝之后再替换。这样的好处我个人理解是,不会把这个节点搞得到处都是,这样这个节点就只有一个父结点。
可以通过getparent()查询到:
>>> root is root[0].getparent() # lxml.etree only!
True
如果真的想拷贝方式来替换:
可以用copy模块的deepcopy来做
>>> from copy import deepcopy
>>> element = etree.Element("neu") //创建neu节点
>>> element.append( deepcopy(root[1]) ) 把root的第2个子元素拷贝添加到neu下面
>>> print(element[0].tag) //打印neu的第一个子元素,果然是child1
child1
>>> print([ c.tag for c in root ]) //然后再看下root里面child1还在,是拷贝过去的
['child3', 'child1', 'child2']
节点的邻居查询:
>>> root[0] is root[1].getprevious() # lxml.etree only! 我是你的上家
True
>>> root[1] is root[0].getnext() # lxml.etree only! 我是你的下家
True
4. Elements的属性
>>> root = etree.Element("root", interesting="totally") //带着属性创建节点
>>> etree.tostring(root) //打印root节点里的所有内容
b'<root interesting="totally"/>'
属性只是无序的K-V对罢了,所以我们可以通过字典来处理他们。
>>> print(root.get("interesting"))
totally
// root节点 set设置属性的值 get获取值
>>> print(root.get("hello"))
None
>>> root.set("hello", "Huhu")
>>> print(root.get("hello"))
Huhu
>>> sorted(root.keys())
['hello', 'interesting']
//对root的k v进行排序
%s表示转成字符串 string
%r表示显示原始数据,raw data
【注意我说的只是%s和%r这一段,与其他部分无关。】
如果是int类型,两者显示一样。
如果是字符串这种的,%s就把其他符号去掉只显示字符串,%r就把什么符号都继续带上。
例子:
>>> print '%s, %s'%('one', 'two')one, two
>>> print '%r, %r'%('one', 'two')'one', 'two'
%表示把name和value和前面的%s %r对应上
>>> for name, value in sorted(root.items()):
... print('%s = %r' % (name, value))
hello = 'Huhu'
interesting = 'totally'
——————————————————————————————————————————
lxml先学到这里,虽然后面内容老多了,但是先简单了解下即可。
————————————————————————————————————————
5. XPath用法
XPath可以轻易帮你定位和搜索到一个节点。
用法:
len(html.xpath( ' /htiml/* ' )) //表示html下有几个子节点
2个
html.xpath(‘/html/body/@class’)[0] //表示查找body下面的class属性,返回的属性是集合,取集合第一个元素。
取到body那就是body自己这一个元素了。
最后一个例子 中括号里表示条件 我匹配一切【local-name等于body】的元素。
.//中的"."代表相对路径 // 两个杠代表中间可以有任意的其他东西
a代表元素,中括号表示条件
拿到属性有两种方法,get(' ')或者attrib[' ']
添加子节点的时候,class是python的关键字,那怎么添加class属性呢:
你直接用class=属性 这种格式机器肯定不认得,所以需要用字典的方式传进去
一个xml元素可以有好几个属性:
CSSSelector:
用CSSS来定位元素,可以定位到body~
在body这个元素的基础上在用xpath,这时候就用到了 ./ 的相对路径
./ 当前目录 ../上级目录 都是相对路径
python的css3选择器
上述的都是把网页文件变成一个xml文档来load进来,做的练习。
实际我们面对的是网页,从网页上我们当然拿一个request
现在做一个从yahoo上抓数据的demo:
注意cssselect是一个单独的模块,从lxml中提取出来了。还需要先pip install cssselect才能使用。
%代表的是,左边放一个要格式化的字符串,右边放上你要格式化的值。你可以用%s占位,然后最后定义一下。
%s代表字符,%d代表数字,r%也代表字符 但是显示原始数据。
例如
"hello,%s" % "zhangsan" --》"hello,zhang3"
//用zhangsan格式化%s
"d%"%3-->用3代替d%
"$s:%d" %("ab",3) // 用ab代替%s,用3代替%d
定义了一下网页,为了用着方便我用占位符%s:
我拿到他的页面的text,然后verify我不做验证。
对上图邮件,选择view sourece我们去了解下源代码,不了解页面结构怎么抓数据呢?
示范一下:
注意xpath返回的都是list,发现抓到的是两个div元素
也可以对div这个元素自己的文本内容 i这个元素就只有自己一个 ///这块弄得不是太明白
//用lxm.html把抓到的网页的内容load进来,成为一个xml文档。
然后通过xpath来找,这一串是啥意思呢
首先是htm--body body下面//说明可以有很多个东西,然后找一个div,什么样的div呢,中括号表示条件,属性data-test里包含"summary-table"的div。最后还可以中括号之后补个 //tr,表示中间可以有很多东西,summary_rows是从//tr开始。
那你抓到的就是一堆tr标签。因为最后是//tr
再看一个json的例子
这是一个新的例子,但是方法和上面的大同小异,要和元素结构对应上。
json的那个url访问不了,所以我现在就把它看懂就好。