BeautifulSoup-4.2使用笔记

Github : BeautifulSoup-4.2使用笔记

参考文档:Beautiful Soup 4.2.0 中文文档

本文仅为常用的方法总结,内容来自官方文档,这里只是做了摘选,完整的内容还请查看官方文档。

BeautifulSoup是一个可以从html或者xml文件中提取数据的一个Python第三方库。我们可以在编写爬虫项目的时候,使用BeautifulSoup来帮助我们解析html文件。

1、安装BeautifulSoup

Python的第三方库一般都可以使用pip来进行安装,使用pip进行安装的方法是:

pip install beautifulsoup4

也可以使用 easy_install 来进行安装:

easy_install beautifulsoup4

BeautifulSoup 的发布协议允许将bs4 的代码打包在项目中,可以无须安装便可以使用。

2、安装解析器

BeautifulSoup 支持Python标准库中的HTML解析器(html.parser 无需安装),也支持一些第三方的解析器,其中一个是lxml,也支持html5lib,html5lib是纯Python实现,解析方式与浏览器相同

解析器的安装和BeautifulSoup的安装类似:

easy_install lxml
pip install lxml

easy_install html5lib
pip install html5lib

各个解析器的对比如下:

image
image

推荐使用lxml作为解析器,因为效率更高。在Python2.7.3之前的版本和Python3中3.2.2之前的版本,必须安装lxml或html5lib,因为那些Python版本的标准库中内置的HTML解析方法不够稳定。

3、使用BeautifulSoup

使用BeautifulSoup的方法和其他Python库的方法类似

from bs4 import BeautifulSoup

我们可以将我们采集到的网页内容传入BeautifulSoup的构造方法,就能得到一个文档的对象,也可以传入一段字符串或一个文件句柄。

soup = BeautifulSoup(html,'html.parser')
soup = BeautifulSoup(open('html.html'),'html.parser')
soup = BeautifulSoup('<html>data</html>','html.parser')

我们需要注意到,文档在传入构造方法之后均被转换成Unicode,并且HTML的实例都被转换成Unicode编码。

3.1、编码问题

任何HTML或XML文档都有自己的编码方式,比如ASCII或UTF-8,但是使用BeautifulSoup解析后,文档都被转换成了Unicode。

BeautifulSoup用了编码自动检测子库来识别当前文档编码并转换成Unicode编码。BeautifulSoup对象的.original_encoding属性记录了自动识别编码的结果。也可以通过from_encoding属性来指定编码格式。

soup = BeautifulSoup(html,'lxml',from_encoding='utf-8')

我们在编写爬虫的时候,一般都会遇到中文编码的显示问题,一般我们是将requests的返回,传入BeautifulSoup的构造方法中,有时候会出现中文乱码的情况,我们这里以百度官网https://www.baidu.com为例,看一下怎么解决编码问题。

import requests
from bs4 import BeautifulSoup
    
res = requests.get('https://www.baidu.com')
print(res.encoding)
soup = BeautifulSoup(res.text,'lxml')
print(soup)
print(soup.original_encoding)

当我们运行上面这些代码的时候,我们会发现我们输出的soup是乱码。我们查看requestes以及BeautifulSoup的相关文档,发现requests会自动将从服务器端获取到的内容自动转换成unicode,而BeauifulSoup也会将获取到内容自动转换成unicode。requests和BeautifulSoup会自行猜测原文的编码格式,大多数时候猜测出来的编码都是正确的,但也有猜错的情况,如果猜错了可以指定原文的编码。

所以我们认为是上面是由于编码格式猜测错误导致的中文乱码,我们查看res.encoding的输出结果为’ISO-8859-1’,然后在网页中的charset属性指出网页是’utf-8’编码格式的,所以我们需要指定原文的编码:

res.encoding = 'utf-8'

然后再传入BeautifulSoup中,我们输出的soup时中文显示就正常了。

我们在通过BeautifulSoup输出文档时,不管输入文档是什么编码方式,输出编码均为utf-8编码。如果我们不想使用utf-8格式输出文档,或者因为有些字符乱码而需要使用其他编码格式的时候,那我们只需要:

soup.encode('你想要的编码')
#or
soup.prettify('你想要的编码')
#prettify()除了可以指定编码外,最主要功能是对beautifulsoup的k语法分析树重新排版

ps:针对中文编码问题,我们也可以以bytes传递给BeautifulSoup,一般bs都能猜测到正确的编码。

import reuqests
from bs4 import BeautifulSoup
    
