如何将 cURL 与 Python 结合使用进行网页抓取
本教程将向您展示使用 cURL 和 Python 来收集数据的基础知识。
cURL 是一种流行的命令行工具,用于网页抓取和测试 API。将 cURL 与 Python 结合使用有很多好处。例如,它比传统 Python 的 HTTP 客户端(如 Requests)快得多。
本教程将教您如何使用 cURL 进行网页抓取及其优势。您还可以找到一个分步示例,了解如何通过安装 PycURL 模块使用 cURL 和 Python 抓取数据。
什么是cURL?
cURL 是客户端 URL 的缩写,是一种在终端中运行的开源命令行工具,用于通过网络传输数据。它应用广泛,具有简单的文本界面,几乎适用于所有系统。
通过将 cURL 与 Python 结合使用,您可以控制爬虫程序并操作 cookie 或用户代理等请求头。您还可以将 cURL 与代理一起使用,并且该工具允许您通过简单地输入 cURL 命令来测试不同的 API 端点,而无需调用 API。
在 Web 抓取中使用 cURL 和 Python 的好处
与 Python 内置的 HTTP 请求库相比,cURL 具有多种优势。
与 Python 的 Requests 等传统 HTTP 客户端相比,cURL 非常快,特别是当您需要通过多个连接发送许多请求时。
cURL 的另一大优势是该模块支持最常见的互联网协议,如 HTTP(S)(POST、GET、PUT)、FTP(S)、IMAP、MQTT、SMB 和 POP3。此外,它还适用于大多数操作系统。
与 Requests 相比,cURL 具有更多功能:例如,更多身份验证选项、使用多个 TLS 后端的能力等。
需要提醒的是,cURL 的学习难度很高,甚至连其创建者本人都向高级开发人员推荐它。但不要因此而感到害怕,一旦您熟悉了 cURL,一切就会变得更容易。
在 Python 中使用 cURL:分步指南
在这个实际例子中,我们选择从 books.toscrape.com 抓取书籍信息。这是一个专为此类目的设计的网络爬虫沙箱。该网站每页显示 20 本书,总共有 1,000 本书。想象一下,如果全部手动抓取——那将耗费大量时间。
本教程将向您展示如何使用 cURL(特别是 PycURL 模块)收集标题、价格、评分和可用性。此外,我们还将向您展示如何使用 Python 的解析器 Beautiful Soup 将信息写入 CSV 文件。
硬件需求
- Python 3: 我在计算机上安装最新版本的Python。参考官方网站。
- 美丽汤: 在操作系统的终端中使用 Pip 添加 BeautifulSoup:
pip install beautifulsoup4. - 网址: 添加
pip install pycurl。如果你想在 Windows 上使用 pyCURL,你必须从源代码构建它;请参阅 官方网站. - libcurl:如果您是 Linux/Mac 用户,请在系统上安装 libcurl 以使 pyCURL 正常工作。
如何使用 Python 执行 cURL 请求
第一步:我们需要做的第一件事是导入 Requests、Beautiful Soup 和 BytesIO。其中,BytesIO 有助于管理文件相关的输入和输出。
from bs4 import BeautifulSoup
import pycurl
from io import BytesIO
步骤二:传入你想抓取的网址。这里,我们将使用 books.toscrape.com。
url = "http://books.toscrape.com/"
步骤 3.创建一个pycurl实例。然后,设置请求选项。
buffer = BytesIO()
curl = pycurl.Curl()
curl.setopt(curl.URL, url)
curl.setopt(curl.WRITEDATA, buffer)
第四步:提出请求。
curl.perform()
关闭连接
curl.close()
response = buffer.getvalue()
print (response)
如何解析下载的 HTML
步骤一:现在,让我们用 Beautiful Soup 提取数据。首先,你需要为抓取的页面创建一个 Beautiful Soup 对象。
soup = BeautifulSoup(response , "html.parser")
步骤 2.接下来,你需要找到我们要抓取的数据点(书名、价格、评分和库存情况)所在的位置。右键单击页面,查看页面内容。数据位于名为product_pod 的类下:
步骤 3.书籍将出现在词典列表中。
books = []
1)第一页有 20 本书。你需要为每个product_pod的相关元素创建一个循环:
for element in soup.find_all(class_='product_pod'):
2) 然后,提取页面标题。您可以在 `<h3>`标签下找到它。使用 `<h3>` 标签可以去除 URL 等不必要的数据。您可以通过指定一个字符串来实现这一点。
book_title = element.h3.string
如果您想了解更多详细信息,可以获取该书的 URL。
book_url = url + element.h3.a['href']
3)现在,让我们提取price_color下的书籍价格,并去掉英镑符号。
book_price = element.find(class_='price_color').string.replace('£','')
#getting the rating
#finding element class but it has two: star-rating and 'num' e.g. 'One' so we're only getting the second one
4) 评分位于`<p>`标签内。但是,评分信息包含在名称本身中,所以这里有点棘手。名称包含两个单词,而你只需要一个。你可以通过将类提取为列表来解决这个问题。
book_rating = element.p['class'][1]
5) 最后一个数据点是库存状态(类别为instock)。我们不使用find 方法,而是使用 CSS 选择器。这是一种更直接的定位元素的方法。我们将获取文本并去除不必要的元素,例如空格。
book_stock = element.select_one(".instock").get_text().strip()
#same as:
#book_stock = element.find(class_="instock").get_text().strip()
第四步:打印出来仔细核对。
print(book_title)
print(book_url)
print(book_price)
print(book_rating)
print(book_stock)
如何将输出导出为 CSV
步骤 1.现在,让我们把这本书的详细信息添加到它的列表中。
books.append({
'title': book_title,
'price': book_price,
'rating': book_rating,
'stock': book_stock,
'url': book_url
}
)
print (books)
步骤 2.然后,写入 csv 文件。
with open("books_output.csv", "a") as f:
步骤 3.如果您选择“a”(不带标题行),则可以打开一个现有文件进行追加写入。或者,如果文件已存在,您可以使用“w”覆盖该文件。
for book in books:
f.write(f"{book['title']},{book['price']},{book['rating']},{book['stock']},{book['url']}\n")
输出:
完整脚本如下:
from bs4 import BeautifulSoup
import pycurl
from io import BytesIO
url = "http://books.toscrape.com/"
buffer = BytesIO()
curl = pycurl.Curl()
curl.setopt(curl.URL, url)
curl.setopt(curl.WRITEDATA, buffer)
curl.perform()
curl.close()
response = buffer.getvalue()
print (response)
soup = BeautifulSoup(response , "html.parser")
#books will be a list of dicts
books = []
for element in soup.find_all(class_='product_pod'):
book_title = element.h3.string
#getting the url for the book if we ever want to get more details
book_url = url + element.h3.a['href']
#getting the price and ridding ourselves of the pound sign
book_price = element.find(class_='price_color').string.replace('£','')
#getting the rating
#finding element class but it has two: star-rating and 'num' e.g. 'One' so we're only getting the second one
book_rating = element.p['class'][1]
#finding availability
#can also use css selector instead of find
book_stock = element.select_one(".instock").get_text().strip()
#same as:
#book_stock = element.find(class_="instock").get_text().strip()
#printing out to double check
print(book_title)
print(book_url)
print(book_price)
print(book_rating)
print(book_stock)
#lets append it to the books list now
books.append({
'title': book_title,
'price': book_price,
'rating': book_rating,
'stock': book_stock,
'url': book_url
}
)
print (books)
#write it to a csv file
with open("books_output.csv", "a") as f:
#not writing in a header row in this case
#if using "a" then you can open a file if it exists and append to it
#can also use "w", then it would overwrite a file if it exists
for book in books:
f.write(f"{book['title']},{book['price']},{book['rating']},{book['stock']},{book['url']}\n")