爬虫处理之结构化数据操作
目录
正则表达式提取数据
正则表达式案例操作
Xpath提取数据
Xpath案例操作
BeautifulSoup4提取数据
BeautifulSoup4案例操作
章节内容
1. 关于数据
爬虫程序,主要是运行在网络中进行数据采集的一种计算机程序,正常的一个爬虫采集数据的过程大致如下:
访问目标服务器
采集数据,获取访问url的数据
根据需要筛选数据
处理数据,存储到文件或者数据库,等待下一步进行数据分析或者数据展示
由于存在着不同的服务器和软件应用,所以爬虫获取到的数据就会出现各种不同的表现形式,但是总体来说还是有规律的,有规律就可以被掌握的
首先,关于爬虫处理的数据,一般分为两种数据
非结构化数据:数据的内容整体没有固定的格式和语法规范
结构化数据:数据的内容有固定的语法规范,按照一定的结构进行组织管理
这两种数据都分别表示什么样的数据呢,分别应该通过什么样的方式进行处理呢,这是爬虫在采集完数据之后,针对数据进行筛选必须要进行的操作
接下来,了解两种不同的数据的表现形式
非结构化数据
无格式字符串数据:用户名、邮箱、账号、电话号码、地址、电影名称、评分、评论、商品名称等等
结构化数据
带有一定的格式的数据:HTML网页文档、XML网页文档、JSON等等
第三,对于不同的数据,进行有效数据筛选时,应该分别怎么进行操作呢
非结构化数据:由于没有任何固定的格式,只能根据字符出现的规律进行动态匹配的方式来完成数据的提取:正则表达式
结构化数据:由于数据本身存在一定的规律性,可以通过针对这些规律的分析工具进行数据的提取:正则表达式、Xpath、BeautifulSoup4、select、css等等
2. 正则表达式
正则表达式是一门单独的技术,在实际操作过程中由于它优雅的字符匹配特性,各种编程语言都陆续支持正则表达式的操作方式,Python中通过内建模块re进行正则表达式的处理,大致按照如下三个步骤进行数据的操作:
确定源数据:获取整体数据
按照目标数据定义正则表达式匹配规则
从整体数据中匹配符合要求的数据
正则表达式的处理,最核心的是先掌握正则表达式的语法和匹配规则,根据实际操作的不同需要,正则表达式定义了不同的数据匹配方式
匹配规则规则描述\将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符。例如,“n”匹配字符“n”。“
”匹配一个换行符。串行“\”匹配“\”而“(”则匹配“(”。^匹配输入字符串的开始位置。如果设置了RegExp对象的Multiline属性,^也匹配“
”或“\r”之后的位置。$匹配输入字符串的结束位置。如果设置了RegExp对象的Multiline属性,$也匹配“
”或“\r”之前的位置。匹配前面的子表达式零次或多次。例如,zo能匹配“z”以及“zoo”。等价于{0,}。+匹配前面的子表达式一次或多次。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{1,}。?匹配前面的子表达式零次或一次。例如,“do(es)?”可以匹配“does”或“does”中的“do”。?等价于{0,1}。{n}n是一个非负整数。匹配确定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o。{n,}n是一个非负整数。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于“o”。{n,m}m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”将匹配“fooooood”中的前三个o。“o{0,1}”等价于“o?”。请注意在逗号和两个数之间不能有空格。?当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串“oooo”,“o+?”将匹配单个“o”,而“o+”将匹配所有“o”。.匹配除“
”之外的任何单个字符。要匹配包括“
”在内的任何字符,请使用像“(.|
)”的模式。(pattern)匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用“(”或“)”。(?:pattern)匹配pattern但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用或字符“(|)”来组合一个模式的各个部分是很有用。例如“industr(?:y|ies)”就是一个比“industry|industries”更简略的表达式。(?=pattern)正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,“Windows(?=95|98|NT|2000)”能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。(?!pattern)正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如“Windows(?!95|98|NT|2000)”能匹配“Windows3.1”中的“Windows”,但不能匹配“Windows2000”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始(?<=pattern)反向肯定预查,与正向肯定预查类拟,只是方向相反。例如,“(?<=95|98|NT|2000)Windows”能匹配“2000Windows”中的“Windows”,但不能匹配“3.1Windows”中的“Windows”。(?
匹配一个换行符。等价于\x0a和\cJ。\r匹配一个回车符。二手游戏账号交易平台等价于\x0d和\cM。\s匹配任何空白字符,包括空格、制表符、换页符等等。等价于[ \f
\r \v]。\S匹配任何非空白字符。等价于[^ \f
\r \v]。 匹配一个制表符。等价于\x09和\cI。\v匹配一个垂直制表符。等价于\x0b和\cK。\w匹配包括下划线的任何单词字符。等价于“[A-Za-z0-9_]”。\W匹配任何非单词字符。等价于“1”。\xn匹配n,其中n为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,“\x41”匹配“A”。“\x041”则等价于“\x04&1”。正则表达式中可以使用ASCII编码。.
um匹配num,其中num是一个正整数。对所获取的匹配的引用。例如,“(.)\1”匹配两个连续的相同字符。
标识一个八进制转义值或一个向后引用。如果
之前至少n个获取的子表达式,则n为向后引用。否则,如果n为八进制数字(0-7),则n为一个八进制转义值。
m标识一个八进制转义值或一个向后引用。如果
m之前至少有nm个获得子表达式,则nm为向后引用。如果
m之前至少有n个获取,则n为一个后跟文字m的向后引用。如果前面的条件都不满足,若n和m均为八进制数字(0-7),则
m将匹配八进制转义值nm。
ml如果n为八进制数字(0-3),且m和l均为八进制数字(0-7),则匹配八进制转义值nml。瀝d>匹配n,其中n是一个用四个十六进制数字表示的Unicode字符。例如,?匹配版权符号(?)。
3. python操作正则表达式
python内置了re模块,可以很方便快捷的操作正则表达式语法完成字符串的查询匹配操作行为,需要注意的是通过re操作正则表达式的两种表现形式
第一种方式主要是通过compile()函数根据给定的正则表达式编译生成正则匹配对象,通过正则匹配对象完成字符串的查询匹配操作过程
import re# 定义正则表达式,通过compile()函数编译pattern=repile('正则表达式')# 核心操作函数# 1.起始位置匹配一次:仅从指定的起始位置进行匹配(默认开头位置)# 匹配成功返回匹配到的字符串,表示目标字符串是该字符串开头的# 匹配失败返回Nonevalue=pattern.match(string[, start[ , end]])# 2.全文匹配一次:从指定的起始位置开始匹配(默认开头位置)# 陆续对字符串中的所有字符进行匹配# 匹配成功返回匹配到的字符串,表示目标字符串中包含该字符串# 匹配失败返回Nonevalue=pattern.search(string[, start[, end]])# 3.全文匹配# 从目标字符串中查询所有符合匹配规则的字符,并存储到一个列表中# 匹配结束返回列表,包含匹配到的数据# 没有匹配到数据返回空列表,否则返回包含所有匹配数据的列表value_list=pattern.findall(string[, start[, end]])# 4.全文匹配获取迭代器# 从目标字符串中查询所有符合匹配规则的字符,并存储到一个迭代器中value_iter=pattern.finditer(string[, start[, end]])# 5.字符串切割:根据指定的正则表达式切割目标字符串并返回切割后的列表value_list=pattern.split(string)# 6.字符串替换:根据指定的匹配规则,将string中符合的字符串替换为value值,count是替换次数,默认全部替换value_replace=pattern.sub(value, string[, count])
通过正则匹配到的值对象value,可以通过指定的函数输出匹配到的数据的信息
# 输出匹配到的数据value()# 输出匹配到的第一组数据value(1)# 输出匹配的第n组数据:前提条件是在正则表达式中使用圆括号进行了n次分组value(n)# 输出匹配数据的索引范围value.span()# 输出匹配的第n组数据的索引范围value.span(n)# 输出匹配的第n组数据的索引开始位置value.start(n)# 输出匹配的第n组数据的索引结束位置value.end(n)
注意的是:在使用正则表达式时,贪婪模式和懒惰模式的操作行为可以精确的匹配数据
通常情况下,正则表达式模式是贪婪模式进行匹配的,如果需要精确匹配的情况下,在正常正则表达式后面添加一个?匹配符号即可!
# 定义目标字符串>>> s="helelo world"# 编译正则匹配对象:这里我们只是想得到: lel>>> pattern=repile('ll')# 进行数据匹配操作>>> r=repile('l.l')# 展示数据>>> r.search(s)()# 展示得到的数据,显然匹配的数据中包含了其他数据'lelo worl'# 那么,下面这个例子,貌似更加实际>>> html="
i am div
i am p
i am div too
"# 定义匹配规则,只是想匹配div中包含的数据>>> pattern=repile("
.*
")# 打印展示数据>>> pattern.search(html)()# 显示的数据,明显包含了不需要的数据,这是贪婪模式'
i am div
i am p
i am div
'# 重新定义>>> html="
i am div
i am p
i am div too
"# 修改过的正则表达式>>> pattern=repile("
.*?
")# 匹配得到数据>>> pattern.search(html)()# 显示的数据,包含的数据,就是精确匹配到的数据'
i am div
'
正则表达式案例操作:百度图片搜索下载
4. Xpath
Xpath原本是在可扩展标记语言XML中进行数据查询的一种描述语言,可以很方便的在XML文档中查询到具体的数据;后续再发展过程中,对于标记语言都有非常友好的支持,如超文本标记语言HTML。
在操作Xpath之前,首先需要了解一些基础的技术术语
下面是一段常见的HTML代码
一级标题
标题 标题 标题 标题
内容 内容 内容 内容
根标签:在标记语言中,处在最外层的一个标签就是根标签,根标签有且仅有一个,在上述代码中就是跟标签
父标签:和子标签对应,内部包含了其他元素数据,该标签就是内部标签的父标签,如是的父标签,又是
个人简介
姓名:某某某
住址:中国 乡下
座右铭:岂能尽如人意,但求无愧于心
"""# 转换成html数据# html=etree.parse("index.html")# 从文件中直接加载html数据html=etree.HTML(content)# 通过etree.HTML()函数将字符串转换成HTML文档对象print dir(html)# 查看文档对象的所有函数print html.getchildren()# 查看文档对象根节点的所有子节点# 转换成字符数据str_html=etree.tostring(html)# 将HTML文档对象转换成字符串print type(str_html)# 查看输出类型print str_html# 查看输出数据
xpath操作
# -- coding:utf-8 --from lxml import etree# 模拟得到爬虫数据content=u"""
个人简介
姓名:某某某
住址:中国 乡下
座右铭:岂能尽如人意,但求无愧于心
"""# 将爬取到的数据转换成HTML文档html=etree.HTML(content)# 查询所有的p标签p_x=html.xpath("//p")
print(p_x)# 查询所有Name属性的值v_attr_name=html.xpath("//@name")
print(v_attr_name)# 查询所有包含name属性的标签e_attr_name=html.xpath("//*[@name]")
print(e_attr_name)# 查询所有包含name属性,并且name属性值为desc的标签e_v_attr_name=html.xpath("//*[@name='desc']")
print(e_v_attr_name)# 查询所有p标签的文本内容,不包含子标签p_t=html.xpath("//p")for p in p_t: print (p.text)# 查询多个p标签下的所有文本内容,包含子标签中的文本内容p_m_t=html.xpath("//p")for p2 in p_m_t:
print(p2.xpath("string(.)"))
案例操作:爬虫智联招聘中前10页的某个工作岗位名称、薪水、公司信息
6. BeautifulSoup4
BeautifulSoup也是一种非常优雅的专门用于进行HTML/XML数据解析的一种描述语言,可以很好的分析和筛选HTML/XML这样的标记文档中的指定规则数据
在数据筛选过程中其基础技术是通过封装HTML DOM树实现的一种DOM操作,通过加载网页文档对象的形式,从文档对象模型中获取目标数据
BeautifulSoup操作简单易于上手,在很多对于数据筛选性能要求并不是特别苛刻的项目中经常使用,目前市场流行的操作版本是BeautifulSoup4,经常称BS4
Xpath和BeautifulSoup
Xpath和BeautifulSoup都是基于DOM的一种操作模式
不同点在于加载文档对象模型DOM时出现的文档节点遍历查询操作过程,Xpath在进行遍历操作时针对描述语言指定的语法结构进行局部DOM对象树的遍历得到具体的数据,但是BS4在操作过程中,会将整个文档树进行加载然后进行查询匹配操作,使用过程中消耗资源较多,处理性能相对Xpath较低
那么为什么要用BS4呢?因为,它,足够简单!
描述语言处理效率上手程度正则表达式效率非常高困难Xpath效率很高正常BS4效率较高简单
BS4本身是一种对描述语言进行封装的函数操作模块,通过提供面向对象的操作方式将文档对象中的各种节点、标签、属性、内容等等都封装成了python中对象的属性,在查询操作过程中,通过调用指定的函数直接进行数据 匹配检索操作,非常的简单非常的灵活。
一般BS4将HTML文档对象会转换成如下四种类型组合的文档树
Tag:标签对象
NavigableString:字符内容操作对象
BeautifulSoup:文档对象
Comment:特殊类型的NavigableString
说道这里,其实都是太多的理论性语法,BS4不同于正则和Xpath,没有什么基础语法结构,它封装的对象以及对象的属性操作,才是BS4不同凡响的核心价值
let's 上干货
7. python操作BeautifulSoup4
python中对于BeautifulSoup的支持,通过安装第三方模块来发挥它最好的操作
$ pip install beautifulsoup4
入门第一弹:了解BeautifulSoup4
# coding:utf-8# 引入解析模块BS4from bs4 import BeautifulSoup# 从文件中加载html网页,指定HTML解析器使用lxml# 默认不指定的情况下,BS4会自动匹配当前系统中最优先的解析器soup=BeautifulSoup(open("index.html"), "lxml")# 如果是爬虫获取到的字符数据,直接交给BS4就OK拉# soup=BeatufulSoup(spider_content, "lxml")# 打印BeautifulSoup文档对象,得到的是文档树内容print(soup)# 打印类型:print(type(soup))
入门第二弹:操作标签、属性、内容
# coding:utf-8from bs4 import BeautifulSoup# 得到构建的文档对象soup=BeautifulSoup(open("index.html"), "lxml")# Tag操作# 1. 获取标签print(soup.title) # print(soup.p) #
姓名:大牧
只返回第一个匹配到的标签对象print(soup.span) # 大牧# 2.获取标签的属性print(soup.p.attrs) # {}:得到属性和值的字典print(soup.span.attrs) # {'id': 'name'}:得到属性和值的字典print(soup.span['id']) # name:得到指定属性的值soup.span['id']="real_name"print(soup.span['id']) # real_name : 可以方便的在BS4中直接对文档进行修改# 3. 获取标签的内容print(soup.head.string) # 文章标题:如果标签中只有一个子标签~返回子标签中的文本内容print(soup.p.string) # None:如果标签中有多个子标签,返回Noneprint(soup.span.string) # 大牧:直接返回包含的文本内容
入门第三弹:操作子节点
# coding:utf-8# 引入BS4操作模块from bs4 import BeautifulSoup# 加载网页文档,构建文档对象soup=BeautifulSoup(open("index.html"), "lxml")
print(dir(soup))
print(soup.contents)# 得到文档对象中所有子节点print(soup.div.contents)# 得到匹配到的第一个div的子节点列表print(soup.div.children)# 得到匹配到的第一个div的子节点列表迭代器# for e1 in soup.div.children:# print("-->", e1)print(soup.div.descendants)# 得到匹配到的第一个div的子节点迭代器,所有后代节点单独一个一个列出# for e2 in soup.div.descendants:# print("==>", e2)
入门第四弹: 面向对象的DOM匹配
# coding:utf-8# 引入BS4模块from bs4 import BeautifulSoup# 加载文档对象soup=BeautifulSoup(open("../index.html"), "lxml")# DOM文档树查询# 核心函数~请对比javasript dom结构了解它的方法# 如:findAllPrevious()/findAllNext()/findAll()/findPrevious()/findNext()等等# findAll()为例# 1. 查询指定的字符串res1=soup.findAll("p")# 查询所有包含p字符的标签print(res1)# 2. 正则表达式import re
res2=soup.findAll(repile(r"d+"))# 查询所有包含d字符的标签print(res2)# 3. 列表:选择res3=soup.findAll(["div", "h1"])# 查询所有的div或者h1标签print(res3)# 4. 关键字参数res4=soup.findAll(id="name")# 查询属性为id="name"的标签print(res4)# 5. 内容匹配res5=soup.findAll(text=u"男")# 直接匹配内容中的字符,必须保证精确匹配print(res5)
res6=soup.findAll(text=[u"文章标题", u"大牧"])# 查询包含精确内容的所有的标签print(res6)
res7=soup.findAll(text=repile(u"大+"))# 通过正则表达式进行模糊匹配print(res7)
入门第五弹: 又见CSS
# coding:utf-8# 引入BS模块from bs4 import BeautifulSoup# 加载网页构建文档对象soup=BeautifulSoup(open("index.html"), "lxml")# 1. CSS 标签选择器:根据标签名称查询标签对象res1=soup.select("span")
print(res1)# 2. CSS ID选择器:根据ID查询标签对象res2=soup.select("#gender")
print(res2)# 3. CSS 类选择器:根据class属性查询标签对象res3=soup.select(".intro")
print(res3)# 4. CSS 属性选择器res41=soup.select("span[id]")
print(res41)
res42=soup.select("span[id='gender']")
print(res42)# 5. CSS 包含选择器res5=soup.select("p span#name")
print(res5)# 6. 得到标签内容res6=soup.select("p > span.intro")
print(res6[0].string)
print(res6[0].getText())
- A-Za-z0-9_ ↩