res = requests.get('https://www.baidu.com')
soup = BeautifulSoup(res.content,'lxml')
print(soup.prettify())

输出中文正常显示。

3.2、BeautifulSoup的对象

BeautifulSoup将复杂HTML文档转换成一个树形结构,每个节点都是Python对象,所有对象可以归为4种: Tag,NavigableString,BeautifulSoup,Comment。我们在编写爬虫代码的时候主要使用的是Tag和NavigableString对象,所以着重介绍一下。我们使用bs的最重要的目的是解析html文档,从里面提取出我们所需要的内容,当然bs还有很多其他非常好的功能。

3.2.1、Tag对象

Tag 对象与XML或HTML原生文档中的tag相同,我们也可以认为是树形结构中的节点。一个Tag可能包含多个字符串或其它的Tag,这些都是这个Tag的子节点。Beautiful Soup提供了许多操作和遍历子节点的属性.

我们可以通过 .tag_name 的方法来获得子节点,但是这种方法只能获得第一个匹配到的节点。

soup = BeautifulSoup('<b id="bold" class="boldest">Extremely bold</b>')
tag_b = soup.b
type(tag_b)
#<class 'bs4.element.Tag'>

每个tag都有自己的名字,可以通过 .name 来获取:

tag_b.name
#'b'

一个tag可能有很多个属性,例如tag_b有一个 “id”的属性,值为“blod”,有一个“class” 的属性,值为 “boldest” ,tag的属性的操作方法与字典相同。也可以通过使用 .attrs 的方式,获得所有的属性。

HTML4定义了一系列可以包含多个值的属性,在HTML5中移除了一些,却增加更多,最常见的多值的属性是 class (一个tag可以有多个CSS的class), 还有一些属性rel,rev,accept-charset,headers,accesskey 等等。在BeautifulSoup中多值属性的返回类型是list。

tag_b['class']
#['boldest']
tag_b.attrs
#{'id': 'bold', 'class': ['boldest']}

css_soup = BeautifulSoup('<p class="body strikeout"></p>')
css_soup.p['class']
#["body", "strikeout"]
.parents & .parent

tag对象的 .parent属性,可以获取某个节点的父节点。例如,标签是标签的父节点,我们可以这么获得。

title_tag = soup.title
title_tag
#<title>This is a story</title>

title_tag.parent
#<head><title>This is a story</title></head>

tag对象的 .parents属性是递归返回当前tag对象的所有父辈节点。

.contents & .children

tag的 .contents 属性可以将tag的子节点以列表的方式输出:

soup = BeautifulSoup('<a><b class="boldest">Extremely bold</b>123</a>')
soup.a.contents
#[<b class="boldest">Extremely bold</b>, '123']

也可以通过tag的 .children 生成器,对tag的子节点进行循环:

for child in soup.a.children:
    print(child)
    
#<b class="boldest">Extremely bold</b>
#123
.string

如果tag只有一个 NavigableString 类型子节点,也就是只有一个字符串节点,没有其他tag,那么这个tag可以使用 .string 得到子节点:

soup.a.b.string

#'Extremely bold'
.strings & .stripped_strings

如果tag中包含多个字符串,可以使用 .strings 来循环获取:

soup = BeautifulSoup('<a><b class="boldest">Extremely bold</b><b class="boldest">Extremely bold1\n</b>123</a>')
for string in soup.a.strings:
    print(string)
    
#Extremely bold
#Extremely bold1
#
#123

输出的字符串中可能包含了很多空格或空行,使用 .stripped_strings 可以去除多余空白内容:

soup = BeautifulSoup('<a><b class="boldest">Extremely bold</b><b class="boldest">Extremely bold1\n</b>123</a>')
for string in soup.a.stripped_strings:
    print(string)
    
#Extremely bold
#Extremely bold1
#123
get_text()

如果只想得到tag中包含的文本内容,那么可以调用get_text()方法,这个方法获取到tag中包含的所有文字内容包括子孙tag中的内容,并将结果作为Unicode字符串返回:

soup = BeautifulSoup('<a><b class="boldest">Extremely bold</b>123</a>')
soup.a.get_text()
soup.a.b.get_text()

#'Extremely bold123'
#'Extremely bold'

注意: Beautiful Soup中字符串节点不支持以上这些属性,因为字符串没有子节点

3.2.2、NavigableString对象

字符串常被包含在tag内,Beautiful Soup用 NavigableString类来包装tag中的字符串。一个 NavigableString字符串与Python中的Unicode字符串相同。

