你是否正在寻找从 Goodreads 爬取一些数据的方法?那么下面的指南就是为你准备的。我将为你展示一个关于如何使用 Goodreads 爬取数据的详细步骤指南。

Goodreads 是一个汇集书籍评论和推荐的平台,拥有超过 35 亿本书。几乎所有广受关注的书籍都能在 Goodreads 上找到,用户在评论方面也非常用心。

如果你正在寻找一个可以爬取优质书评、价格信息,甚至元数据的平台,那么 Goodreads 就是你的最佳选择。如果你决定爬取 Goodreads 的数据,那么你有多种选择。你可以使用现成的爬取工具,为你的特定需求定制开发一个,或者找人帮你完成。

在本指南中,我将重点介绍如何开发你自己的定制 Goodreads 爬取工具,同时也会为那些没有编程技能的人推荐一些最好的现成爬取工具。在开发自己的爬取工具的部分,我将使用 Python 语言,因为它是爬取领域最流行的选择,而且对于非程序员来说,学习起来快速且简单。

Goodreads 爬取概览

Goodreads 和其他平台一样是可以被爬取的。通过查看网络选项卡(Network Tab),你可以看到所有的网络请求及其对应的响应,这些响应会返回你想要爬取的数据。有趣的是,Goodreads 是一个相对容易爬取的目标。

这是因为其页面上大部分感兴趣的数据(如书籍详情和评论)都可以直接在页面的 HTML 文档中找到。而那些通过 Ajax 请求获取的数据,其请求也很容易复制,因此相比许多其他目标,爬取 Goodreads 的复杂性较低。

然而,你需要知道 Goodreads 并不支持机器人访问,也不允许通过非官方途径爬取其内容。它有一个反垃圾系统,用于检测和阻止自动化访问,除非是通过其官方 API,而官方 API 很可能对你来说不可用。你的唯一选择是爬取其页面上公开可用的数据。

为了能够在短时间内发送多个请求而不超过请求限制(否则会被封禁),你需要使用多个 IP 地址。然而,你的设备一次只能获得一个 IP 地址。

解决方法是使用代理。通过轮换住宅代理,你可以获得一个代理端点,该端点会为你发送的每个请求分配一个新的 IP 地址。这意味着如果你需要爬取 1000 个页面,轮换住宅代理会为每个请求分配不同的 IP,这样最终你将使用 1000 个不同的 IP 地址。


使用 Python 爬取 Goodreads 数据的要求

要使用 Python 爬取 Goodreads 数据,你需要以下工具。

  • Python:

你需要安装 Python 编程语言。所有计算机都已安装 Python。但是,已安装的是 Python 2.7,由于遗留原因,它可用。你需要安装 Python 3 — 最新版本最好。

前往Python 官方网站并下载适用于你的操作系统 (OS) 的特定安装程序。安装它并在 Windows 的命令提示符和 macOS 的终端中运行“python3”命令。

  • Python Web 爬取库:

Python 功能强大,这意味着它附带了用于常见任务(包括网页爬取)的库。但是,用于网页爬取的库并不易于使用。我建议你使用第三方替代方案。

要爬取 Goodreads,你需要一个 HTTP 库来下载网页,以及一个解析/数据提取库来从页面中提取所需的数据。Requests和 BeautifulSoup分别是HTTP 客户端和解析库的两个推荐选项。你可以根据情况在命令提示符或终端中运行以下命令来安装这两个选项。

pip 安装请求 beautifulsoup4

  • 轮换住宅代理:

如果没有代理,你的爬取工具发送的所有请求都将具有相同的 IP 地址,这将导致你的爬取工具在几次请求后被阻止。要超出请求限制并以你想要的规模爬取数据。你需要使用轮换住宅代理。这将在每次请求后更改你的 IP 地址。Smartproxy是一个很好的提供商。它将为你提供来自全球 195 多个国家/地区的超过 5200 万个 IP 的访问权限。每 GB 的费用为 6 美元。


