页面数据解析
一、数据的结构
1. 结构化数据
关系模型数据,即可以使用关系型数据库表示和存储,表现为二维形式的数据
2. 半结构化数据
半结构化数据是结构化数据的一种形式,它介于完全结构化(如:关系型数据库、面向对象数据库中的数据)和完全无结构的数据(如:声音、图像文件等)之间的数据,它并不符合关系型数据库或其他数据表的形式关联起来的数据模型结构,但包含相关标记,用来分隔语义元素以及对记录和字段进行分层。因此,它也被称为自描述的结构,即数据的结构和内容混在一起的,如:日志文件、XML文档、JSON文档、Email、HTML等,就属于半结构化数据。
半结构化数据,属于同一类实体可以有不同的属性,即使他们被组合在一起,这些属性的顺序并不重要。
3. 非结构化数据
没有预定义的数据模型,不方便用数据库二维逻辑表来表现的数据。它是没有固定格式的数据,其字段长度可变,并且每个字段的记录又可以由可重复的或不可重复的子字段构成的数据库,用它不仅仅可以处理结构化数据(如:数字、符号等信息),而且更适合处理非结构化数据(如:文本文件、图像、声音、影视、超媒体等信息)。
非结构化数据包括所有格式的办公文档(WORD、PDF、PPT、EXL)、文本、图片、各类报表、图像、音频、视频等。
4. 应用场景
结构化数据,简单来说就是数据库。结合到典型场景中更容易理解,比如企业ERP、财务系统;医疗HIS数据库;教育一卡通;政府行政审批;其他核心数据库等。这些应用需要哪些存储方案呢?基本包括高速存储应用需求、数据备份需求、数据共享需求以及数据容灾需求。
半结构化数据,包括邮件、HTML、报表、资源库等等,典型场景如邮件系统、WEB集群、教学资源库、数据挖掘系统、档案系统等等。这些应用对于数据存储、数据备份、数据共享以及数据归档 等基本存储需求。
非结构化数据,包括视频、音频、图片、图像、文档、文本等形式。具体到典型案例中,像是医疗影像系统、教育视频点播、视频监控、国土GIS、设计院、文件服务器(PDM/FTP)、媒体资源管理等具体应用,这些行业对于存储需求包括数据存储、数据备份以及数据共享等。
二、HTML
1. HTML的理解
HTML 指的是超文本标记语言 (Hyper Text Markup Language)是用来描述网页的一种语言。HTML 不是一种编程语言,而是一种标记语言 (markup language),其标记语言是一套标记标签 (markup tag)。
所谓超文本,有2层含义:
- 因为它可以加入图片、声音、动画、多媒体等内容( 超越文本限制 )
- 不仅如此,它还可以从一个文件跳转到另一个文件,与世界各地主机的文件连接(超级链接文本 )。
一句话概括:
网页是由网页元素组成的 , 这些元素是利用html标签描述出来,然后通过浏览器解析,就可以显示给用户了。
2. HTML的骨架结构
HTML 有自己的语言语法骨架格式,主要包括:
<html>
<head>
<title></title>
</head>
<body>
</body>
</html>
html骨架主要标签含义:
标签名 | 定义 | 说明 |
---|---|---|
<html></html> | HTML标签 | 页面中最大的标签,我们成为 根标签 |
<head></head> | 文档的头部 | 注意在head标签中我们必须要设置的标签是title |
<titile></title> | 文档的标题 | 让页面拥有一个属于自己的网页标题 |
<body></body> | 文档的主体 | 元素包含文档的所有内容,页面内容 基本都是放到body里面的 |
<titile></title> | 文档的标题 | 让页面拥有一个属于自己的网页标题 |
解析图示:
3. HTML标签关系
- 嵌套关系
<head>
<title> </title>
</head>
- 并列关系
<head></head>
<body></body>
总结:
html双标签 可以分为 有 一种是 父子级 包含关系的标签 一种是 兄弟级 并列关系的标签
注意:
如果两个标签之间的关系是嵌套关系,子元素最好缩进一个tab键的身位(一个tab是4个空格)。如果是并列关系,最好上下对齐。
三、CSS选择器
选择器
选择器 | 例子 | 例子描述 |
---|---|---|
.class | .intro | 选择 class=“intro” 的所有元素 |
#id | #firstname | 选择 id=“firstname” 的所有元素 |
* | * | 选择所有元素 |
element | p | 选择所有 <p> 元素 |
element,element | div,p | 选择所有 <div> 元素和所有 <p> 元素 |
element element | div p | 选择 <div> 元素内部的所有\ 元素。 |
element>element | div>p | 选择父元素为 <div> 元素的所有 <p> 元素。 |
[attribute] | [target] | 选择带有 target 属性所有元素。 |
[attribute=value] | [target=_blank] | 选择 target="_blank" 的所有元素。 |
后面会用到的html网页:
import parsel
html = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>标签选择器</title>
</head>
<style>
p{
color: #f00;
font-size: 16px;
}
</style>
<body>
<p>css标签选择器的介绍</p>
<p>标签选择器、类选择器、ID选择器</p>
<a href="https://www.baidu.com">百度一下</a>
<span> 我是一个span标签</span>
</body>
</html>
"""
1. 标签选择器
选择"标签"
selector = parsel.Selector(html)
p = selector.css("body")
print(p.getall())
2. 类选择器
选择".class"
selector = parsel.Selector(html)
p = selector.css(".p")
print(p.getall())
详细讲解:
1、类选择器都是使用英文圆点(.)开头;
2、每个元素可以有多个类名,,名称可以任意起名(但不要起中文,一般都是与内容相关的英文缩写)
3、类选择器只会改变类下的元素样式,而不会改变其它标签的默认样式;
我们上边的页面在浏览器上显示的效果就如下所示:(content下的文字内容颜色变成了红色,字体变成了16px)
3. ID选择器
选择"#id"
ID选择器类似于类选择符,作用同类选择符相同,但也有一些重要的区别。
详细讲解:
1、ID选择器为标签设置id=“ID名称”,而不是class=“类名称”。
2、ID选择符的前面是符号为井号(#),而不是英文圆点(.)。
3、ID选择器的名称是唯一的,即相同名称的id选择器在一个页面只能出现一次;
4. 组合选择器
可以"p.class#id",无需空格隔开
可以多个选择器一起使用,就是组合选择器
5. 伪类选择器
用法 | 例子 | 说明 |
---|---|---|
:last-child | p:last-child | 选择所有p元素的最后一个子元素 (倒序、孩子、一个) |
:last-of-type | p:last-of-type | 选择每个p元素是其母元素的最后一个p元素(倒序、孩子、自身) |
:not(selector) | :not(p) | 选择所有p以外的元素 (除了自身) |
:nth-child(n) | p:nth-child(2) | 选择所有 p 元素的父元素的第二个子元素(正序) |
:nth-last-child(n) | p:nth-last-child(2) | 选择所有p元素倒数的第二个子元素 (倒序) |
a>p | a>p | a标签下的p标签 |
6. 属性提取器
可以用 :: 指定选择标签的属性。
selector = parsel.Selector(html)
href = selector.css('a::attr(href)').extract()
text = selector.css('a::text').getall()
print(href)
extract()与getall()效果等同。
四、XPath数据提取
XPath (XML Path Language) 是一门在 XML 文档中查找信息的语言,可用来在 XML/HTML 文档中对元素和属性进行遍历,并提取相应元素。
也是一种数据提取方式,只不过针对的是HTML/XML数据,因为爬虫主要和HTML页面打交道。
1. XML介绍
XML称为可扩展标记语言,XML是互联网数据传输的重要工具,它可以跨越互联网任何的平台,不受编程语言和操作系统的限制,可以说它是一个拥有互联网最高级别通行证的数据携带者。非常类似HTML。
HTML 和 XML的区别在于HTML主要用来显示数据,XML是用来传输数据。
XML都是标签闭合的。例如:<bookstore> … </bookstore> 成对出现。
<?xml version="1.0" encoding="utf-8"?>
<bookstore>
<book category="奇幻">
<title lang="ch">冰与火之歌</title>
<author>乔治 马丁</author>
<year>2005</year>
<price>365.00</price>
</book>
<book category="童话">
<title lang="ch">哈利波特与死亡圣器</title>
<author>J K. 罗琳</author>
<year>2005</year>
<price>48.98</price>
</book>
<book category="编程">
<title lang="ch">Python编程-从入门到放弃</title>
<author>挖掘机小王子</author>
<year>2048</year>
<price>99.00</price>
</book>
<book category="web" cover="paperback">
<title lang="en">Python编程-从看懂到看开</title>
<author>尼古拉斯-赵四</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>
在上面的xml
语法中存在父子,先辈等关系。
2. XPath匹配规则
3. lxml库
lxml 是一个Python第三方模块。主要的功能是如何解析和提取 HTML/XML 数据。
lxml和正则类似,是一款高性能的 Python HTML/XML 解析器,我们可以利用之前学习的XPath语法,来快速的定位特定元素以及节点信息。
- 安装:
pip install lxml
在线安装不成功就采用离线安装。
在安装 parsel 时会自动安装 lxml,这里就不需要再次安装了。
4. 使用lxml模块
初始化生成一个XPath解析对象,同时可以自动补全残缺的HTML标签。传入网页源码。
from lxml import etree
string = """
<book category="web" cover="paperback">
<title lang="en">Python编程-从看懂到看开</title>
<author>Python编程</author>
<year>2003</year>
<price>39.95</price>
</book>
"""
# 再解析之前必须先转化一下
html = etree.HTML(string)
# 返回结果是列表
result = html.xpath("//book[contains(@cover,'paper')]/title/text()")
result = html.xpath("//book[4]/title/text()")
print(result)
因为 parsel
对 lxml
进行了封装, 所以可以直接在 parsel 中无缝切换使用 xpath 。
使用XPath选取指定内容。括号里面书写XPath语法规则。返回列表。
# -*- coding: utf-8 -*-
import requests
import parsel
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36'
}
response = requests.get('https://maoyan.com/board/4?offset=0', headers=headers)
html = response.text
# %% 选择任意节点
sel = parsel.Selector(html)
# 提取 p 标签
ps = sel.xpath('//p')
for p in ps:
print(p.get())
5. XPath爬取猫眼电影
使用XPath提取猫眼电影排行榜前100名。猫眼电影页面
'''
猫眼电影:
https://maoyan.com/board/4?offset=20
函数式:
1. 获取(请求)一页的函数
2. 定义解析函数(解析一页)
3. 写入文件函数
4. 循环函数
'''
import json
import requests
from lxml import etree
# 获取响应
def getOnePage(url):
'''获取一页的响应的函数'''
response = requests.get(url)
return response.text
# 解析响应 --> 结果
def parseOnePage(text):
# 初始化解析
html = etree.HTML(text)
# 里面有所有的数据 先选择上一层 这一层里面包含所有数据 然后循环遍历
data = html.xpath('//dl[@class="board-wrapper"]')
# 遍历提取所有的元素
for dat in data:
print(dat)
# 继续选取
# 标题
title = dat.xpath('.//div//a/text()')
# 主演
star = dat.xpath('.//p[@class="star"]/text()')
# 时间
releasetime = dat.xpath('//p[@class="releasetime"]/text()')
for tit, sta, rel in zip(title, star, releasetime):
# 在函数里面遇到return就终止
# 生成器
yield {
'电影名字': tit,
'主演': sta.strip(),
'上映时间': rel
}
def save2File(data):
# with open('maoyan66.txt', 'a', encoding='utf-8') as fp:
# fp.write(data+'\n')
with open('maoyan66.txt', 'a', encoding='utf-8') as fp:
fp.write(json.dumps(data, ensure_ascii=False)+'\n')
if __name__ == "__main__":
for page in range(10):
# 一页网址
url = f'https://maoyan.com/board/4?offset={page*10}'
# 调用
r = getOnePage(url)
# 解析数据 返回生成器
result = parseOnePage(r)
for res in result:
# with open('maoyan.txt','a',encoding='utf-8') as fp:
# # 打印到文件
# print(str(res), file=fp)
save2File(str(res))
五、正则re
请看我以前的文章:正则表达式
六、转义字符
在需要在字符中使用特殊字符时,python 用反斜杠转义字符。如下表:
原始字符串:
由于字符串中的反斜线都有特殊的作用,因此当字符串中包含反斜线时,就需要使用转义字符 \ 对字符串中包含的每个 ‘’ 进行转义。
比如说,我们要写一个关于 Windows 路径 G:\publish\codes\02\2.4 这样的字符串,如果在 Python 程序中直接这样写肯定是不行的,需要使用 \ 转义字符,对字符串中每个 ‘\’ 进行转义,即写成 G:\\publish\\codes\\02\\2.4 这种形式才行。
有没有觉得这种写法很啰嗦,有没有更好的解决办法呢?答案是肯定的,借助于原始字符串可以很好地解决这个问题。
原始字符串以“r”开头,它不会把反斜线当成特殊字符。因此,上面的 Windows 路径可直接写成如下这种形式:
# 原始字符串包含的引号,同样需要转义
s2 = r'"Let\'s go", said Charlie'
print(s2)
r - 表示raw-原生的意思