tag中包含的字符串不能编辑,但是可以被替换成其它的字符串,用 replace_with() 方法:

soup = BeautifulSoup('<b class="boldest">123</b>')
soup.b.string.replace_with('replace')
soup.b

#<b class="boldest">replace</b>

注:一个字符串不能包含其它内容(tag能够包含字符串或是其它tag),字符串不支持 .contents 或 .string 属性或 find() 方法。

3.3、搜寻目标Tag

在我们获得正确的网页返回内容之后,我们需要搜寻到正确的目标Tag来获得我们想要的内容。Beautiful Soup定义了很多搜索方法,这里着重介绍2个: find() 和 find_all() ,其它方法的参数和用法类似。

3.3.1、过滤器

介绍 find_all() 方法前,先介绍一下过滤器的类型,这些过滤器贯穿整个搜索的API。过滤器可以被用在tag的name中,节点的属性中,字符串中或他们的混合中。

我们使用下面的html,作为我们解析的内容

html = '''
<html>
    <body>
        <a id="link1" class="a1">
            <b id="bold1" class="b1">123</b>
            <b class="b2">456</b>
        </a>
        
        <a id="link2" class="a2">
            <b id="bold1" class="b1">321</b>
            <b class="b2">654</b>
        </a>
    </body>
</html>
'''

soup = BeautifulSoup(html)
字符串

最简单的过滤器是字符串,在搜索方法中传入一个字符串参数,Beautiful Soup会查找与字符串完整匹配的内容,下面的例子用于查找文档中所有的<b>标签:

soup.find_all('b')

#[<b class="b1" id="bold1">123</b>, <b class="b2">456</b>, <b class="b1" id="bold1">321</b>, <b class="b2">654</b>]

字符串过滤器的主要功能是筛选出 tag.name=‘字符串’ 的节点

正则表达式

如果传入正则表达式作为参数,Beautiful Soup会通过正则表达式的 match() 来匹配内容。下面例子中找出所有以b开头的标签,这表示<body>和<b>标签都应该被找到:

import re
for tag in soup.find_all(re.compile("^b")):
    print(tag.name)
    
#body
#b
#b
#b
#b
列表

如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回。下面代码找到文档中所有<a>标签和<b>标签:

soup.find_all(['a','b'])

#[<a class="a1" id="link1"><b class="b1" id="bold1">123</b><b class="b2">456</b></a>, <b class="b1" id="bold1">123</b>, <b class="b2">456</b>, <a class="a2" id="link2"><b class="b1" id="bold1">321</b><b class="b2">654</b></a>, <b class="b1" id="bold1">321</b>, <b class="b2">654</b>]
函数方法

如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数,如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则反回 False。

下面方法校验了当前tag,如果包含 class 属性却不包含 id 属性,那么将返回 True:

def has_class_but_no_id(tag):
    return tag.has_attr('class') and not tag.has_attr('id')

将这个方法作为参数传入 find_all() 方法,将得到所有满足条件的标签:

soup.find_all(has_class_but_no_id)

#[<b class="b2">456</b>, <b class="b2">654</b>]

可以看到返回结果中,只有满足“含有class属性,不含有id属性”的标签被返回。

3.3.2、find_all()

find_all( name , attrs , recursive , text , **kwargs )

find_all() 方法搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件。下面有几个例子,根据find_all()方法的参数,进行理解。

name参数

name参数在之前的介绍中有所提及,类似字符串过滤器。name参数查找所有名字为 name 的tag,字符串对象会被自动忽略掉。

soup.find_all("title")
#获得tag.name为“title”的标签

attrs参数

attrs参数是根据tag属性,对标签进行查找。

soup.find_all(attrs={'class':'b1'})
#获得满足 class=“b1” 的所有标签

soup.find_all('b',attrs={'class':'b1',id='bold1'})
#获得满足 class=“b1” id="bold1" 的所有<b>标签
keyword参数

如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索,如果包含一个名字为 id 的参数,Beautiful Soup会搜索每个tag的”id”属性。

soup.find_all(id="link2")
#搜寻所有满足 id="link2" 的标签

如果传入 href 参数,Beautiful Soup会搜索每个tag的”href”属性:

soup.find_all(href=re.compile("^https://"))
#搜寻所有 href为https 开头的标签

搜索指定名字的属性时可以使用的参数值包括 字符串 , 正则表达式 , 列表, True ,参照过滤器的使用方法。