开发 Goodreads 爬取工具的分步指南
如果你已经安装了 Python、Requests 和 BeautifulSoup,那么这一部分将带你实际操作。在本指南中,由于你只会发送少量请求,因此可以暂时不购买代理。然而,当你开始进行实际爬取时,你需要购买代理并进行配置,以避免因发送过多请求而被封禁。

第 1 步:确定要爬取的数据

如果你想从 Goodreads 爬取数据,你需要做的第一件事就是定义要爬取的数据。在本指南中,我们将爬取传记类的新书。这里没有什么特别的,我们不会逐页爬取,只会爬取第一页上的书籍,对于每本书,我们将爬取其名称、封面图片 URL、作者、评分和描述。这只是一个 MVP,只是为了向你展示如何完成它。

确定要爬取的数据

上图是图书列表的截图。在这个列表页上,收集的只是用于访问详情页的 URL。下面是第一个详情页。

详细信息页面

正如你上面所看到的,详细信息页面保存了此爬取任务中感兴趣的主要数据。

第 2 步:在开发者工具中学习图书列表页面 HTML

下一步是研究页面的结构。为此,请转到列表页面,然后右键单击图书列表的任意页面。单击“检查”选项或其等效选项(具体取决于你的浏览器)。

在开发者工具中学习图书列表页面 HTML

如上所示,图书列表的主要容器是具有两个类(bigBoxContent 和 containerWithHeaderContent)的 div。但是,在同一页面上,大约有 10 个 div 共享这些相同的类。幸运的是,保存感兴趣数据的 div 是页面上的第一个,因此我们可以使用 find 方法轻松找到它 — 无需使用 find_all。

每行书籍都包含在一个带有类 (coverRow) 的 div 元素中。单个书籍列表也包含在带有类 (leftAlignedImage bookBox) 的 div 元素中。书籍的 div 元素中包含一个锚元素 (a href),其中包含每本书的 URL。

通过上述课堂研究,我们可以爬取所有书籍 URL,为下一步做好准备。

第 3 步:爬取图书 URL

这是我们第一次启动 IDE。我使用 Pycharm,但你可以使用任何工具,包括 Visual Studio Code 和 Notepad。确保导入所有必要的库,包括请求和 BeautifulSoup。

import requests

from bs4 import BeautifulSoup

HEADERS = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36',}

def scrape_book_URLs(genre_URL):

response = requests.get(url=genre_URL, headers=HEADERS)

if response.status_code != 200:

raise Exception(' An occured while accessing genre page')

books_URLs = []

soup = BeautifulSoup(response.text, 'html.parser')

books_container = soup.find('div', attrs={'class': 'bigBoxContent containerWithHeaderContent'})

cover_rows = books_container.find_all('div', attrs={'class': 'coverRow'})

for row in cover_rows:

books = row.find_all('div', attrs={'class': 'leftAlignedImage bookBox'})

for book in books:

url = book.find('a')['href']

full_book_url = f'https://goodreads.com{url}'

books_URLs.append(full_book_url)

return books_URLs

如上所示,爬取书籍 URL 的代码封装在名为 scrape_book_urls 的函数中。该函数接受来自 Goodreads 的书籍类型的 URL,并返回第一页的书籍 URL。

你会看到我定义了一个带有用户代理字符串的标头。用户代理字符串是 Chrome 浏览器的。这是为了让脚本看起来像是 Chrome 浏览器。

在函数中,我使用请求下载页面 HTML 文档。如果状态代码不是 200,则表示下载不成功,因此,我会引发异常并终止程序。如果返回 200,我将进入下一步,即解析和提取页面数据。

我使用 BeautifulSoup 进行解析和数据提取。book_container 变量保存所有书籍的主要容器 — 它是一个带有类(bigBoxContent 和 containerWithHeaderContent)的 div 元素。

