用Beautiful Soup建立一个Python网络搜刮器

387 阅读10分钟

如果你在技术领域花了一些时间,你可能会遇到 "网络搜刮 "和 "网络搜刮器 "这些术语。但你知道它们是什么,它们如何工作,或者如何为自己建立一个?

如果你对这些问题的答案是否定的,请继续阅读,因为我们将在这篇文章中涵盖有关网络刮削的一切。你还将有机会使用Python和Beautiful Soup库建立一个。

什么是网络刮削?

网络刮削是指通过超文本传输协议(HTTP),使用被认为是网络刮削器的脚本或程序,以自动化的方式从网站上提取和收获数据。

网络刮削器是一种软件应用程序,能够访问互联网上的资源并提取所需的信息。通常情况下,网络刮削器可以对收集到的数据进行结构化和组织,并将其存储在本地供将来使用。

一些标准的网络刮削工具包括。

你可能想知道为什么有人会对使用网络搜刮器感兴趣。以下是一些常见的使用情况。

  • 为营销目的产生线索
  • 监测和比较多家商店的产品价格
  • 数据分析和学术研究
  • 收集数据用于训练机器学习模型
  • 分析社交媒体资料
  • 信息收集和网络安全
  • 获取金融数据(股票、加密货币、外汇汇率等)。

网络搜刮面临的挑战

当你需要数据时,网络刮削听起来会是一个首选的解决方案,但由于多种原因,它并不总是容易设置的。让我们来看看其中的一些。

1.每个网站都有不同的结构

人们使用不同的团队、工具、设计和部分来建立网站,使一个给定的网站的一切都与另一个不同。这意味着,如果你为一个网站创建一个网络搜刮器,你必须建立一个单独的版本才能与另一个网站完全兼容--除非它们共享非常相似的内容,或者你的网络搜刮器使用了聪明的启发式方法。

2.网站经常改变其设计和结构

网络搜刮器的耐用性是一个重要的问题。你可以有一个今天工作得很好的网络刮刀,但它似乎会突然坏掉,因为你要提取数据的网站更新了它的设计和结构。因此,你还得经常对你的搜刮器逻辑进行修改,以保持它的运行。

3.一些网站实施机器人预防措施

多年来,人们开始滥用网络搜刮器的力量来进行恶意活动。网络开发者对这一举动进行报复,实施了防止其数据被刮取的措施。这些措施中的一些包括

  • 在提交表格时,添加CAPTCHA
  • 使用Cloudflare对访问者进行授权
  • 验证访问者的用户代理
  • 拒绝代理请求
  • 节制网络资源
  • IP地址安全名单/屏蔽名单

4.速率限制技术可以干扰搜刮活动

简而言之,速率限制是一种技术,它通过为一个系统的操作设置使用上限来控制其处理的流量大小。在这种情况下,该操作允许访问者访问网站上托管的内容。

当你试图从多个网站页面刮取大量数据时,速率限制就会变得很麻烦。

5.动态网站更难搜刮

动态网站使用脚本在网站上生成其内容。通常情况下,它从外部来源获取数据并将其预填充到页面中。

如果你的网络搜刮器向网页发出GET请求并搜刮返回的数据,它将不会像预期的那样发挥作用,因为它没有在网站上运行脚本。这里的解决方案是使用像Selenium这样的工具,它可以启动一个浏览器实例并执行所需的脚本。

基本概念

在我们进入深入的例子之前,让我们确保我们已经正确设置,并在实践中理解关于网络刮擦的一些基本概念。

要遵循和理解本教程,你需要以下条件。

  • 具有HTML和Python的工作知识
  • 在你的机器上安装Python 3.6或更高版本
  • 一个Python开发环境(例如,文本编辑器、IDE)。
  • Beautiful Soup ≥4.0