soup.find_all(id=True)
#搜索查找所有包含 id属性的标签,无论 id 的值是什么

可以使用多个指定名字的参数可以同时过滤tag的多个属性:

soup.find_all(href=re.compile("^https://"),id='link')
#搜索查找所有id为link href为https开头的标签

注意:有些tag属性不能在搜索中使用,例如html5中的data-*属性

soup = BeautifulSoup('<div data-foo="value">foo!</div>')
soup.find_all(data-foo = "value")
#SyntaxError: keyword can't be an expression

但是可以通过使用 find_all() 方法中的 attrs 参数来获得data-*的属性内容

soup.find_all(attrs={'data-foo':'value'})
#[<div data-foo="value">foo!</div>]
按css搜索

按照css类名搜索tag的功能非常实用,但标识css类名的关键字 class 在Python中是保留字,使用 class 做参数会导致语法错误。从BeautifulSoup的4.1.1版本开始,可以通过 class_ 参数搜索有指定css类名的tag。

soup.find_all('a',class_='test')
#搜索出所有class属性为‘test'的<a>标签

class_ 参数同样接受不同类型的 过滤器 ,例如:字符串,正则表达式,函数方法以及True。使用方法和上面在讲过滤器的时候的使用方法类似,我们可以自行验证。

tag的 class 是多值属性,按照css类名搜索tag的时候,可以分别搜索tag中的每个类名。

soup = BeautifulSoup('<p class="body table"></p>')
soup.find_all('p',class_='body')
#['<p class="body table"></p>']

soup.find_all('p',class_='table')
#['<p class="body table"></p>']

同时使用 class_ 也可以进行对css类名的完全匹配:

soup.find_all('p',class_='body tabel')
#['<p class="body table"></p>']

注意:如果在使用完全匹配class值的时候,如果css类名的顺序与实际不符,则不会搜索到正确的结果。

string参数

通过string参数可以搜索文档中的字符串内容,它同样接受不同类型的过滤器,string参数接受字符串,正则表达式,列表和True,同样也接受函数方法过滤。

注意:如果只使用了string参数,返回结果列表中的元素只包含满足搜索条件的字符串,也可以说是NavigableString对象,而不是tag对象,可以观察下面的这两个语句进行对比。

soup.find_all(string = 'test')
#['test']

soup.find_all('a',string='test')
#[<a href='http://www.test.com'>test</a>]

limit参数

find_all() 方法返回的是全部的搜索结构,那么如果文档树很大的时候搜索所需要的时间也会非常大,如果我们不需要全部的搜索结果,那么我们可以使用 limit 参数来限制返回结果的数量。

例如如果文档中有5个满足搜索条件的tag,当我们使用 limit 参数的时候就可以限制它的返回数量。

soup.find_all('a',limilt=2)
#只返回满足搜索条件的前两个
recursive参数

在调用 find_all() 方法时,默认的是搜索当前tag下的所有子孙节点,查找符合我们要求的tag。如果我们只想搜索当前tag的直接子节点,可以使用参数 recursive = False

html = '<html>
            <head>
                <title>
                    Test Title!
                </title>
            </head>
        </html>'
soup = BeautifulSoup(html,'lxml')
soup.html.find_all('title')
#[<title>
#     The Dormouse's story
#    </title>]
soup.html.find_all('title',recursive=False)
#[]

标签在 <html> 标签下,但并不是直接子节点, 标签才是直接子节点。在允许查询所有后代节点时 BeautifulSoup 能够查找到 标签。 但是使用了 recursive=False 参数之 后,只能查找直接子节点,这样就查不到 标签了。

find_all()方法的简写

find_all() 方法是BeautifulSoup中最常用的搜索方法,所以BeautifulSoup的作者定义了他的简写方法,BeautifulSoup对象和tag对象可以被当作一个方法来使用,这个方法的执行结果和调用这个对象的 find_all() 方法的结果相同。

soup.find_all('a')
soup('a')

上面的这两行代码的执行结果是一样的,同样下面的这两行代码的执行结果也是一样的。

soup.title.find_all(string=True)
soup.title(string=True)
3.3.3、find()

find( name , attrs , recursive , string , **kwargs)

find()方法的使用

find() 方法的参数使用与 find_all() 的类似,这里不再重新阐述。

find_all() 方法将返回文档中所有符合条件的tag,当我们只想得到一个结果时,可以使用limit参数,或者可以直接使用 find() 方法,例如下面的两行代码是等价的。

soup.find_all('title',limit=1)
soup.find('title')