我找到其中所有带有类 (coverRow) 的 div 元素,然后循环查找包含在带有类 classes (leftAlignedImage bookBox) 的 div 元素中的所有书籍,并从中获取 URL。这些 URL 是相对 URL,因此我使用 f 字符串格式将其转换为绝对 URL。现在将以下内容添加到页面底部。

if __name__ == "__main__":

genre_URL = 'https://www.goodreads.com/genres/biography'

book_urls = scrape_book_URLs(genre_URL)

for url in book_urls:

print(url)

如果你使用上面新添加的部分运行脚本,你将看到控制台上打印了书籍 URL 列表。下面是我得到的结果。

书籍 URL 列表

在新添加的部分中,我定义了传记类型的 URL,并将其作为参数添加到 scrape_book_URLs 函数中,该函数被分配给 book_urls 变量。循环部分只是将 URL 列表打印到屏幕上。删除 for 循环并转到下一步。

第 4 步:学习书籍详细信息 HTML 页面

在图书详情页上,我们将爬取图书封面 URL、图书名称、作者、评分、评分数、评论数和描述。这些详细信息均包含在页面上的不同元素中。

  • 图书封面图片网址

对于书籍封面图片 URL,它是具有类 (BookCover__image) 的 div 元素中的第一个或唯一一个图像 (IMG 元素)。实际 URL 位于 src 属性中的 IMG 元素中。

  • 书籍作者姓名

书籍作者的姓名位于带有类 (ContributorLink__name) 的 span 元素中。它以文本形式提供,因此在元素上应用 .text 将显示作者的姓名

  • 等级

要获取该书的评分,你需要找到具有类 (RatingStatistics__rating) 的 div 元素。实际评分值可作为 div 的内容。

  • 评分数和评论数

对于评分计数,你可以在 span 元素中通过属性 data-testid (ratingsCount) 找到它。评论计数也可以在 span 元素中通过属性 data-testid (reviewsCount) 找到。

  • 书名

书名很容易获取,就是页面上的 h1 元素,为了保险起见,我们还可以添加 data-testid (bookTitle),书名就是 h1 元素的内容。

  • 书籍描述

书籍描述包含粗体部分和常规文本部分。我们将爬取所有内容。它包含在类为 (DetailsLayoutRightParagraph__widthConstrained) 的 div 元素中。在其中,你可以在类为 (Formatted) 的 span 元素和粗体 (<b>) 元素中找到它。

第 5 步:爬取单本书的详细信息

找到包含所需数据点的元素后,我们现在可以继续创建一个函数来爬取所有所需数据。该函数 (scrape_book_detail) 将访问书籍 URL 并返回详细信息。

def scrape_book_detail(book_url):

response = requests.get(url=book_url, headers=HEADERS)

if response.status_code != 200:

raise Exception(' An occured while accessing genre page')

book_detail = {}

soup = BeautifulSoup(response.text, 'html.parser')

# extract book cover url

cover_url = soup.find('div', attrs={'class': 'BookCover__image'}).find('img')['src']

# extract book title

book_title = soup.find('h1', attrs={'data-testid': 'bookTitle'}).text

# extract author name

author_name = soup.find('span', attrs={'class': 'ContributorLink__name'}).text

# extract rating

rating_value = soup.find('div', attrs={'class': 'RatingStatistics__rating'}).text

# extract rating count

rating_count = soup.find('span', attrs={'data-testid': 'ratingsCount'}).text.replace('\xa0ratings', '')

# extract review count

review_count = soup.find('span', attrs={'data-testid': 'reviewsCount'}).text.replace('\xa0reviews', '')

# extract book description

description = soup.find(

'div', attrs={'class': 'DetailsLayoutRightParagraph__widthConstrained'}).find(

'span', attrs={'class': 'Formatted'}).text

# add extracted data to dictionary

book_detail['cover_url'] = cover_url

book_detail['title'] = book_title

book_detail['author'] = author_name

book_detail['rating'] = rating_value

book_detail['rating_count'] = rating_count

book_detail['review_count'] = review_count

book_detail['description'] = description

return book_detail