首先,安装 [Beautiful Soup](https://pypi.org/project/beautifulsoup4/target=),这是一个Python库,为你提供了从HTML和XML文档中提取数据的简单方法。

在你的终端,输入以下内容。

pip install beautifulsoup4

使用Beautiful Soup解析一个HTML文档

让我们来探讨一个Python代码块,它使用Beautiful Soup来解析和浏览一个HTML文档。

from bs4 import BeautifulSoup

# define a HTML document
html = "<!DOCTYPE html><html><head><title>This is the title of a website</title></head><body><h1 id='heading-1'>This is the main heading</h1><h2 id='heading-2'>This is a subheading</h2><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p><ul><li class='list-item'>First</li><li class='list-item'>Second</li><li class='list-item'>Third</li></ul></body></html>"

# parse the HTML content with Beautiful Soup
soup = BeautifulSoup(html, "html.parser")

# print the HTML in a beautiful form
print(soup.prettify())

我们将Beautiful Soup库导入一个脚本,并在上面的代码中从我们的HTML文档中创建了一个BeautifulSoup 对象。然后,我们使用prettify() 方法,以充分缩进的形式显示HTML内容。下面是输出结果。
The Beautiful Soup object we created in HTML form

通过标签名称提取HTML元素

接下来,让我们提取我们文档中的一些 HTML 标签。Beautiful Soup 提供了一些方法,允许你提取元素。

让我们看一个例子。

# getting the title element of the HTML
print(soup.title)

# getting the first h1 element in the HTML
print(soup.h1)

以及它的输出。
Extract the HTML elements by tag names

Beautiful Soup提供了一个find() 方法,允许你用特定的标准来提取元素。让我们看看如何使用它。

# getting the first h2 element in the HTML
print(soup.find("h2"))

# getting the first p element in the HTML
print(soup.find("p"))

以及输出结果的样子。
The output for extracting HTML elements with specific criteria

Beautiful Soup还提供了一个find_all() 方法,可以以列表的形式提取具有特定标签的每个元素,而不是只得到第一次出现的元素。让我们来看看它的用法。

# getting all the li elements in the HTML
print(soup.find_all("li"))

The output when you extract elements by specific tags as a list

通过他们的ID提取HTML元素

你可能想提取那些有特定ID的HTML元素。find() 方法允许你提供一个ID来过滤其搜索结果。

让我们看看如何使用它。

# getting the h1 element with the heading-1 id
print(soup.find("h1", id="heading-1"))

# getting the h2 element with the heading-2 id
print(soup.find("h2", {"id": "heading-2"}))

而下面是输出结果。
The output when extracting HTML elements by their IDs

提取有类的HTML元素

Beautiful Soup还可以让你通过向find()find_all() 方法提供适当的参数来提取具有特定类别的HTML元素,以过滤它们的搜索结果。让我们看看它的用法。

# getting the first li element with the list-item class
print(soup.find("li", {"class": "list-item"}))

# getting all the li elements with the list-item class
print(soup.find_all("li", {"class": "list-item"}))

Extracting HTML elements by their class

访问一个元素的属性和内容

你可能想检索你提取的元素的属性和内容的值。

幸运的是,Beautiful Soup提供了实现这一目的的功能。让我们看看一些例子。

# define a HTML document
html = "<a id='homepage' class='hyperlink' href='https://google.com'>Google</a>"

# parse the HTML content with Beautiful Soup
soup = BeautifulSoup(html, "html.parser")

# extract the a element in the HTML
element = soup.find("a")

# extract the element id
print("ID:", element["id"])

# extract the element class
print("class:", element["class"])

# extract the element href
print("href:", element["href"])

# extract the text contained in the element
print("text:", element.text)
print("text:", element.get_text())

Access the element's attributes and contents

让我们建立一个网络搜刮器

现在我们已经涵盖了用Python和Beautiful Soup进行网络刮削的基础知识,让我们建立一个脚本,从CoinGecko刮削并显示加密货币信息。

第1步:安装依赖项

你需要安装Python的Requests库,以扩展你的脚本的功能,非常容易地发送HTTP/1.1请求。

在你的终端,输入以下内容。

pip install requests

第2步:获取CoinGecko的HTML数据

现在,我们将检索CoinGecko的HTML内容,用Beautiful Soup解析和提取所需的信息。创建一个名为scraper.py 的文件,并在其中保存以下代码。

import requests


def fetch_coingecko_html():
    # make a request to the target website
    r = requests.get("https://www.coingecko.com")
    if r.status_code == 200:
        # if the request is successful return the HTML content
        return r.text
    else:
        # throw an exception if an error occurred
        raise Exception("an error occurred while fetching coingecko html")

第三步:研究CoinGecko网站的结构

记住:我们强调每个网站都有不同的结构,所以在建立网络刮刀之前,我们需要研究CoinGecko的结构和构建方式。

在你的浏览器中打开[https://coingecko.com](https://coingecko.com) ,这样我们就可以看到我们正在搜刮的网站(下面的截图来自我的Firefox浏览器)。
The CoinGecko website in a Firefox browser

由于我们想搜刮加密货币信息,在Web Developer Toolbox ,打开Inspector 标签,查看信息表中任何加密货币元素的源代码。
Bitcoin's price according to CoinGecko

The source code of the Bitcoin element

从上面的源代码中,我们可以注意到关于我们正在检查的HTML标签的以下内容。

  • 每个加密货币元素都存储在一个tr 标签中,该标签包含在一个div 标签中,其类别为coin-table
  • 加密货币的名称存储在一个td 标签中,该标签具有coin-name 类别
  • 价格存储在一个具有td-priceprice 类的td 标签中。
  • 价格变化存储在td 标签中,并带有td-change1h,td-change24h, 和td-change7d 类。
  • 交易量和市值存储在td 标签中,并带有td-liquidity_scoretd-market_cap 类别。

第四步:用Beautiful Soup提取数据

现在我们已经研究了CoinGecko网站的结构,让我们用Beautiful Soup来提取我们需要的数据。

scraper.py 文件中添加一个新的函数。

from bs4 import BeautifulSoup

def extract_crypto_info(html):
    # parse the HTML content with Beautiful Soup
    soup = BeautifulSoup(html, "html.parser")

    # find all the cryptocurrency elements
    coin_table = soup.find("div", {"class": "coin-table"})
    crypto_elements = coin_table.find_all("tr")[1:]

    # iterate through our cryptocurrency elements
    cryptos = []
    for crypto in crypto_elements:
        # extract the information needed using our observations
        cryptos.append({
            "name": crypto.find("td", {"class": "coin-name"})["data-sort"],
            "price": crypto.find("td", {"class": "td-price"}).text.strip(),
            "change_1h": crypto.find("td", {"class": "td-change1h"}).text.strip(),
            "change_24h": crypto.find("td", {"class": "td-change24h"}).text.strip(),
            "change_7d": crypto.find("td", {"class": "td-change7d"}).text.strip(),
            "volume": crypto.find("td", {"class": "td-liquidity_score"}).text.strip(),
            "market_cap": crypto.find("td", {"class": "td-market_cap"}).text.strip()
        })

    return cryptos

在这里,我们创建了一个extract_crypto_info() 函数,从CoinGecko的HTML内容中提取所有的加密货币信息。我们使用Beautiful Soup中的find(),find_all(), 和.text 方法来浏览CoinGecko的数据并提取我们需要的东西。

第五步:显示提取的数据

让我们使用上面创建的函数来完成我们的搜刮器并在终端显示加密货币信息。在scraper.py 文件中添加以下代码。

# fetch CoinGecko's HTML content
html = fetch_coingecko_html()

# extract our data from the HTML document
cryptos = extract_crypto_info(html)

# display the scraper results
for crypto in cryptos:
    print(crypto, "\n")

一旦你运行这个,你会看到以下内容。
The display of the extracted data

你也可以决定将结果保存在本地的JSON文件中。

import json

# save the results locally in JSON
with open("coingecko.json", "w") as f:
    f.write(json.dumps(cryptos, indent=2))

The same extracted data displayed in JSON format

结语

在这篇文章中,你了解了网络搜刮和网络搜刮器,它们的用途,与网络搜刮有关的挑战,以及如何使用Beautiful Soup库。我们还探索了多个实现代码片段,并建立了一个网络刮刀,用Python和Beautiful Soup从CoinGecko检索加密货币信息。

加密货币网络搜刮器的源代码以GitHub Gist的形式提供。你可以前往官方的Beautiful Soup文档,探索它提供的更多功能,并利用从本教程中获得的知识构建令人惊叹的东西。