Beautiful Soup4是一个 Python 库,用于从 HTML 和 XML 文件中提取数据。它是一个工具箱,通过解析文档为用户提供需要抓取的数据,Beautiful Soup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。你不需要考虑编码方式,除非文档没有指定一个编码方式,这时,Beautiful Soup就不能自动识别编码方式了。
BeautifulSoup安装
使用pip来安装BeautifulSoup。
另外要安装解析器,下列表格列出一些常用的解析器。
使用BeautifulSoup及四大对象
1、创建BeautifulSoup对象
from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content)
print(soup.prettify()) // 格式化输出
print(soup.get_text()) // 获取网页所有的文字内容
2、BeautifulSoup四大对象
Beautiful Soup 将复杂 HTML 文档转换成一个复杂的树形结构,每个节点都是 Python 对象,所有对象可以归纳为 4 种。
- Tag:HTML中的标签,简单来说就是html标签。
- NavigableString:简单来说就是标签里面的内容,它的类型是一个NavigableString,翻译过来叫可以遍历的字符串。
- BeautifulSoup:BeautifulSoup对象表示的是一个文档的全部内容,大部分时候,可以把它当作Tag对象,是一个特殊的Tag,我们可以分别获取它的类型、名称、以及属性
- Comment:一个特殊类型的NavigableString对象,其实输出的内容不包括注释符号
Tag对象示例
from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content)
print(soup.title)
print(soup.a)
print(soup.p)
运行输出如下图所示,但是发现好像这个网页不止一个a标签跟p标签,是因为它查找的是在所有内容中的第一个符合要求的标签,要想得到所有符合要求的标签,后面会介绍find_all函数。
在Tag对象中有两个重要的属性,name和attrs。
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content)
print(soup.a.attrs)
运行输出如下图所示,name输出的是标签的本身,attrs输出的是一个字典的类型,如果我们需要得到某个标签的某个属性可以使用字典一些方法去获取比如get方法,print(soup.p.get("class"))或者直接使用print(soup.p["class"])。
NavigableString代码示例:
from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content)
print(soup.a.string)
运行输出如下图,可以NavigableString类型的string方法轻松获取到了标签里面的内容。
BeautifulSoup代码示例:
from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content)
print(soup.name)
print(soup.attrs)
运行输出如下图所示:
Comment代码示例:
from bs4 import BeautifulSoup
htmlText = '#<a class="sister" href="http://example.com/elsie" id="link1"><!-- Comment --></a>'
soup = BeautifulSoup(htmlText)
print(soup.a.string)
运行输出如下,a 标签里的内容实际上是注释,但是如果利用 .string方法来输出它的内容,发现它已经把注释符号去掉了,所以这可能会给带来不必要的麻烦。
文档树遍历
tag里面的content属性可以将tag的子节点以列表的形式返回。通过遍历content.返回的列表来获取每一个子节点或者直接使用tag的children方法来获取。
from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content)
print(soup.head.contents)
for child in soup.head.contents:
print(child)
for child in soup.head.children:
print(child)
运行输出结果如下图所示:
tag里面的.descendants 属性可以对所有tag的子孙节点进行递归循环,和 children类似,我们也需要遍历获取其中的内容。
from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content)
for child in soup.descendants:
print(child)
运行结果输出如下图所示:
使用.string方法来获取内容,如果一个标签里面没有标签了,那么 .string 就会返回标签里面的内容。如果标签里面只有唯一的一个标签了,那么 .string 也会返回最里面的内容,如果标签里面没有内容则返回None。
from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content)
print(soup.a.string)
print(soup.title.string)
运行结果输出如下图所示:
使用strippend_strings 属性来获取多个内容还可以出除多余的空白字符,需要使用遍历来获取。
from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content)
for child in soup.stripped_strings:
print(child)
运行结果输出如下图所示:
通过元素的 .parents 属性可以递归得到元素的所有父辈节点。
from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"html.parser")
parentObject = soup.head.title
for parent in parentObject.parent:
print(parent.name)
运行结果输出如下图所示:
还有一些节点就不举例,跟其它获取节点一样也是需要遍历,而且使用的场景不同,兄弟节点使用.next_siblings或者.previous_sibling方法,前后节点使用.next_element或者.previous_element方法。
搜索文档树
find_all(name,attrs,recursive,text,\kwargs)**,find_all()方法用于搜索当前tag的所有tag子节点,并判断是否符合过滤条件。
最简单的过滤器是字符串,在搜索方法中传入一个字符串参数,beautifulsoup会查找与字符串完整匹配的内容,下面的例子用于查找文档中的所有a标签
from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")
print(soup.find_all("a"))
运行结果如下图所示:
如果传入正则表达式作为参数,beautiful soup会通过正则表达式的match()来匹配内容,下面例子中找出所有以b开头的标签,这表示b开头标签都应该被找到。
from bs4 import BeautifulSoup
import requests
import re
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")
for tag in soup.find_all(re.compile('^b')):
print(tag.name)
运行结果如下图所示:
如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有标签和标签。
from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")
print(soup.find_all(["a", "p"]))
运行结果如下图所示:
true 可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点。
from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")
for tag in soup.find_all(True):
print(tag.name)
运行结果如下图所示:
如果没有合适过滤器,那么还可以定义一个函数,函数只接受一个元素参数,如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则返回 False。
from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")
def has_class_but_no_id(tag):
return tag.has_attr('class') and not tag.has_attr('id')
print(soup.find_all(has_class_but_no_id))
输出结果如下图所示:
注意:如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索,如果包含一个名字为id的参数,Beautifulsoup会搜索每个tag的'id'值。
import re
from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")
print(soup.find_all(id='lg'))
print(soup.find_all(href=re.compile("hao123")))
运行结果如下图所示:
find(name , attrs , recursive , text , **kwargs ), 它与 find_all() 方法唯一的区别是 find_all() 方法的返回结果是值包含一个元素的列表,而 find() 方法直接返回结果。
CSS选择器
在使用BeautifulSoup中常用的有5中css选择器方法,用到的方法是 soup.select(),返回类型是列表。
from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")
print(soup.select("title"))
运行结果如下图所示:
from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")
print(soup.select(".mnav"))
运行结果如下图所示:
from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")
print(soup.select("#lg"))
运行结果如下图所示:
组合查找有点类似前端CSS选择器中的组合选择器,组合查找还可以使用子代选择器。
from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")
print(soup.select('div #lg'))
print(soup.select('div > a'))
运行结果如下图所示:
使用属性需要用中括号括起来,注意属性和标签属于同一节点,所以中间不能加空格,否则会无法匹配到。
from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")
print(soup.select('a[class="mnav"]'))
from bs4 import BeautifulSoup
import requests
url = "https://www.baidu.com"
content = requests.get(url).content
soup = BeautifulSoup(content,"lxml")
print(soup.select('span input[class="bg s_btn"]'))
运行结果如下图所示:
修改文档树
Beautiful Soup的强项是文档树的搜索,但同时也可以方便的修改文档树
from bs4 import BeautifulSoup
import requests
soup = BeautifulSoup('<b class="boldest">Extremely bold</b>',"lxml")
tag = soup.b
tag.name = "newtag"
tag['class'] = 'newclass'
tag['id'] = 1
print(tag)
del tag['class']
print(tag)
运行结果如下图所示:
给tag的 .string 属性赋值,就相当于用当前的内容替代了原来的内容,如果当前的tag包含了其它tag,那么给它的 .string 属性赋值会覆盖掉原有的所有内容包括子tag。
from bs4 import BeautifulSoup
import requests
markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
soup = BeautifulSoup(markup,"lxml")
tag = soup.a
tag.string = "New link text."
print(tag)
运行结果如下图所示:
Tag.append() 方法可以在tag中添加内容。
from bs4 import BeautifulSoup
import requests
soup = BeautifulSoup("<a>Foo</a>","lxml")
soup.a.append("Bar")
print(soup)
print(soup.a.contents)
运行结果如下图所示:
总结
本篇内容比较多,把 Beautiful Soup 的方法进行了大部分整理和总结,但是还不够完整只是列出一些常用的,如果需要完整的可以查看Beautiful Soup 官网的文档,希望对大家有帮助,掌握了 Beautiful Soup,一定会给你在数据爬取带来方便。
本文转载自微信公众号「爱编码的社畜」,可以通过以下二维码关注。转载本文请联系爱编码的社畜公众号。