上述函数将爬取并返回一个字典,其中包含 Goodreads 上书籍的详细信息(你提供其 URL 作为参数)。如你所见,它使用选择器来找到并识别需要爬取的数据点。

完成所有爬取工作后,它会将数据添加到字典中并将其作为响应返回。现在让我们调整 if __name__ == “__main__”:,以便从列表爬取的每个书籍 URL 都作为参数提供给 scrape_book_detail 函数。这样,​​我们将爬取所有详细信息并将其打印到控制台。

if __name__ == "__main__":

genre_URL = 'https://www.goodreads.com/genres/biography'

book_urls = scrape_book_URLs(genre_URL)

for url in book_urls:

book_detail = scrape_book_detail(url)

print(book_detail)

以下是我们创建的完整代码。运行它,你将看到每本书的详细信息。

import requests

from bs4 import BeautifulSoup

HEADERS = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36',}

def scrape_book_URLs(genre_URL):

response = requests.get(url=genre_URL, headers=HEADERS)

if response.status_code != 200:

raise Exception(' An occured while accessing genre page')

books_URLs = []

soup = BeautifulSoup(response.text, 'html.parser')

books_container = soup.find('div', attrs={'class': 'bigBoxContent containerWithHeaderContent'})

cover_rows = books_container.find_all('div', attrs={'class': 'coverRow'})

for row in cover_rows:

books = row.find_all('div', attrs={'class': 'leftAlignedImage bookBox'})

for book in books:

url = book.find('a')['href']

full_book_url = f'https://goodreads.com{url}'

books_URLs.append(full_book_url)

return books_URLs

def scrape_book_detail(book_url):

response = requests.get(url=book_url, headers=HEADERS)

if response.status_code != 200:

raise Exception(' An occured while accessing genre page')

book_detail = {}

soup = BeautifulSoup(response.text, 'html.parser')

# extract book cover url

cover_url = soup.find('div', attrs={'class': 'BookCover__image'}).find('img')['src']

# extract book title

book_title = soup.find('h1', attrs={'data-testid': 'bookTitle'}).text

# extract author name

author_name = soup.find('span', attrs={'class': 'ContributorLink__name'}).text

# extract rating

rating_value = soup.find('div', attrs={'class': 'RatingStatistics__rating'}).text

# extract rating count

rating_count = soup.find('span', attrs={'data-testid': 'ratingsCount'}).text.replace('\xa0ratings', '')

# extract review count

review_count = soup.find('span', attrs={'data-testid': 'reviewsCount'}).text.replace('\xa0reviews', '')

# extract book description

description = soup.find(

'div', attrs={'class': 'DetailsLayoutRightParagraph__widthConstrained'}).find(

'span', attrs={'class': 'Formatted'}).text

# add extracted data to dictionary

book_detail['cover_url'] = cover_url

book_detail['title'] = book_title

book_detail['author'] = author_name

book_detail['rating'] = rating_value

book_detail['rating_count'] = rating_count

book_detail['review_count'] = review_count

book_detail['description'] = description

return book_detail

if __name__ == "__main__":

genre_URL = 'https://www.goodreads.com/genres/biography'

book_urls = scrape_book_URLs(genre_URL)

for url in book_urls:

book_detail = scrape_book_detail(url)

print(book_detail)

需要改进的地方

上面的代码确实可以运行,但它远未达到完美。它除了在返回状态码不是 200 时处理了一些异常外,并没有应对其他情况的异常处理。而且,即使状态码不是 200,它也没有提供具体的错误详情。为了让爬取工具更加健壮,你需要处理各种可能预见到的异常。

此外,代码中并未实现代理。如果你打算爬取大量页面,建议从 Smartproxy 等服务商处购买高质量的轮换住宅代理,并将其集成到代码中,以避免因发送过多请求而被封禁。


市场上最好的现成 Goodreads 爬取工具