上面两行代码唯一的区别就是,find_all() 方法返回的是包含一个元素的列表,而 find() 方法是直接返回的结果。如果没有找到满足搜索条件的tag对象, find_ll() 返回的是一个空列表, find() 找不到目标返回的是None。

find() 方法的简写

soup.head.title 是 调用 find() 方法的简写,这个简写的原理就是多次调用当前tag的 find() 方法,下面这两句代码是具有相同效果的。

soup.head.title
soup.find('head').find('title')
3.3.4、其他搜索API

find()find_all() 方法是最常使用的搜索文档的方法,但是BeautifulSoup还给我们提供了10个可以使用搜索功能的API,其中五个与 find_all() 方法的参数相同,另外五个与 find() 方法的参数类似,这里不再做举例验证。

find_parents() & find_parent()

find_parents(name, attrs, recursive, string, **kwargs)

find_parent(name, attrs, recursive, string, **kwargs)

find()find_all() 方法是搜索当前节点的子孙节点,find_parent() 和 find_parents() 则是用来搜索当前节点的父辈节点。同样两个方法的区别是,在参数缺省的时候, find_parent() 只返回直接父亲节点(1个), find_parents() 迭代返回所有的父辈节点。

find_next_siblings() & find_next_sibling()

find_next_siblings(name, attrs, recursive, string, **kwargs)

find_next_sibling(name, attrs, recursive, string, **kwargs)

这两个方法通过.next_siblings 属性对当前tag节点后面所有满足搜索条件的兄弟tag节点进行迭代,两个方法的区别是 find_next_siblings() 返回所有满足搜索条件的兄弟节点,find_next_sibling() 只返回满足搜索条件的第一个兄弟节点。

find_previous_siblings() & find_previous_sibling()

find_previous_siblings(name, attrs, recursive, string, **kwargs)

find_previous_sibling(name, attrs, recursive, string, **kwargs)

这两个方法是通过.previous_siblings属性对当前tag节点前面所有满足搜索条件的兄弟tag节点进行迭代,两个方法的区别是 find_previous_siblings() 返回所有满足搜索条件的兄弟节点, find_previous_sibling() 只返回满足走索条件的第一个兄弟节点。

find_all_next() & find_next()

find_all_next(name, attrs, recursive, string, **kwargs)

find_next(name, attrs, recursive, string, **kwargs)

这2个方法通过 .next_elements 属性对当前节点之后的 tag 和字符串进行迭代, find_all_next() 方法返回所有符合条件的节点,find_next() 方法返回第一个符合条件的节点。

find_all_previous() & find_previous()

find_all_previous(name, attrs, recursive, string, **kwargs)

find_previous(name, attrs, recursive, string, **kwargs)

这2个方法通过 .previous_elements 属性对当前节点前面的tag和字符串进行迭代, find_all_previous() 方法返回所有符合条件的节点, find_previous() 方法返回第一个符合条件的节点。

3.3.5、css选择器

BeautifulSoup支持大部分的CSS选择器,在tag或者BeautifulSoup对象的.select()方法中传入字符串,就可以使用css选择器的语法找到目标tag。

熟练使用css的人,可以选择使用css选择器来进行搜索。

css选择器的使用方法,可以自行查找官方文档的相关使用,本人由于掌握的不好,便不在这里班门弄斧了。

4、如何提高效率

BeautifulSoup解析文档的速度不会比它所依赖的解析器的速度更快,如果对时间要求比较高,那我们应该直接使用 lxml 作为BeautifulSoup所依赖的解析器,用 lxml 作解析器比用 html5lib 或者python的内置解析器速度要快很多。

在安装cchardet后对文档解码的编码检测也会更快。

此外一个更好提高速率的方法是,解析部分文档虽然不会节省多少解析时间,但是会节省很多内存,并且搜索时也会变得更快。

解析部分文档 (SoupStrainer)

如果仅仅是想查找文章中的<a>标签而对整个文档进行解析,实在是浪费内存和时间,最快的方法是从一开始就将出了<a>标签之外的内容都忽略掉。 SoupStrainer 类可以定义文档的某段内容,这样搜索文档时就不必解析整个文档,只会解析在 SoupStrainer 中定义过的文档。这样我们只需要构建一个 SoupStrainer 对象,并作为 parse_only 的参数传递个BeautifulSoup构造方法即可。

SoupStrainer 具有与典型搜索方法相同的参数,name,attrs,recursive,string,**kwargs,下面举例说明三种 SoupStrainer 对象。

