在上一教程中,我向您展示了如何使用Requests模块使用Python访问网页 。 该教程涵盖了许多主题,例如发出GET / POST请求以及以编程方式下载诸如图像或PDF之类的内容。 该教程缺少的一件事是有关使用“请求”提取所需信息来刮取您访问的网页的指南。
在本教程中,您将学习Beautiful Soup ,这是一个从HTML文件提取数据的Python库。 本教程的重点是学习库的基础知识,下一个教程将介绍更高级的主题。 请注意,本教程的所有示例均使用Beautiful Soup 4。
安装
您可以使用pip
安装Beautiful Soup 4。 包名称为beautifulsoup4
。 它应该同时在Python 2和Python 3上运行。
$ pip install beautifulsoup4
如果您的系统上未安装pip,则可以直接下载Beautiful Soup 4源tarball并使用setup.py
安装。
$ python setup.py install
BeautifulSoup最初打包为Python 2代码。 当您安装它以供Python 3使用时,它会自动更新为Python 3代码。 除非您安装该软件包,否则不会转换该代码。 您可能会注意到以下一些常见错误:
- 当您在Python 3下运行代码的Python 2版本时,会出现“名为HTMLParser的模块”
ImportError
。 - 当您在Python 2下运行代码的Python 3版本时,会出现“名为html.parser的模块”
ImportError
。
可以通过卸载并重新安装Beautiful Soup来纠正上述两个错误。
安装解析器
在讨论可以用于Beautiful Soup的不同解析器之间的差异之前,让我们编写代码来创建汤。
from bs4 import BeautifulSoup
soup = BeautifulSoup("<html><p>This is <b>invalid HTML</p></html>", "html.parser")
BeautifulSoup
对象可以接受两个参数。 第一个参数是实际的标记,第二个参数是您要使用的解析器。 不同的解析器是: html.parser
, lxml和html5lib 。 lxml
解析器有两个版本,一个HTML解析器和一个XML解析器。
html.parser
是内置的解析器,在旧版本的Python中效果不佳。 您可以使用以下命令安装其他解析器:
$ pip install lxml
$ pip install html5lib
lxml
解析器非常快,可用于快速解析给定HTML。 另一方面, html5lib
解析器非常慢,但也非常宽松。 这是使用每个解析器的示例:
soup = BeautifulSoup("<html><p>This is <b>invalid HTML</p></html>", "html.parser")
print(soup)
# <html><p>This is <b>invalid HTML</b></p></html>
soup = BeautifulSoup("<html><p>This is <b>invalid HTML</p></html>", "lxml")
print(soup)
# <html><body><p>This is <b>invalid HTML</b></p></body></html>
soup = BeautifulSoup("<html><p>This is <b>invalid HTML</p></html>", "xml")
print(soup)
# <?xml version="1.0" encoding="utf-8"?>
# <html><p>This is <b>invalid HTML</b></p></html>
soup = BeautifulSoup("<html><p>This is <b>invalid HTML</p></html>", "html5lib")
print(soup)
# <html><head></head><body><p>This is <b>invalid HTML</b></p></body></html>
上面的示例概述的差异仅在解析无效HTML时才有意义。 但是,网络上的大多数HTML格式都是错误的,了解这些差异将帮助您调试一些解析错误并确定要在项目中使用哪个解析器。 通常, lxml
解析器是一个很好的选择。
美丽汤中的对象
Beautiful Soup将给定HTML文档解析为Python对象树。 您需要了解四个主要的Python对象: Tag
, NavigableString
, BeautifulSoup
和Comment
。
Tag
对象引用文档中的实际XML或HTML标签。 您可以使用tag.name
访问标签的名称。 您还可以将标签的名称设置为其他名称。 名称更改将在Beautiful Soup生成的标记中可见。
您可以分别使用tag['class']
和tag['id']
访问不同的属性,例如标签的class和id。 您还可以使用tag.attrs
访问整个属性字典。 您还可以添加,删除或修改标签的属性。 可以采用多个值的属性(如元素的class
将存储为列表。
标记中的文本在NavigableString
中存储为Beautiful Soup。 它具有一些有用的方法,例如replace_with("string")
来替换标签中的文本。 您还可以使用unicode()
将NavigableString
转换为unicode字符串。
Beautiful Soup还可以让您访问网页中的评论。 这些注释存储为Comment
对象,该对象基本上也是NavigableString
。
您已经在上一节中了解了BeautifulSoup
对象。 它用于表示整个文档。 由于它不是实际对象,因此没有任何名称或属性。
获取标题,标题和链接
您可以使用Beautiful Soup非常轻松地提取页面标题和其他此类数据。 让我们抓取有关Python的Wikipedia页面 。 首先,您将必须使用以下基于请求模块教程的代码来获取页面的标记, 以访问网页 。
import requests
from bs4 import BeautifulSoup
req = requests.get('https://en.wikipedia.org/wiki/Python_(programming_language)')
soup = BeautifulSoup(req.text, "lxml")
现在已经创建了汤,您可以使用以下代码获取网页的标题:
soup.title
# <title>Python (programming language) - Wikipedia</title>
soup.title.name
# 'title'
soup.title.string
# 'Python (programming language) - Wikipedia'
您也可以抓取网页以获得其他信息,例如主标题或第一段,其类或id
属性。
soup.h1
# <h1 class="firstHeading" id="firstHeading" lang="en">Python (programming language)</h1>
soup.h1.string
# 'Python (programming language)'
soup.h1['class']
# ['firstHeading']
soup.h1['id']
# 'firstHeading'
soup.h1.attrs
# {'class': ['firstHeading'], 'id': 'firstHeading', 'lang': 'en'}
soup.h1['class'] = 'firstHeading, mainHeading'
soup.h1.string.replace_with("Python - Programming Language")
del soup.h1['lang']
del soup.h1['id']
soup.h1
# <h1 class="firstHeading, mainHeading">Python - Programming Language</h1>
同样,您可以使用以下代码遍历文档中的所有链接或子标题:
for sub_heading in soup.find_all('h2'):
print(sub_heading.text)
# all the sub-headings like Contents, History[edit]...
导航DOM
您可以使用常规标签名称浏览DOM树。 链接这些标签名称可以帮助您更深入地导航树。 例如,您可以使用soup.pa
获得给定的Wikipedia页面第一段中的第一个链接。 可以使用soup.p.find_all('a')
访问第一段中的所有链接。
您还可以使用tag.contents
将标签的所有子级作为列表tag.contents
。 要使子级处于特定索引,可以使用tag.contents[index]
。 您还可以使用.children
属性来遍历标签的子级。
仅当您要访问标签的直接或第一级后代时, .children
和.contents
才有用。 要获取所有后代,可以使用.descendants
属性。
print(soup.p.contents)
# [<b>Python</b>, ' is a widely used ',.....the full list]
print(soup.p.contents[10])
# <a href="/wiki/Readability" title="Readability">readability</a>
for child in soup.p.children:
print(child.name)
# b
# None
# a
# None
# a
# None
# ... and so on.
您还可以使用.parent
属性访问元素的父.parent
。 同样,您可以使用.parents
属性访问元素的所有祖先。 顶级<html>
标记的父级是BeautifulSoup
对象本身,而其父级是None。
print(soup.p.parent.name)
# div
for parent in soup.p.parents:
print(parent.name)
# div
# div
# div
# body
# html
# [document]
您可以使用.previous_sibling
和.next_sibling
属性访问元素的上一个和下一个同级。
为了使两个元素成为同级,它们应具有相同的父元素。 这意味着元素的第一个子代将没有先前的同级。 同样,元素的最后一个子元素将没有下一个同级元素。 在实际的网页中,元素的上一个和下一个同级很有可能是换行符。
您还可以使用.previous_siblings
和.next_siblings
遍历元素的所有同级.next_siblings
。
soup.head.next_sibling
# '\n'
soup.p.a.next_sibling
# ' for '
soup.p.a.previous_sibling
# ' is a widely used '
print(soup.p.b.previous_sibling)
# None
您可以使用.next_element
属性.next_element
当前元素之后的元素。 要访问紧接当前元素的元素,请使用.previous_element
属性。
同样,您可以分别使用.previous_elements
和.next_elements
当前元素之前和之后的所有元素。
最后的想法
完成本教程后,您现在应该对不同HTML解析器之间的主要区别有了很好的了解。 现在,您还应该能够浏览网页并提取重要数据。 当您要分析给定网站上的所有标题或链接时,这将很有帮助。
在本系列的下一部分中,您将学习如何使用Beautiful Soup库搜索和修改DOM。