首先下载Beautiful Soup 4,然后解压安装,记得安装代码是
python setup.py install
这里要说明一点!!!很重要,我吃了一个多小时的亏。
我把文件命名为 bs4.py
这时候如果要from bs4 import ... 的话,就是直接读取文件本身,所以千万不要乱写脚本名字。如果把脚本名字改成 test.py 就可以了。
真的烦人。
进入演示页面 http://python123.io/ws/demo.html
测试 bs4 安装:
from bs4 import BeautifulSoup
到这里如果没问题就搞定了。
使用方法很简单,牢记这两行代码就行了。
from bs4 import BeautifulSoup
soup = BeautifulSoup('<p>data/p', 'html.parser') # 解析代码
=============================================================================
HTML 代码大概是
<html>
<body>
<p class="title"> ... </p>
</body>
</html>
这个样的,所以 bs4 库就是解析、遍历、维护“标签树”的功能库。
其中
<p> ... </p>
是 标签 tag,是成对出现的。
<p class="title"> ... </p>
p是标签的名称,p后面那个叫属性域,包含0个或者多个属性域。class就是一种属性,属性内容是 title。
============================================================
bs4 的 HTML 解析器是 html.parser ,此外还有 lxml 和 html5lib 的解析器。
这里主要用 HTML 解析器。
这里假设已经获得了网页信息了,以下是一些命令:
# -*- coding:utf-8 -*-
from bs4 import BeautifulSoup
soup = BeautifulSoup(demo, 'html.parser') # 解析代码
print 'title: '
print soup.title # 打印 title 标签
tag = soup.a # 获取 .a 标签,也就是链接标签
print'.a tag: '
print tag
print soup.a.name #获取 a 标签的名字
print soup.a.parent.name # a 的父标签的名字
print soup.a.parent.parent.name # a 的父标签的父标签的名字
print tag.attrs # attrs 获得标签的属性(全部)
print tag.attrs['class'] # 单独获得某个属性
print tag.attrs['href']
print type(tag.attrs) # 标签属性的类型(这里是字典)
print type(tag) # 标签本身的类型(就是 tag 类型)
print soup.a.string # a 标签的 string
print soup.p # a 标签的 string 信息 'Basic Python'
print soup.p.string # p 标签的 string 信息
print type(soup.p.string) # p 标签的 string 的类型
# tag 的 comment(注释)类型
newsoup = BeautifulSoup("<b><!--This is a comment--></b><p>This is not a comment</p>", "html.parser")
print newsoup.b.string
print type(newsoup.b.string)
print newsoup.p.string
print type(newsoup.p.string) # 注意 b 和 p 标签字符串的类型是不同的
=======================================================================
基于 bs4 库的 html 提取方法
首先回顾 demo 的写法:
# -*- coding:utf-8 -*-
# demo
import requests
from bs4 import BeautifulSoup
r = requests.get("http://python123.io/ws/demo.html")
demo = r.text
print demo
先说子节点的用法
# 子节点
soup = BeautifulSoup(demo, 'html.parser') # 解析代码
print soup.head # head 标签
print soup.head.contents # head 的子节点是 title 标签,存在了一个列表中
print soup.body.contents # 同理,这是 body 的子节点标签
print len(soup.body.contents) # 表示 body 的子节点的数量,这里有五个
print soup.body.contents[1] # 这里输出的是第一个子节点
for child in soup.body.children: # 遍历儿子节点
print child
for child in soup.body.descendants # 遍历子孙节点(课上说是 children ,感觉错了)
print child
然后是父节点
# 父节点
soup = BeautifulSoup(demo, 'html.parser') # 解析代码
print soup.title.parent # title 标签的父节点
print soup.html.parent # html 的父亲就是他自己
print soup.parent # soup 是一个特殊标签,他的父亲是空的
for parent in soup.a.parents: # 对 a 标签的所有先辈打印
if parent is None:
print parent # 这里要注意
# 遍历的时候会遍历到 soup 本身,但是他是不存在 .name 信息的
else:
print parent.name
=============================================================================
然后平行遍历
# 平行遍历
soup = BeautifulSoup(demo, 'html.parser') # 解析代码
print soup.a.next_sibling # a 标签的下一个平行节点(不一定还是标签,也有可能是字符串)
print soup.a.next_sibling.next_sibling # 再下一个平行节点
print soup.a.prebious_sibling # a 标签之前的一个平行节点
print soup.a.prebious_sibling.prebious_sibling # 再前一个,也许为空
print soup.a.parent
for sibling in soup.a.next_siblings: # 遍历后续节点
print sibling
for sibling in soup.a.prebious_siblings: # 遍历前续节点
print sibling
然后是输出
# 输出
soup = BeautifulSoup(demo, 'html.parser') # 解析代码
print soup.prettify() # 带换行符,这个方法就是为文本标签内容添加换行符
print soup.a.prettify() # 单独打印 a 标签
总结:
到此就是BS4基本用法,接下来讲进一步用法。
==========================================================================
HTML 信息标记有三大种类 :XML, JSON, YAML
先有HTML,后有XML,它通过标签形式来构建信息。
JSON:是有类型的键值对——key: value 的组合。比如 :
"name": "北京大学"
"name": ["北京大学","理学院"]
除了数字,需要双引号。
YAML:采用无类型键值对——
name:北京大学
注意没有双引号,需要的时候可以利用缩进表达从属,用 - 号表达并列。
XML:扩展性好,但是繁琐
JSON:适合 JavaScript,和XML比更简洁
YAML:文本信息比例最高,可读性好。
================================================================
===========================================================
信息提取方法:
一、完整解析信息的标记形式,然后提取关键信息,比如bs4库的标签树遍历。
二、无视标记形式,直接搜索关键信息。
三、融合方法:结合形式解析和搜索方法,提取关键信息。
from bs4 import BeautifulSoup
soup = BeautifulSoup(demo, "html.parser")
for link in soup.find_all('a'): # 查找 a 标签
print(link.get('href')) # 得到了内容,href 是属性。
记得之前的demo,再写一遍:
# -*- coding:utf-8 -*-
# demo
import requests
r = requests.get("http:/python123.io/ws/demo.html")
demo = r.text
print demo
=====================================================
现在介绍 find_all :
# <>.find_all(name, attrs, recursive, string, **kwargs)
# name: 对标签名称的检索字符串
# attrs: 对标签属性值的检索字符串,可标注属性检索
# recursive: 对标签的子孙搜索,默认为 True
# string: <>...</> 中字符串区域的检索字符串
print soup.find_all('a'): # 查找 a 标签
print soup.find_all(['a','b']) # 找 a b 标签
for tag in soup.find_all(True): # True 会显示所有标签信息
print(tag.name)
import re
for tag in soup.find_all(re.compile('b')) # 返回的所有以 b 开头的信息
print tag.name
print soup.find_all('p','course') # 查找了带有 course 属性的 p 标签
print soup.find_all(id = 'link1') # 属性中 id 域为 link1 的标签
print soup.find_all(id = 'link') # 属性中 id 域为 link 的标签
print soup.find_all(id = re.compile('link')) # 输出以 link 开头但是不全是 link 的信息
print soup.find_all('a', recursive = False) # 返回这里是一个空列表,说明儿子节点上面没有 a 标签
print soup.find_all(string = 'Basic Python') # 精确检索 Basic Python
print soup.find_all(string = re.compile("Python")) # 使用正则,把带有 python 的字符串域全部检索
以后会介绍正则库
find_all() 函数 和 正则结合很好用。
缩写:
<tag>(..) 等价于 <tag>.find_all(..)
soup(..) 等价于 soup.find_all(..)
=========================================================================
实战 http://www.zuihaodaxue.cn/zuihaodaxuepaiming2016.html
最好大学网的2016中国最好大学排名,这里我们要试着使用爬虫爬取。
功能:
输入:大学排URL
输出:大学排名信息的屏幕输出(排名,大学,总分)
路线:requests / BS4
这个称为:定向爬虫
先打开看看源代码。我们要看看这个页面的大学排名信息是动态的还是写在源代码中的,如果是源代码中就有的,那么可以爬取。
实际上打开发现是写好了的,那么我们可以用爬虫爬取信息了。
<tr class="alt"><td>1</td>
<td><div align="left">清华大学</div></td>
<td>北京市</td><td>95.9</td>
然后看一看网站的robots协议。
404说明随便爬取。
# -*- coding:utf-8 -*-
# 爬取中国最好大学数据
import requests
from bs4 import BeautifulSoup
import bs4
def getHTMLText(url): # 从网络上获取大学排名网页内容
try:
r = requests.get(url, timeout = 30) # 限定时间 30 秒
r.raise_for_status() # 有问题就产生异常
r.encoding = r.apparent_encoding # 编码
return r.text
except:
return "" # 否则返回空字符串
def fillUnivList(ulist, html ): # 提取网页内容中信息到合适的数据结构
# 注意这里的 "tbody"/"td"/"tr" 都是根据网页源代码来的
soup = BeautifulSoup(html, "html.parser")
for tr in soup.find('tbody').children: # 遍历、查找在 tbody 中子标签中的 tr 标签,tr 包含大学信息
if isinstance(tr, bs4.element.Tag): # 检测 tr 标签类型,如果不是 bs4 库定义的 tag 类型,则过滤掉
tds = tr('td') # 把所有的 td 标签存为列表 tds
ulist.append([tds[0].string, tds[1].string, tds[2].string]) # 把需要的 td 标签加入到 ulist
def printUnivList(ulist, num ): # 利用数据结构展示并输出结果
tplt = "{0:^10}\t{1:{3}^10}\t{2:^10}"
# 注意 ":" 是引导符号,"3"是填充的字符,"^" 是居中对齐符号,"10"是宽度,最前面"0"、"1"、"2"是顺序
# chr 12288是中文空格变量
print (tplt.format("排名", "学校名称", "总分",chr(12288))) # format 方法格式化输出
for i in range(num):
u = ulist[i] # 所有信息保存在 ulist 中
print (tplt.format(u[0], u[1], u[2],chr(12288))) # 格式化输出
def main(): # 主函数
uinfo = [] # 大学信息放在 uinfo 列表
url = 'http://www.zuihaodaxue.cn/zuihaodaxuepaiming2016.html'
html = getHTMLText(url)
fillUnivList(uinfo, html)
printUnivList(uinfo, 20) # 打印 20 所大学信息
main()
最后输出效果:
个中的表格对齐很重要,请看图片和注释。