from bs4 import SoupStrainer

only_a_tags = SoupStrainer('a')

only_tags_with_id_link2 = SoupStrainer(id='link2')

def is_short_string(string):
    return len(string) < 10
    
only_short_strings = SoupStrainer(string=is_short_string)

这里使用官方文档中给出的一个例子来进行验证。

html_doc = """
  <html><head><title>The Dormouse's story</title></head>
      <body>
  <p class="title"><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>
  """
  
print(BeautifulSoup(html_doc,'lxml',parse_only=only_a_tags).prttify())
#<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> 

print(BeautifulSoup(html_doc,'lxml',parse_only=only_tags_with_id_link2).prettify())
#<a class="sister" href="http://example.com/lacie" id="link2">
# Lacie
#</a>

print(BeautifulSoup(html_doc,'lxml',parse_only=only_short_strings).prettify())
#Elsie
#,
#Lacie
#and
#Tillie
#...

此外还可以将SoupStrainer传入搜索方法中,可以实现同样的效果

soup = BeautifulSoup(html_doc,'lxml')
print(soup.find_all(only_short_strings))

#['\n', '\n', 'Elsie', ',\n', 'Lacie', ' and\n', 'Tillie', '\n', '...', '\n']
文档解析错误

文档解析错误有两种,一种是崩溃,BeautifulSoup尝试解析一段文档结果却抛除了异常,通常是 HTMLParser.HTMLParseError 。还有一种异常情况是BeautifulSoup解析后的文档树看起来与原来的内容相差很多,这些错误几乎都不是BeautifulSoup的原因,这是因为BeautifulSoup没有包含任何文档解析代码。异常产生自被依赖的解析器,如果解析器不能很好的解析出当前的文档,那么最好的办法是换一个解析器。

最常见的解析错误是 HTMLParser.HTMLParseError: malformed start tagHTMLParser.HTMLParseError: bad end tag 。这都是由Python内置的解析器引起的,解决方法是安装lxml或html5lib。

最常见的异常现象是当前文档找不到指定的Tag,而这个Tag光是用眼睛就足够发现的了。 find_all() 方法返回 [],而 find() 方法返回 None。这是Python内置解析器的又一个问题:解析器会跳过那些它不知道的tag,解决方法还是安装lxml或html5lib。

所以最好的方法就是直接使用lxml作为BeautifulSoup的解析器,这样既不容易发生文档解析错误,又可以提高文档解析速度。

代码诊断 (diagnose)

如果想知道BeautifulSoup到底是怎么处理一份文档的,可以将文档传入diagnose()方法中,BeautifulSoup会输出一段报告,说明不同的解析器是如何处理这段文档的,并标出当前解析过程所使用的解析器。

from bs4.diagnose import diagnose

diagnose('<html><head><title>Test</title></head></html>')

#Diagnostic running on Beautiful Soup 4.7.1
#Python version 3.7.1 (v3.7.1:260ec2c36a, Oct 20 2018, 03:13:28) 
#[Clang 6.0 (clang-600.0.57)]
#I noticed that html5lib is not installed. Installing it may help.
#Found lxml version 4.2.6.0
#
#Trying to parse your markup with html.parser
#Here's what html.parser did with the markup:
#<html>
# <head>
#  <title>
#   Test
#  </title>
# </head>
#</html>
#--------------------------------------------------------------------------------
#Trying to parse your markup with lxml
#Here's what lxml did with the markup:
#<html>
# <head>
#  <title>
#   Test
#  </title>
# </head>
#</html>
#--------------------------------------------------------------------------------
#Trying to parse your markup with lxml-xml
#Here's what lxml-xml did with the markup:
#<?xml version="1.0" encoding="utf-8"?>
#<html>
# <head>
#  <title>
#   Test
#  </title>
# </head>
#</html>
#--------------------------------------------------------------------------------

通过使用diagnose()方法输出结果可以帮助我们找到出现某些问题的原因,如果还不能解决可以Google或者其他方法解决。

5、使用更高版本应注意的内容

好像是在4.3以及更高版本中(不是很确定,但我用的4.7版本是这样的),搜索方法find_all() find() SoupStrianer()… 等等中的string参数修改为text参数,string参数仍然可以继续使用,但可能会出现一下问题,这个等读完源码之后在进行更新。下面这两句代码是等价的

soup.find_all(string=True)

soup.find_all(text=True)

有问题请联系我 欢迎各位大佬指正

邮箱:13402826535@163.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值