【第2周】网络爬虫之提取

单元4:Beautiful Soup库入门

Beautiful Soup库的安装

管理员身份运行命令提示符

pip install beautifulsoup4

Beautiful Soup库的安装小测
演示HTML页面网址:http://python123.io/ws/demo.html
查看源代码:

  • 浏览器页面右键→查看源代码
  • 使用requests库
>>> import requests
>>> r = requests.get("https://python123.io/ws/demo.html")
>>> r.text
'This is a python demo page\r\n\r\n

The demo python introduces several python courses.

\r\n

Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:\r\nBasic Python and Advanced Python.

\r\n' >>> demo = r.text

测试beautiful soup库

>>> from bs4 import BeautifulSoup
>>> soup = BeautifulSoup(demo, "html.parser")

其中html.parser是解释器,对demo进行解析。

>>> print(soup.prettify())

 
  
   This is a python demo page
  
 
 
  

The demo python introduces several python courses.

Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses: Basic Python and Advanced Python .

beautifulsoup成功解析demo。
使用beautifulsoup:

from bs4 import BeautifulSoup
soup = BeautifulSoup('

data

', 'html.parser')

前一个参数为html类型的参数,后面是解释器

Beautiful Soup库的基本元素

Beautiful Soup库的理解

html:一堆“标签树”组成的
解析、遍历、维护“标签树”的功能库。
以其中一组“标签树”为例:

...

其中

...

:标签。成对出现。第一个是名称Name,属性Attributes,0个或多个,属性由键值对构成。
Beautiful Soup库的引用,常用为

from bs4 import BeautifulSoup

也可以直接

import bs4

Beautiful Soup类

HTML<->标签树<->BeautifulSoup类

Beautiful Soup库解析器

解析器 使用方法 条件
bs4的HTML解析器 BeautifulSoup(mk, 'html.parser') 安装bs4库
lxml的HTML解析器 BeautifulSoup(mk, 'lxml') pip install lxml
lxml的XML解析器 BeautifulSoup(mk, 'xml') pip install lxml
html5lib的解析器 BeautifulSoup(mk, 'html5lib') pip install html5lib

Beautiful Soup类的基本元素

基本元素 说明
Tag 标签,最基本的信息组织单元,分别用<>和表明开头和结尾
Name 标签的名字,

...

的名字是'p',格式:.name
Attributes 标签的属性,字典形式组织,格式:.attrs
NavigableString 标签内非属性字符串,<>...中字符串,格式.string
Comment 标签内字符串的注释部分,一种特殊的Comment类型

回顾demo.html
查看title标签

>>> from bs4 import BeautifulSoup
>>> import requests
>>> r = requests.get("https://python123.io/ws/demo.html")
>>> demo = r.text
>>> soup = BeautifulSoup(demo, "html.parser")
>>> soup.title
This is a python demo page

获取html中.a标签的内容

>>> tag = soup.a
>>> tag
Basic Python

任何标签都可以用这种方式获得。存在相同标签时,只返回第一个。
获取标签名字:

>>> soup.a.name
'a'
>>> soup.a.parent.name    #获取父亲名字
'p'
>>> soup.a.parent.parent.name
'body'

获取标签属性信息

>>> tag = soup.a
>>> tag.attrs
{'href': 'http://www.icourse163.org/course/BIT-268001', 'class': ['py1'], 'id': 'link1'}

属性是一个字典,可进行信息提取。

>>> tag.attrs['class']
['py1']
>>> tag.attrs['href']
'http://www.icourse163.org/course/BIT-268001'

标签属性的类型

>>> type(tag.attrs)

>>> type(tag)

标签的NavigableString元素

>>> soup.a
Basic Python
>>> soup.a.string
'Basic Python'
>>> soup.p

The demo python introduces several python courses.

>>> soup.p.string 'The demo python introduces several python courses.'

其类型为

>>> type(soup.p.string)

NavigableString可跨多个标签层次
标签的Comment元素

>>> newsoup = BeautifulSoup("

This is not a comment

", "html.parser") >>> newsoup.b.string 'This is a comment' >>> type(newsoup.b.string) >>> newsoup.p.string 'This is not a comment' >>> type(newsoup.p.string)

p标签是一个字符串,NavigableString;b标签是一个字符串,为Comment。

5种基本类型


BeautifulSoup库的理解

基于bs4库的HTML内容遍历方法

回顾demo.html:

>>> import requests
>>> r = requests.get("https://python123.io/ws/demo.html")
>>> r.text
'This is a python demo page\r\n\r\n

The demo python introduces several python courses.

\r\n

Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:\r\nBasic Python and Advanced Python.

\r\n' >>> demo = r.text

HTML基本格式


HTML标签树

3种遍历方法:

  • 下行遍历
  • 上行遍历
  • 平行遍历

标签树的下行遍历

属性 说明
.contents 子节点的列表,列表类型,将所有儿子节点存入列表
.children 子节点的迭代类型,与.contents类似,用于循环遍历儿子节点
.descendants 子孙节点的迭代类型,包含所有子孙节点,用于循环遍历

例如

>>> soup = BeautifulSoup(demo, "html.parser")
>>> soup.head
This is a python demo page
>>> soup.head.contents
[This is a python demo page]
>>> soup.body.contents
['\n', 

The demo python introduces several python courses.

, '\n',

Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses: Basic Python and Advanced Python.

, '\n'] >>> len(soup.body.contents) 5 >>> soup.body.contents[1]

The demo python introduces several python courses.

遍历儿子节点:

for child in soup.body.children:
    print(child)

遍历子孙节点:

for child in soup.body.descendants:
    print(child)

上行遍历

属性 说明
.parent 节点的父亲标签
.parents 节点先辈标签的迭代类型,用于循环遍历先辈节点
>>> soup.title.parent
This is a python demo page
>>> soup.html.parent
This is a python demo page

The demo python introduces several python courses.

Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses: Basic Python and Advanced Python.

#html先辈是其本身 >>> soup.parent #soup的先辈是空的

上行遍历:

>>> soup.parent
>>> for parent in soup.a.parents:
    if parent is None:
        print(parent)
    else:
        print(parent.name)

p
body
html
[document]

平行遍历

属性 说明
.next_sibling 返回按照HTML文本顺序的下一个平行节点标签
.previous_sibling 返回按照HTML文本顺序的上一个平行节点标签
.next_siblings 迭代类型,返回按照HTML文本顺序的后续所有平行节点标签
.previous_siblings 迭代类型,返回按照HTML文本顺序的前续所有平行节点标签

所有的平行遍历发生在同一个父节点下的各节点间。

>>> soup = BeautifulSoup(demo, "html.parser")
>>> soup.a.next_sibling
' and '

a标签的下一个平行标签是and。
平行遍历获得的下一个节点不一定是标签。

>>> soup.a.next_sibling.next_sibling
Advanced Python
>>> soup.a.previous_sibling
'Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:\r\n'
>>> soup.a.previous_sibling.previous_sibling
>>> soup.a.parent

Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses: Basic Python and Advanced Python.

标签树的循环平行遍历:
遍历后续节点

for sibling in soup.a.next_siblings:
    print(sibling)

遍历前序节点

for sibling in soup.a.previous_siblings:
    print(sibling)

基于bs4库的HTML格式化和编码

bs4的prettify()方法

>>> soup.prettify()
'\n \n  \n   This is a python demo page\n  \n \n \n  

\n \n The demo python introduces several python courses.\n \n

\n

\n Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:\n \n Basic Python\n \n and\n \n Advanced Python\n \n .\n

\n \n' >>> print(soup.prettify()) This is a python demo page

The demo python introduces several python courses.

Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses: Basic Python and Advanced Python .

为html文本增加换行符。
也可为每个标签进行处理。

>>> print(soup.a.prettify())

 Basic Python

bs4库编码为utf-8编码。可以很好地支持中文编码。


三种遍历方法

单元5:信息组织与提取方法

信息标记的三种形式

对信息进行标记,理解信息的类型与真实含义。

  • 标记后的信息形成组织结构,增加信息维度
  • 标记后的信息可用于通信、存储或展示
  • 标记的结构与信息一样具有重要价值
  • 标记后的侵袭更利于程序理解和运用

HTML的信息标记

通过预定义的<>...标签形式组织不同类型的信息
信息标记的形式:XML,JSON和YAML。

  • XML:eXtensible Markup Language,用尖括号标签表示信息。
  • JSON:JavaScript Object Notation,有类型的键值对构成的形式。例如:"name" : "abc",多个值用逗号隔开[,],如"name" : [ "abc", "def" ]。键值对可嵌套,用{,},如:
"name" : {
    "newName" : "abc",
    "oldName" : "def"
         }
  • YAML:YAML Ain't Markup Language,无类型的键值对。例如:name : abc。多个嵌套,使用缩进:
name : 
    newName : abc
    oldName : def

表达并列关系:前面加上“-”,表达整块数据,用“|”,注释加上“#”

三种标记形式的比较

  • XML:有效信息比例不高,大部分信息被标签占用。最早的通用信息标记语言,扩展性很好,繁琐。Internet上的信息传递与交互。
  • JSON:需要用双引号表达类型。信息有类型,适合程序处理(js),较XML简洁。移动应用云端与节点的信息通信,无注释。
  • YAML:信息五类型,文本信息比例最高,可读性好。各类系统的配置文件,有注释易读。

信息提取的一般方法

  • 方法一:完整解析信息的标记形式,再提取关键信息。
    需要表及解析器,例如:bs4库的标签树遍历
    优点:信息解析准确。
    缺点:提取过程繁琐,速度慢。
  • 方法二:无视标记形式,直接搜索关键信息。
    对信息的文本查找函数即可。
    优点:提取过程简洁,速度较快。
    缺点:提取结果准确性与信息内容相关。
  • 方法三:融合方法。
    结合形式解析与搜索方法,提取关键信息。
    需要标记解析器及文本查找函数。

实例:
提取HTML中所有URL连接
思路:

>>> for link in soup.find_all('a'):
    print(link.get('href'))

    
http://www.icourse163.org/course/BIT-268001
http://www.icourse163.org/course/BIT-1001870001

基于bs4库的HTML内容查找方法

<>.find_all(name, attrs, recursive, string, **kwargs)

返回一个列表类型,存储查找的结果。

  • name:对标签名称的检索字符串。
>>> soup.find_all('a')
[Basic Python, Advanced Python]

输出为列表类型。
同时查找a标签和b标签:

>>> soup.find_all(['a','b'])
[The demo python introduces several python courses., Basic Python, Advanced Python]

查找所有标签标签:

>>> for tag in soup.find_all(True):
    print(tag.name)

    
html
head
title
body
p
b
p
a
a

只显示b开头的标签:
(使用新库:正则表达式库)

>>> import re
>>> for tag in soup.find_all(re.compile('b')):
    print(tag.name)

    
body
b
  • attrs:对标签属性值的检索字符串,可标注属性检索。
    例如,带有course属性值的p标签:
>>> soup.find_all('p', 'course')
[

Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses: Basic Python and Advanced Python.

]

查找属性中id域为link1的元素:

>>> soup.find_all(id='link1')
[Basic Python]

查找属性时,必须精确复制信息,完整准确。如查找link就没有结果:

>>> soup.find_all(id='link')
[]

查找属性的部分信息:(需要正则表达式库),如包含link的标签信息。

>>> soup.find_all(id=re.compile('link'))
[Basic Python, Advanced Python]
  • recursive:是否对子孙全部搜索,默认为True。
>>> soup.find_all('a')
[Basic Python, Advanced Python]
>>> soup.find_all('a', recursive = False)
[]

结果说明,从soup开始的儿子节点没有a标签。

  • string:对<>...中字符串区域的检索字符串。
    必须要精确输入才能进行检索。如果需要检索包含的信息,使用正则 表达式库。
>>> soup
This is a python demo page

The demo python introduces several python courses.

Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses: Basic Python and Advanced Python.

>>> soup.find_all(string = 'Basic Python') ['Basic Python'] >>> import re >>> soup.find_all(string = re.compile("python")) ['This is a python demo page', 'The demo python introduces several python courses.']

简化形式:
(...)等价于.find_all(...)
soup(...)等价于soup.find_all(...)

扩展方法,与find_all()具有相同的参数。

方法 说明
<>.find() 搜索且只返回一个结果,字符串类型
<>.find_parents() 在先辈节点中搜索,返回列表类型
<>.find_parent() 在先辈节点中返回一个结果,字符串类型
<>.find_next_siblings() 在后续平行节点中搜索,返回列表类型
<>.find_next_sibling() 在后续平行节点中返回一个结果,字符串类型
<>.find_previous_siblings() 在前续平行节点中搜索,返回列表类型
<>.find_previous_sibling() 在前续平行节点中返回一个结果,字符串类型

“中国大学排名定向爬虫”实例

实例介绍

链接:http://www.zuihaodaxue.cn/zuihaodaxuepaiming2019.html

功能描述:
输入:大学排名URL链接
输出:大学排名信息的屏幕输出(排名,大学名称,总分)
技术路线:requests-bs4
定向爬虫:仅对输入的URL进行爬取

确定可行性:
确认返回的信息是否存在于html代码中。
打开浏览器,输入网站。
右键,查看源代码。快速搜索清华大学的信息。可以发现html代码中发现具体信息。
查看robots协议。http://www.zuihaodaxue.cn/robots.txt。页面不存在,可进行爬取。

步骤:

  • 第一步:从网络上获取大学排名网页内容。
    getHTMLText()
  • 第二步:提取网页内容中信息到合适的数据结构。
    fillUnivList()
  • 第三步:利用数据结构展示并输出结果。
    printUnivList()

数据结构:二维列表。

代码实现

import requests
from bs4 import BeautifulSoup
import bs4

def getHTMLText(url):  #从网络上获取大学排名网页内容
    try:
        r = requests.get(url, timeout = 30)  #获取信息,超时时间设定为30s
        r.raise_for_status()  #获取状态
        r.encoding = r.apprent_encoding  #改变编码方式
        return r.text
    except:
        return ""

def fillUnivList(ulist, html):  #提取网页内容中信息到合适的数据结构
    soup = BeautifulSoup(html, "html.parser")
    for tr in soup.find('tbody').children:  #观察html信息,所有信息都在tbody中的孩子的tr标签中
        if isinstance(tr, bs4.element.Tag):  #检测tr标签的类型,如果不是bs4库中的Tag类型,将进行过滤
            tds = tr('td')  #找出tr标签中tds
            ulist.append([tds[0].string, tds[1].string, tds[2].string])  #添加信息到ulist表中
    pass

def printUnivList(ulist, num):  #利用数据结构展示并输出结果
    print("{:^10}\t{:^6}\t{:^10}".format("排名", "学校名称", "总分"))  #打印表头
    for i in range(num):
        u = ulist[i]
        print("{:^10}\t{:^6}\t{:^10}".format(u[0], u[1], u[2]))
    print("Suc" + str(num))

def main():
    uinfo = []  #将大学信息放在一列表中
    url = 'http://www.zuihaodaxue.cn/zuihaodaxuepaiming2019.html'
    html = getHTMLText(url)   #将url中的内容转换为html格式
    fillUnivList(uinfo, html)
    printUnivList(uinfo, 20)  #打印前20所学校的信息

main()

注:现在网页源代码改变,运行会报错。

优化

已经实现了从网上获取相关信息并获取结果。中文字符对齐效果不好。
原因:format方法中相关格式。当中文字符宽度不够时,采用西文字符填充。
处理:采用中文字符额空格填充chr(12288)
printUnivList()中修改:

def printUnivList(ulist, num):
    tplt = "{0:^10}\t{1:{3}:^10}\t{2:^10}"
    print(tplt.format("排名","学校名称","总分", chr(12288)))
    for i in range(num):
        u=ulist[i]
        print(tplt.format(u[0],u[1],u[2], chr(12288)))

你可能感兴趣的:(【第2周】网络爬虫之提取)