上面的部分是为想要亲自编写代码的开发人员准备的。如果你不是程序员,或者即使是程序员但不想重复造轮子,那么这一部分适合你。在这里,我将推荐一些最佳的 Goodreads 爬取工具,总共 5 个,其中 3 个适合非程序员,另外 2 个适合程序员使用。

Octoparse

Octoparse 是非程序员最好的数据爬取工具之一。它被视为一款可视化工具,为你提供点击式界面来识别感兴趣的数据,同时具有自动识别页面上重要数据点的智能功能。

说到从 Goodreads 爬取图书数据,可以说 Octoparse 让这一切变得简单,因为它提供了一个Goodreads 模板。有了它,你不需要对爬取工具进行太多训练。该模板可帮助你爬取图书标题、作者、图片 URL、出版年份、评级、评级计数和版本等。因为它是为非程序员设计的,所以每个人都可以使用它。作为新用户,你可以获得 14 天免费试用,不受限制地使用其所有功能。

ScrapeStorm

另一款面向非程序员的 Goodreads 爬取工具是 ScrapeStorm 可视化爬取工具。这款工具由前 Google 爬取工具团队开发,能够了解网络爬取方面的现状。它由人工智能驱动,能够智能识别要爬取的网页上的关键信息。这使得其点击界面的使用仅限于结构不够完善的网页。

你可以使用它来爬取 Goodreads 上的图书列表页面并访问单个图书的详细信息。它支持分页、爬取图片和使用正则表达式,因此你只能爬取描述中包含特定关键字的图书详细信息。它为导出格式提供了最广泛的支持,包括 CSV、Excel、TXT、HTML、MySQL 和 Google Sheets 等。

WebScraper.io 扩展

WebScraper.io 是一种基于云的付费爬取解决方案。但是,对于轻量级爬取,它提供了一个免费的浏览器扩展,专门针对 Chrome。你可以使用它来高效地爬取 Goodreads 数据。此扩展是为现代网络构建的,可让你爬取 Ajaxified 页面。

有趣的是,大多数重要的数据点甚至没有 ajax 化,这使得一开始就有点小题大做。你可以使用点击界面指向重要的数据点,它会记录它们并在不同的页面上识别类似的数据点。它让你可以选择 CSV 或 TXT 的数据导出格式。

Apify平台

Apify 平台是一个网页爬取和自动化平台,提供大量自动化工具(称为“演员”)。几乎没有哪个热门网站找不到演员,Goodreads 也不例外。你可以通过此链接访问 Goodreads 演员。

与 Octoparse、ScrapeStorm 和 WebScraper.io 不同,这款应用面向程序员,特别是 nodeJS 和 Python 程序员。你可以使用它来爬取关键字、书架、书单、类型、书籍详情和书评等。你需要安装他们的 SDK。它是付费的,但新用户可以试用一段时间。

ScraperAPI

列表中的最后一个是 ScraperAPI,这是一种爬取 API,开发者可以使用它从网页中爬取数据而不会被封禁。通过它,你可以爬取 Goodreads 上的任何重要数据点,包括书评、书籍详情以及书籍列表等。如果使用 ScraperAPI,你无需安装任何工具,因为它是一个基于 Web 的 API。

由于它是语言无关的,你可以用任何你喜欢的编程语言来使用它。ScraperAPI 会为你处理 IP 轮换、无头浏览(用于渲染 JavaScript)、绕过验证码以及其他所有形式的拦截。它的定价按请求收费,你只需为成功的请求付费。


结    论

从 Goodreads 爬取书籍数据和书评相对简单,因为其页面结构良好,且反垃圾系统对爬取工具的限制不算严格。对于希望进行小规模爬取的 Python 开发者,可以使用文中提供的代码。然而,如果你需要更健壮的工具,可以使用 Apify 提供的爬取工具或 ScraperAPI。非程序员则可以选择 Octoparse、ScrapeStorm 和 WebScraper.io 插件中的任意一个。

这篇文章有用吗?

点击星号为它评分!

平均评分 / 5. 投票数:

到目前为止还没有投票!成为第一位评论此文章。

No more articles