在 Python 中使用 BeautifulSoup 抓取网页

694 阅读9分钟

什么是网页抓取?

Web 抓取Web 数据提取是从 Internet 收集信息的过程。它可以是来自特定网站的数据的简单复制粘贴,也可以是来自具有实时数据的网站的高级数据集合。

有些网站不介意提取他们的数据,而有些网站则严格禁止在他们的网站上提取数据。

如果您出于教育目的抓取网站,那么您可能没有任何问题,但如果您开始大型项目,那么请务必查看网站的服务条款

我们为什么需要它?

并非所有网站都有获取内容的 API,因此要提取内容,我们只剩下一个选项,那就是抓取内容。

网页抓取的步骤

  • 检查数据源
  • 获取 HTML 内容
  • 使用 Beautifulsoup 解析 HTML

现在让我们继续安装本教程所需的依赖项。

安装依赖项

我们将安装requests帮助我们获取网站 HTML 内容并beautifulsoup4解析 HTML 的库。

pip install requests beautifulsoup4

抓取网站

我们将抓取关于Python Programming Language的维基百科文章。这个网页包含了几乎所有类型的 HTML 标签,这将有助于我们测试 BeautifulSoup 的各个方面。

1. 检查数据来源

在编写任何 Python 代码之前,您必须仔细查看要执行网络抓取的网站

您需要了解网站的结构才能提取项目的相关信息。

彻底浏览网站,执行基本操作,了解网站的工作原理,并检查 URL、路由、查询参数等。

使用开发人员工具检查网页

现在,是时候使用开发人员工具检查网站的DOM (文档对象模型)了。

开发人员工具有助于理解网站的结构。它能够做很多事情,从检查加载的 HTML、CSS 和 JavaScript 到显示页面请求的资产以及加载它们所花费的时间。所有现代浏览器都安装了开发者工具。

要打开开发工具, 只需右键单击网页并单击 **“检查” **选项。此过程适用于 Windows 上的 Chrome 浏览器或只需应用以下键盘快捷键 -

CtrlShift+I

对于macOS,我认为命令是 -

+I

现在是时候查看我们要抓取的网页的 DOM 了。

DOM

我们可以在右侧看到 HTML,它表示我们在左侧看到的页面结构。

2.获取HTML内容

我们需要requests库来抓取我们已经安装在系统中的网站的 HTML 内容。

接下来,打开您最喜欢的 IDE 或代码编辑器,只需几行 Python 代码即可检索站点的 HTML。

import requests

url = "https://en.wikipedia.org/wiki/Python_(programming_language)"

# Step 1: Get the HTML
r = requests.get(url)
htmlContent = r.content

# Getting the content as bytes
print(htmlContent)

# Getting the encoded content
print(r.text)

如果我们打印,r.text我们将得到与我们之前使用浏览器的开发人员工具检查的 HTML 相同的输出。现在我们可以在我们的 Python 脚本中访问网站的 HTML。

现在让我们使用 Beautiful Soup 解析 HTML

3. 用 Beautifulsoup 解析 HTML

我们已经成功地抓取了网站的 HTML,但是如果我们查看它就会出现问题,因为到处都是 HTML 元素,属性和标签散落在各处。因此,我们需要使用 Python 代码解析冗长的响应,以使其更具可读性和可访问性。

Beautiful Soup帮助我们解析结构化数据。它是一个用于从数据库中提取数据的 Python 库HTML 和 XML 文件.

import requests
from bs4 import BeautifulSoup

url = "https://en.wikipedia.org/wiki/Python_(programming_language)"

# Step 1: Get the HTML
r = requests.get(url)
content = r.content

# Step 2: Parse the HTML
soup = BeautifulSoup(content, 'html.parser')
print(soup)

在这里,我们在之前的代码中添加了一些行。我们为 Beautiful Soup 添加了一个导入语句,然后创建了一个 Beautiful Soup 对象,该对象content的值为r.content

我们在 Beautiful Soup 对象中添加的第二个参数是html.parser。您必须为 HTML 内容选择正确的解析器。

按 ID 查找元素

HTML 网页中的元素可以分配给它们一个id属性。它使页面中的元素具有唯一可识别性。

Beautiful Soup 允许我们通过 ID 找到特定的 HTML 元素

import requests
from bs4 import BeautifulSoup

url = "https://en.wikipedia.org/wiki/Python_(programming_language)"

r = requests.get(url)
content = r.content

soup = BeautifulSoup(content, 'html.parser')

