一、CSS 选择器:BeautifulSoup4
首先和 lxml 一样,Beautiful Soup 也是一个HTML/XML的解析器,主要的功能也是如何解析和提取 HTML/XML 数据。
lxml 只会局部遍历,而Beautiful Soup 是基于HTML DOM的,会载入整个文档,解析整个DOM树,因此时间和内存开销都会大很多,所以性能要低于lxml。
BeautifulSoup 用来解析 HTML 比较简单,API非常人性化,支持CSS选择器、Python标准库中的HTML解析器,也支持 lxml 的 XML解析器。
Beautiful Soup 3 目前已经停止开发,推荐现在的项目使用Beautiful Soup 4。使用 pip 安装即可:pip install beautifulsoup4
官方文档:http://beautifulsoup.readthedocs.io/zh_CN/v4.4.0
说到解析html的工具,主要有三个、BeautifulSoup、lxml:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from bs4 import BeautifulSoup
html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1"><!-- Elsie --></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.</p>
<p class="story">...</p>
"""
#创建 Beautiful Soup 对象
soup = BeautifulSoup(html,'html.parser', from_encoding='utf-8')
#打开本地 HTML 文件的方式来创建对象
#soup = BeautifulSoup(open('index.html'))
#格式化输出 soup 对象的内容
print soup.prettify()
soup.prettify()能够整理html的格式变得更为整洁;下面这句指定了html 的parser
soup = BeautifulSoup(html,'html.parser', from_encoding='utf-8')
也可以通过soup = BeautifulSoup(html,“lxml”) 指定解析器
二、四大对象种类
Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种:
1,Tag
2,NavigableString
3,BeautifulSoup
4,Comment
1,Tag
Tag就是html中的标签,例如:
<p class="title" name="dromouse">
<b>
The Dormouse's story
</b>
</p>
这些p、b都是标签
将原来的代码进行修改,看如何找到这些标签:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from bs4 import BeautifulSoup
html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1"><!-- Elsie --></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.</p>
<p class="story">...</p>
"""
#创建 Beautiful Soup 对象
soup = BeautifulSoup(html,'html.parser', from_encoding='utf-8')
# 获取title
print soup.title
# 获取header
print soup.head
# 获取a标签,只取了第一个
print soup.a
# p也是只取了第一个
print soup.p
# 打印soup.a的类型
print type(soup.a)
#打开本地 HTML 文件的方式来创建对象
#soup = BeautifulSoup(open('index.html'))
#格式化输出 soup 对象的内容
# print soup.prettify()
对于tag而言,有两个属性很重要,一个是name,另一个是attrs
操作name、attrs和class的方法如下:
# [document] #soup 对象本身比较特殊,它的 name 即为 [document]
print soup.name
# head #对于其他内部标签,输出的值便为标签本身的名称
print soup.head.name
# {'class': ['title'], 'name': 'dromouse'}
# 在这里,我们把 p 标签的所有属性打印输出了出来,得到的类型是一个字典。
print soup.p.attrs
# ['title'] #还可以利用get方法,传入属性的名称,二者是等价的
print soup.p['class'] # soup.p.get('class')
# <p class="newClass" name="dromouse"><b>The Dormouse's story</b></p>
soup.p['class'] = "newClass"
print soup.p # 可以对这些属性和内容等等进行修改
# <p name="dromouse"><b>The Dormouse's story</b></p>
del soup.p['class'] # 还可以对这个属性进行删除
print soup.p
2,NavigableString
有了标签之后,要怎么取出标签内部的文字?用.string 就可以了!
print soup.p.string
# <class 'bs4.element.NavigableString'>
print type(soup.p.string)
3, BeautifulSoup
BeautifulSoup 对象表示的是一个文档的内容。大部分时候,可以把它当作 Tag 对象,是一个特殊的 Tag,我们可以分别获取它的类型,名称,以及属性来感受一下
#<type 'unicode'>
print type(soup.name)
# [document]
print soup.name
# {}
print soup.attrs # 文档本身的属性为空
4,Comment
获取注释内的内容,比如<!-- aa --> 会获取aa:
print soup.a.string
print type(soup.a.string)
输出:
Elsie
<class 'bs4.element.Comment'>
三、遍历文档树
1. 直接子节点 :.contents
.children
属性
content:
标签的contents属性可以将子节点的内容以列表方式展示
print soup.head.contents
输出:[<title>The Dormouse's story</title>]
print soup.head.contents[0]
输出:<title>The Dormouse's story</title>
children:
这个返回的不是一个列表
print type(soup.body.children)
<type 'listiterator'>
它是一个迭代类型的
下面这种方式即可打印出全部body的子节点:
for child in soup.body.children:
print child
2. 所有子孙节点: .descendants
属性
与.contents、children不同,.descendants 不仅包括直接子节点,包括所有子孙节点,遍历的方式如下:
for child in soup.html.descendants:
print child
可以打印出所有节点。
3. 节点内容: .string
属性
如果一个标签里面没有标签了,那么 .string 就会返回标签里面的内容。如果标签里面只有唯一的一个标签了,那么 .string 也会返回最里面的内容。其他使用方式是一样的。
四、搜索文档树
1,find_all(name, attrs, recursive, text, **kwargs)
参数说明:
(1)name
name 参数可以查找所有名字为 name 的tag,字符串对象会被自动忽略掉;
A.传字符串
查找文档中所有的<b>
标签:
print soup.findAll('a')
结果:[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
返回的事一个列表
B.传正则表达式
查出所有以t开头的标签:
print soup.findAll(re.compile("^t"))
C.传列表
查所有title和b的标签:
print soup.findAll(['title','b'])
(2)keyword 参数
print soup.find_all(id='link2')
(3)text 参数
text就是根据文本内容来查找的,可以传 字符串、正则表达式、列表
print soup.find_all(text="The Dormouse's story")
输出:[u"The Dormouse's story", u"The Dormouse's story"]
五、CSS选择器
写 CSS 时,标签名不加任何修饰,类名前加.
,id名前加#
可以通过css选择器来进行选择,用到的方法是 soup.select()
,返回类型是 list
1,通过标签名查找
print soup.select('title')
2,通过类名查找
print soup.select('.sister')
3,通过 id 名查找
print soup.select('#link1')
4,组合查找
print soup.select('p #link1')
和css选择器是一样的
直接子标签查找,则使用 >
分隔
print soup.select("head > title")
5,属性查找
print soup.select('a[class="sister"]')
6,获取内容
for title in soup.select('title'):
print title.get_text()
7,获取属性
node.select('td a')[0].attrs['href']
六、腾讯招聘网站爬虫
1,分析网页
页码的规律:
https://hr.tencent.com/position.php?&start=0#a
https://hr.tencent.com/position.php?&start=10#a
https://hr.tencent.com/position.php?&start=20#a
因此,第几页相当于 start=(页数-1)*10
我感兴趣的数据是职位信息:
分析一下每一行的格式,发现class为even或odd:
2,获取一个页面的全部职位信息
from bs4 import BeautifulSoup
import urllib2
import urllib
import json # 使用了json格式存储
url = 'https://hr.tencent.com/position.php?&start=0#a'
request = urllib2.Request(url)
response =urllib2.urlopen(request)
resHtml = response.read()
soup = BeautifulSoup(resHtml,'html.parser', from_encoding='utf-8')
result = soup.select(".even")
result += soup.select(".odd")
print len(result)
for node in result:
print node
发现这样是可以找到所有的职位信息
3,整理node的内容为json格式并输出
# 以json格式输出到文件中
# 禁用ascii编码,按utf-8编码
line = json.dumps(items, ensure_ascii=False)
output =open('tencent.json','w')
output.write(line.encode('utf-8'))
output.close()
这样就可以整理出一个json文件了!
文件内容:
[{"ztime": "2018-10-16", "zname": "21228-MMORPG游戏本地化策划(深圳) ", "detailLink": "position_detail.php?id=44814&keywords=&tid=0&lid=0", "zlocal": "深圳", "znum": "1", "ztype": "产品/项目类"}, {"ztime": "2018-10-16", "zname": "SA-腾讯社交广告平台产品运营经理(生态平台业务)(上海) ", "detailLink": "position_detail.php?id=44816&keywords=&tid=0&lid=0", "zlocal": "上海", "znum": "1", "ztype": "产品/项目类"}, {"ztime": "2018-10-16", "zname": "PCG04-工业设计师", "detailLink": "position_detail.php?id=44819&keywords=&tid=0&lid=0", "zlocal": "深圳", "znum": "1", "ztype": "设计类"}, {"ztime": "2018-10-16", "zname": "22090-天天快报-数据开发工程师(北京)", "detailLink": "position_detail.php?id=44810&keywords=&tid=0&lid=0", "zlocal": "北京", "znum": "3", "ztype": "技术类"}, {"ztime": "2018-10-16", "zname": "23673-娱乐热点运营编辑(北京)", "detailLink": "position_detail.php?id=44812&keywords=&tid=0&lid=0", "zlocal": "北京", "znum": "1", "ztype": "内容编辑类"}, {"ztime": "2018-10-16", "zname": "21228-游戏产品运营活动策划(深圳)", "detailLink": "position_detail.php?id=44815&keywords=&tid=0&lid=0", "zlocal": "深圳", "znum": "1", "ztype": "产品/项目类"}, {"ztime": "2018-10-16", "zname": "15573-手游系统与关卡策划(深圳)", "detailLink": "position_detail.php?id=44818&keywords=&tid=0&lid=0", "zlocal": "深圳", "znum": "2", "ztype": "产品/项目类"}, {"ztime": "2018-10-16", "zname": "PCG01-QQ大数据应用专家 ", "detailLink": "position_detail.php?id=44808&keywords=&tid=0&lid=0", "zlocal": "深圳", "znum": "1", "ztype": "技术类"}, {"ztime": "2018-10-16", "zname": "22834-腾讯音乐战略发展副总监(深圳)", "detailLink": "position_detail.php?id=44811&keywords=&tid=0&lid=0", "zlocal": "深圳", "znum": "1", "ztype": "市场类"}, {"ztime": "2018-10-16", "zname": "21228-集换式卡牌游戏本地化策划(深圳) ", "detailLink": "position_detail.php?id=44813&keywords=&tid=0&lid=0", "zlocal": "深圳", "znum": "1", "ztype": "产品/项目类"}]