大数据(二)数据扒取 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 :元素

大数据(二)数据扒取 1

 

先扫盲一下:

字符串在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】的元素。

大数据(二)数据扒取 1

 

 

.//中的"."代表相对路径    // 两个杠代表中间可以有任意的其他东西

a代表元素,中括号表示条件

大数据(二)数据扒取 1

拿到属性有两种方法,get('  ')或者attrib['  ']

大数据(二)数据扒取 1

 

添加子节点的时候,class是python的关键字,那怎么添加class属性呢:

你直接用class=属性   这种格式机器肯定不认得,所以需要用字典的方式传进去

一个xml元素可以有好几个属性:

大数据(二)数据扒取 1

 

CSSSelector:

用CSSS来定位元素,可以定位到body~

在body这个元素的基础上在用xpath,这时候就用到了 ./ 的相对路径

大数据(二)数据扒取 1

 

./  当前目录  ../上级目录  都是相对路径

大数据(二)数据扒取 1

python的css3选择器

 

 

 

 

 

上述的都是把网页文件变成一个xml文档来load进来,做的练习。

实际我们面对的是网页,从网页上我们当然拿一个request

大数据(二)数据扒取 1

 

现在做一个从yahoo上抓数据的demo:

注意cssselect是一个单独的模块,从lxml中提取出来了。还需要先pip install cssselect才能使用。

大数据(二)数据扒取 1

 

 

%代表的是,左边放一个要格式化的字符串,右边放上你要格式化的值。你可以用%s占位,然后最后定义一下。

%s代表字符,%d代表数字,r%也代表字符 但是显示原始数据。

例如 

"hello,%s" % "zhangsan"   --》"hello,zhang3"

//用zhangsan格式化%s

 

"d%"%3-->用3代替d%

 

"$s:%d" %("ab",3)  // 用ab代替%s,用3代替%d

 

定义了一下网页,为了用着方便我用占位符%s:

大数据(二)数据扒取 1

我拿到他的页面的text,然后verify我不做验证。

大数据(二)数据扒取 1

对上图邮件,选择view sourece我们去了解下源代码,不了解页面结构怎么抓数据呢?

大数据(二)数据扒取 1

 

示范一下:

注意xpath返回的都是list,发现抓到的是两个div元素

大数据(二)数据扒取 1

也可以对div这个元素自己的文本内容   i这个元素就只有自己一个 ///这块弄得不是太明白

大数据(二)数据扒取 1

 

//用lxm.html把抓到的网页的内容load进来,成为一个xml文档。

然后通过xpath来找,这一串是啥意思呢

首先是htm--body   body下面//说明可以有很多个东西,然后找一个div,什么样的div呢,中括号表示条件,属性data-test里包含"summary-table"的div。最后还可以中括号之后补个 //tr,表示中间可以有很多东西,summary_rows是从//tr开始。

那你抓到的就是一堆tr标签。因为最后是//tr

 

再看一个json的例子

这是一个新的例子,但是方法和上面的大同小异,要和元素结构对应上。

json的那个url访问不了,所以我现在就把它看懂就好。