id_content = soup.find(id="firstHeading")

我们可以使用 .prettify() 任何漂亮的 soup 对象来美化 HTML 以便于查看。在这里我们从上面调用 .prettify() id_content变量。

print(id_content.prettify())

.prettify() 注意:我们不能在使用方法的时候使用 .find_all()

按标签查找元素

在 HTML 网页中,我们会遇到很多 HTML 标签,我们可能需要驻留在这些标签中的数据。就像我们想要驻留在 (anchor) 标签中的超链接或想要从(paragraph) 标签 "a" 中抓取描述一样。 "p"

import requests
from bs4 import BeautifulSoup

url = "https://en.wikipedia.org/wiki/Python_(programming_language)"

r = requests.get(url)
content = r.content

soup = BeautifulSoup(content, 'html.parser')

# Getting the first <code> tag
find_tag = soup.find("code")
print(find_tag.prettify())

# Getting all the <pre> tag
all_pre_tag = soup.find_all("pre")

for pre_tag in all_pre_tag:
    print(pre_tag)

按 HTML 类名查找元素

我们可以在 HTML 网页中看到数百个元素,例如<div><p>或者<a>带有一些类, 通过这些类,我们可以访问特定元素中存在的全部内容

Beautiful Soup 提供了一个class_ 参数来查找具有指定类名的元素中存在的内容。

import requests
from bs4 import BeautifulSoup

url = "https://en.wikipedia.org/wiki/Python_(programming_language)"

r = requests.get(url)
content = r.content

soup = BeautifulSoup(content, 'html.parser')

# Getting the "div" element with class name "mw-highlight"
class_elem = soup.find("div", class_="mw-highlight")
print(class_elem.prettify())

我们在美丽的汤对象中提供的第一个参数是元素 我们提供的第二个参数是类名

按文本内容和类名查找元素

Beautiful Soup 提供了一个字符串参数,允许我们搜索字符串而不是标签。我们可以传入字符串、正则表达式、列表、函数或值 True

# Getting all the strings whose value is "Python"
find_str = soup.find_all(string="Python")
print(find_str)

.........
['Python', 'Python', 'Python', 'Python', 'Python', 'Python', 'Python', 'Python', 'Python', 'Python', 'Python']

我们还可以找到其值与字符串参数的指定值匹配的 标签。****

find_str_tag = soup.find_all("p", string="Python")

在这里,我们正在寻找<p>“Python” 必须存在的标签。但是如果我们继续并尝试打印结果,那么我们将得到一个空结果

print(find_str_tag)
.........
[]

这是因为当我们使用*string=*时,我们的程序看起来与我们提供的值完全相同。任何自定义、空格、拼写差异或大小写都会阻止元素匹配。

如果我们提供准确的值,那么程序将成功运行。

find_str_tag = soup.find_all("span", string="Typing")
print(find_str_tag)

.........
[<span class="toctext">Typing</span>, <span class="mw-headline" id="Typing">Typing</span>]

传递函数

在上面的部分中,当我们试图找到<p>包含字符串 “Python” 的标签时,我们感到很失望。

但是 Beautiful Soup 允许我们将函数作为参数传递。我们可以在使用该功能后修改上面的代码以使其完美运行。

# Creating a function
def has_python(text):
    return text in soup.find_all("p")

find_str_tag = soup.find_all("p", string=has_python("Python"))
print(len(find_str_tag))

在这里,我们创建了一个名为的函数has_python,它接受text一个参数,然后返回所有标签中存在的文本<p>

接下来,我们将该函数传递给字符串参数并将字符串 **“Python” **传递给它。然后我们打印了所有标签中 **“Python” **的出现次数<p>

81

从 HTML 元素中提取文本

如果我们不想要附加了 HTML 标签的内容怎么办。如果我们想要来自元素和标签的干净简单的文本数据怎么办?

我们可以使用 .text或仅 .get_text() 返回我们传入 Beautiful Soup 对象的 HTML 元素的文本内容。

import requests
from bs4 import BeautifulSoup

url = "https://en.wikipedia.org/wiki/Python_(programming_language)"

r = requests.get(url)
content = r.content

soup = BeautifulSoup(content, 'html.parser')

table_elements = soup.find_all("table", class_="wikitable")

for table_data in table_elements:
    table_body = table_data.find("tbody")

    print(table_body.text) # or

    print(table_body.get_text())

我们将把整个表格作为文本格式的输出。但是文本之间会有很多空格,所以我们需要剥离该数据并通过简单地使用 .strip method删除空格。

print(table_body.text.strip())

还有其他方法也可以删除空格。在这里查看。

从 HTML 元素中提取属性

一个 HTML 页面有很多属性,比如href、src、style、title 等等。由于 HTML 网页包含大量具有href<a>属性的标签,因此我们将抓取我们网站上存在的所有href属性。********

我们不能像上面的例子那样抓取属性。

# Accessing href in the main content of the HTML page
anchor_in_body_content = soup.find(id="bodyContent")

# Finding all the anchor tags
anchors = anchor_in_body_content.find_all("a")

# Looping over all the anchor tags to get the href attribute
for link in anchors:
    links = link.get('href')
    print(links)

我们简单地遍历了<a>HTML 页面主要内容中的所有标签,然后使用 a .get('href') 获取所有 href 属性。

您也可以对属性执行相同的操作src

# Accessing src in body of the HTML page
img_in_body_content = soup.find(id="bodyContent")

# Finding all the img tags
media = img_in_body_content.find_all("img")

# Looping over all the img tags to get the src attribute
for img in media:
    images = img.get('src')
    print(images)

访问父元素和兄弟元素

Beautiful Soup 允许我们仅通过使用属性来访问元素的父元素 .parent

import requests
from bs4 import BeautifulSoup

url = "https://en.wikipedia.org/wiki/Python_(programming_language)"

r = requests.get(url)
content = r.content

soup = BeautifulSoup(content, 'html.parser')

id_content = soup.find(id="cite_ref-123")

parent_elem = id_content.parent
print(parent_elem)

我们可以找到在美丽的汤对象中传递的特定元素的祖父母或曾祖父母元素。

id_content = soup.find(id="cite_ref-123")

grandparent_elem = id_content.parent.parent
print(grandparent_elem)

Beautiful Soup 提供的另一种方法是 .parents帮助我们迭代所有元素的父元素

id_content = soup.find(id="cite_ref-123")

for elem in id_content.parents:
    print(elem) # to print the elements

    print(elem.name) # to print only the names of elements

注意:此程序可能需要一些时间才能完成,因此请等待程序完成。

的输出 elem.name 将是

p
div
div
div
div
body
html
[document]

类似地,我们可以分别使用和访问元素的下一个上一个兄弟姐妹 .next_sibling `` .previous_sibling

id_content = soup.find(id="cite_ref-123")

# To print the next sibling of an element
next_sibling_elem = id_content.next_sibling

print(next_sibling_elem)
id_content = soup.find(id="cite_ref-123")

# To print the previous sibling of an element
previous_sibling_elem = id_content.previous_sibling

print(previous_sibling_elem)

.next_siblings使用or遍历标签的兄弟姐妹 .previous_siblings

迭代所有下一个兄弟姐妹

next_sibling_elem = id_content.next_sibling

for next_elem in id_content.next_siblings:
    print(next_elem)

遍历所有以前的兄弟姐妹

id_content = soup.find(id="cite_ref-123")

for previous_elem in id_content.previous_siblings:
    print(previous_elem)

使用正则表达式

最后但同样重要的是,我们可以使用正则表达式在 HTML 树中搜索elementtagtext等。

p此代码将找到所有从HTML 元素开始的标签id=bodyContent

import requests
from bs4 import BeautifulSoup
import re

url = "https://en.wikipedia.org/wiki/Python_(programming_language)"

r = requests.get(url)
content = r.content

soup = BeautifulSoup(content, 'html.parser')

id_content = soup.find(id="bodyContent")

for tag in id_content.find_all(re.compile("^p")):
    print(tag.name)

此代码将匹配所有字母数字字符,即a-zA-Z0-9。它还匹配下划线,_. 但是我们没有以数字或下划线开头的元素,因此它将返回 Beautiful Soup 对象中传递的元素的所有标签和元素。

id_content = soup.find(id="bodyContent")

for tag in id_content.find_all(re.compile("\w")):
    print(tag.name)

结论

好吧,我们学习了如何抓取静态网站,尽管对于针对不同请求抛出不同数据的动态网站或具有身份验证的隐藏网站来说,它可能有所不同。这些类型的网站有更强大的抓取工具可用,如SeleniumScrapy等。

requests库允许我们访问站点的 HTML,这有助于我们使用Beautiful Soup从 HTML 中提取数据。

有许多我们还没有看到的方法和函数仍然可用,但我们讨论了一些最常用的关键函数和方法。