区块链API推荐,快速开发去中心化应用
Python网络爬虫教程(2024年分步指南)
网络爬虫是从网站自动提取数据的过程,多年来,Python 一直是数据提取的首选语言。它拥有庞大的开发人员社区和广泛的网络爬虫工具,可帮助爬虫从任何网站中提取几乎任何数据。
在这里,我们将探索一些可用于 Python 中网络爬虫的最佳库和框架,并提供在不同的网络爬虫场景中使用它们的代码示例。
准备Python编程环境进行网络爬虫
在开始使用Python进行网络爬虫之前,确保开发环境已就绪至关重要,为此,您需要安装 Python,选择集成开发环境 (IDE),并掌握安装用于从Web高效提取数据所需Python库的基础知识
安装 Python
- 下载 Python:访问 Python 官方网站并下载适用于您的操作系统的最新版本。
- 安装 Python:运行安装程序并按照所有提示操作,直到 Python 正确安装在您的计算机上。
适用于 Python 的 IDE
安装 Python 后,您需要一个编写 Python 代码的地方。基本上,您需要一个 IDE。
集成开发环境 (IDE) 提供了编写、测试和调试代码的工具。一些流行的 Python 开发 IDE 是:
- PyCharm:提供专门用于 Python 开发的强大环境。可能是 Python 专用开发人员的理想 IDE。
- Visual Studio Code (VS Code):一种轻量级、多功能的 IDE,通过扩展支持 Python。但是,VS Code默认并不配备运行Python的功能。要启用此功能,您需要遵循 VS Code 文档中描述的一些额外步骤。
- Jupyter Notebook:此笔记本非常适合使用 Python 进行数据分析和探索性工作。它只需要最少的设置,并允许您直接在网页上运行代码。
归根结底,IDE的选择取决于个人偏好。以上所有选项都配备了运行 Python 代码的能力,并且可以很好地用于我们的网络爬虫目的。因此,请选择适合您偏好的 IDE。
如何安装 Python 库
从本质上讲,Python 库是预打包函数和方法的集合,它们允许你在不需要从头开始编写所有内容的情况下执行许多操作。库是软件开发不可或缺的一部分。最常用的安装Python库的方式是使用pip,Python的包管理系统。
使用pip安装库非常简单:
- 打开命令行或终端。
- 使用 pip install 命令,后跟库名称。例如,要安装
requests
库,您可以键入pip install requests
。
很简单,对吧?现在您已经掌握了必要的基础知识,让我们来了解使Python成为网络爬虫如此强大和流行的语言的Python库。
Python网络爬虫教程
要在 Python 中开始网页抓取,您需要两个关键工具:HTTP客户端(例如HTTPX)用于请求网页,以及HTML解析器(如BeautifulSoup)来提取并理解数据。
在这一部分,我们将逐步介绍爬虫过程,并解释每一步涉及的技术。通过这个过程,你将学会如何使用BeautifulSoup与HTTPX从Hacker News首页的所有文章中提取标题、排名和URL。
1. 使用 Python 爬取您的目标网站
第一步是向目标页面发送请求并检索其 HTML 内容。使用 HTTPX 只需几行代码即可完成此操作:
⚙️ 安装 HTTPX
pip install httpx
运行下面的代码。
import httpx
response = httpx.get('https://news.ycombinator.com')
html = response.text
print(html[:200]) # print first 200 characters of html
这段代码片段通过发送GET请求获取Hacker News首页的HTML内容,并将前200个字符打印出来,以验证是否成功获取了页面的HTML。这是通过使用httpx.get发送请求并将HTML内容存储在一个名为html的变量中来实现的。然而,原始的HTML对人们阅读并不友好。为了以结构化的形式提取有用的数据,需要使用BeautifulSoup来解析这个HTML。
2. 从页面中提取数据
BeautifulSoup 是一个 Python 库,可帮助您从 HTML 和 XML 文件中提取数据。它对用户友好,非常适合中小型项目,因为它设置快速并且可以有效地解析内容。如前所述,BeautifulSoup 通常与 HTTP 请求库配对。就像 HTTPX 一样。现在,让我们将所有内容结合起来,以结构化格式从 Hacker News 首页上的所有文章中抓取数据。
BeautifulSoup + HTTPX 代码从 Hacker News 首页的所有文章中提取标题内容、排名和 URL:
import httpx
from bs4 import BeautifulSoup
# Function to get HTML content from a URL
def get_html_content(url: str, timeout: int = 10) -> str:
response = httpx.get(url, timeout=timeout)
return str(response.text)
# Function to parse a single article
def parse_article(article) -> dict:
url = article.find(class_='titleline').find('a').get('href')
title = article.find(class_='titleline').get_text()
rank = article.find(class_='rank').get_text().replace('.', '')
return {'url': url, 'title': title, 'rank': rank}
# Function to parse all articles in the HTML content
def parse_html_content(html: str) -> list:
soup = BeautifulSoup(html, features='html.parser')
articles = soup.find_all(class_='athing')
return [parse_article(article) for article in articles]
# Main function to get and parse HTML content
def main() -> None:
html_content = get_html_content('https://news.ycombinator.com')
data = parse_html_content(html_content)
print(data)
if __name__ == '__main__':
main()
# Expected Output:
'''
[
{
"url":"https://ian.sh/tsa",
"title":"Bypassing airport security via SQL injection (ian.sh)",
"rank":"1"
},
{
"url":"https://www.elastic.co/blog/elasticsearch-is-open-source-again",
"title":"Elasticsearch is open source, again (elastic.co)",
"rank":"2"
},
...
{
"url":"https://languagelog.ldc.upenn.edu/nll/?p=73",
"title":"Two Dots Too Many (2008) (upenn.edu)",
"rank":"29"
},
{
"url":"https://collidingscopes.github.io/ascii/",
"title":"Show HN: turn videos into ASCII art (open source, js+canvas) (collidingscopes.github.io)",
"rank":"30"
}
]
'''
上面的代码包含多个协同工作的函数,用于抓取和解析Hacker News的首页。首先,它使用httpx获取HTML内容,然后针对特定的CSS选择器,使用BeautifulSoup提取每篇文章的标题、排名和URL。最后,所有这些逻辑被整合到一个主函数中,该函数将文章的详细信息收集到一个列表中,然后打印出来。
3. 抓取多个页面
从 Hacker News 首页的 30 篇文章中抓取数据后,接下来是时候扩展你的抓取器,以从所有文章中提取数据了。这涉及处理 “分页”,这是 Web 抓取中的一个常见挑战。为了解决这个问题,您需要浏览该网站以了解其分页的工作原理,然后相应地调整您的代码。
下面是 Hacker News 的屏幕截图,突出显示了实现分页所需的关键元素。你还会找到更新后的代码,从所有页面抓取文章。
import httpx
from bs4 import BeautifulSoup
import time
# Function to get HTML content from a URL
def get_html_content(url: str, timeout: int = 10) -> str:
response = httpx.get(url, timeout=timeout)
return str(response.text)
# Function to parse a single article
def parse_article(article) -> dict:
url = article.find(class_='titleline').find('a').get('href')
title = article.find(class_='titleline').get_text()
rank = article.find(class_='rank').get_text(strip=True).replace('.', '')
return {'url': url, 'title': title, 'rank': rank}
# Function to parse all articles in the HTML content
def parse_html_content(html: str) -> list:
soup = BeautifulSoup(html, 'html.parser')
articles = soup.find_all(class_='athing')
return [parse_article(article) for article in articles]
# Main function to get and parse HTML content from all pages
def main() -> None:
base_url = 'https://news.ycombinator.com'
page_number = 1
all_data = []
while True:
url = f'{base_url}/?p={page_number}'
html_content = get_html_content(url)
data = parse_html_content(html_content)
all_data.extend(data)
soup = BeautifulSoup(html_content, 'html.parser')
more_link = soup.select_one('.morelink')
if not more_link:
break
page_number += 1
time.sleep(2) # Adding a 2-second delay between requests
print(all_data)
if __name__ == '__main__':
main()
此代码扩展了用于抓取第一页的初始代码段,并对函数进行了一些调整。现在,它通过循环遍历多个页面来处理这些页面,更新URL中的页码,并使用与之前相同的解析函数。
4. 使用 Python 抓取动态网站
虽然 BeautifulSoup 和 HTTPX 非常适合抓取静态网站,但它们无法处理通过 JavaScript 加载内容的动态网站。
为此,我们使用 Playwright,这是一个浏览器自动化库,可捕获完全呈现的页面,包括动态内容。Playwright 之所以有效,是因为它控制着一个真实的 Web 浏览器,但它比 BeautifulSoup 更耗费资源且速度更慢。因此,请在确实必要的情况下才使用Playwright,对于更直接的任务,请坚持使用我们之前的解决方案。
⚙️ 安装
# Install the Playwright package
pip install playwright
# Install the necessary browser binaries (Chromium, Firefox, and WebKit)
playwright install
目标是在 MintMobile 的第一页上抓取每个产品的名称、价格和 URL。以下是使用 Playwright 执行此操作的方法:
import asyncio
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
browser = await p.firefox.launch(headless=False)
page = await browser.new_page()
await page.goto("https://phones.mintmobile.com/")
# Create a list to hold the scraped data
data_list = []
# Wait for the products to load
await page.wait_for_selector('ul.products > li')
products = await page.query_selector_all('ul.products > li')
for product in products:
url_element = await product.query_selector('a')
name_element = await product.query_selector('h2')
price_element = await product.query_selector('span.price > span.amount')
if url_element and name_element and price_element:
data = {
"url": await url_element.get_attribute('href'),
"name": await name_element.inner_text(),
"price": await price_element.inner_text()
}
data_list.append(data)
await browser.close()
print(data_list)
asyncio.run(main())
此代码使用 Playwright 启动 Firefox 浏览器,导航到 MintMobile 手机页面,然后等待产品加载。产品可见后,它会选择页面上的所有产品元素并循环访问它们以提取每个产品的 URL、名称和价格。这些细节随后被存储在一个名为data_list的列表中。
最后,收集数据后,关闭浏览器,并打印抓取的产品详细信息列表。
这很好用,但仅仅打印数据并不太实用。接下来,让我们看看如何将此数据保存到 CSV 文件。
5. 将抓取的数据导出为 CSV
在Python中将抓取的数据保存到CSV文件相当简单。只需导入Python内置的csv模块,并使用下面的代码:
import csv
# Save the data to a CSV file
with open('products.csv', mode='w', newline='') as file:
writer = csv.DictWriter(file, fieldnames=["url", "name", "price"])
writer.writeheader()
writer.writerows(data_list)
这段代码将抓取的数据保存到名为products.csv的CSV文件中。它创建该文件,并使用csv.DictWriter来写入数据,其中“url”、“name”和“price”作为列标题。writeheader()函数添加这些标题,而writerows(data_list)则将每个产品的详细信息写入文件。
以下是完整代码的最终版本:
import asyncio
import csv
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
browser = await p.firefox.launch(headless=False)
page = await browser.new_page()
await page.goto("https://phones.mintmobile.com/")
# Create a list to hold the scraped data
data_list = []
# Wait for the products to load
await page.wait_for_selector('ul.products > li')
products = await page.query_selector_all('ul.products > li')
for product in products:
url_element = await product.query_selector('a')
name_element = await product.query_selector('h2')
price_element = await product.query_selector('span.price > span.amount')
if url_element and name_element and price_element:
data = {
"url": await url_element.get_attribute('href'),
"name": await name_element.inner_text(),
"price": await price_element.inner_text()
}
data_list.append(data)
await browser.close()
# Save the data to a CSV file
with open('products.csv', mode='w', newline='') as file:
writer = csv.DictWriter(file, fieldnames=["url", "name", "price"])
writer.writeheader()
writer.writerows(data_list)
asyncio.run(main())
如何在云端部署进行Python爬虫
接下来,我们将学习如何使用Apify将我们的爬虫部署到云端,以便我们可以配置它们按计划运行,并使用该平台的其他许多功能。
Apify 使用称为 Actors 的无服务器云程序,这些程序在 Apify 平台上运行并执行计算任务。
为了演示这一点,我们将使用 Apify SDK、BeautifulSoup 和 HTTPX 创建一个开发模板,并调整生成的样板代码以运行我们的 BeautifulSoup Hacker News 抓取工具。那么,让我们开始吧。
安装 Apify CLI
通过 homebrew
在 macOS(或 Linux)上,您可以通过 Homebrew 包管理器安装 Apify CLI。
brew install apify/tap/apify-cli
通过 NPM
通过运行以下命令来安装或升级 Apify CLI:
npm -g install apify-cli
创建新 Actor
在计算机上安装 Apify CLI 后,只需在终端中运行以下命令即可:
apify create bs4-actor
然后,继续选择Python → BeautifulSoup和HTTPX→安装模板
这条命令将创建一个名为bs4-actor的新文件夹,安装所有必要的依赖项,并生成一段样板代码。我们可以使用这段样板代码,结合BeautifulSoup、Requests和Python的Apify SDK来快速启动我们的开发。
最后,进入新创建的文件夹,并使用您喜欢的代码编辑器打开它。在这个例子中,我使用的是VS Code。
cd bs4-actor
code .
在本地测试 Actor
模板已经创建了一个功能齐全的爬虫。如果您想在修改代码之前尝试一下,可以使用命令apify run
来运行它。抓取的结果将被存储在storage/datasets
目录下。
太好了!现在我们已经熟悉了模板,接下来让我们进入src/main.py
并修改其中的代码,以便抓取HackerNews的内容。
只需进行一些调整,最终代码就会变成这样:
from bs4 import BeautifulSoup
from httpx import AsyncClient
from apify import Actor
async def main() -> None:
async with Actor:
# Read the Actor input
actor_input = await Actor.get_input() or {}
start_urls = actor_input.get('start_urls')
if not start_urls:
Actor.log.info('No start URLs specified in actor input, exiting...')
await Actor.exit()
# Enqueue the starting URLs in the default request queue
rq = await Actor.open_request_queue()
for start_url in start_urls:
url = start_url.get('url')
Actor.log.info(f'Enqueuing {url} ...')
await rq.add_request({'url': url, 'userData': {'depth': 0}})
# Process the requests in the queue one by one
while request := await rq.fetch_next_request():
url = request['url']
Actor.log.info(f'Scraping {url} ...')
try:
# Fetch the URL using `httpx`
async with AsyncClient() as client:
response = await client.get(url, follow_redirects=True)
soup = BeautifulSoup(response.content, 'html.parser')
articles = soup.find_all(class_='athing')
for article in articles:
data = {
'URL': article.find(class_='titleline').find('a').get('href'),
'title': article.find(class_='titleline').getText(),
'rank': article.find(class_='rank').getText().replace('.', ''),
}
# Push the extracted data into the default dataset
await Actor.push_data(data)
except:
Actor.log.exception(f'Cannot extract data from {url}.')
finally:
# Mark the request as handled so it's not processed again
await rq.mark_request_as_handled(request)
最后,在您的终端中输入命令apify run
,您将会看到存储中填充了从HackerNews抓取的数据。
在我们进行下一步之前,请转到.actor/input_schema.json
文件,并将预填充的URL更改为https://news.ycombinator.com/news
。当我们在Apify平台上运行爬虫时,这一点非常重要。
将 Actor 部署到 Apify
既然我们已经确认Actor按预期工作,现在就可以将其部署到Apify平台了。要跟随以下步骤,您需要注册一个免费的Apify账户。
一旦您拥有了一个Apify账户,请在终端中运行命令apify login
。系统将提示您提供Apify API Token。您可以在Apify控制台中的“设置”→“集成”下找到该Token。
最后一步是运行apify push
命令。这将启动Actor的构建过程,几秒钟后,您应该能够在Apify控制台下的“Actors”→“我的Actors”中看到您新创建的Actor。apify push
太棒了!您的抓取工具已准备好在 Apify 平台上运行!只需点击“开始”按钮,运行结束后,您就可以在“存储”选项卡中预览并以多种格式下载您的数据。
Python网络爬虫的最佳实践
- 使用用户代理标头:这种简单而有效的策略使您的请求看起来好像它们来自真实的浏览器,从而帮助您避免被封锁。
- 实施错误处理:事情并不总是按计划进行。确保您的代码可以处理网络错误和网站结构中的更改。
- 使用代理:代理是网络抓取的强大工具,通过不同的 IP 地址轮换您的请求,从而帮助您避免IP被封禁。
- 明智地使用浏览器自动化:像Playwright或Selenium这样的工具功能强大但也很繁重。仅在需要抓取简单工具(如BeautifulSoup)无法处理的动态内容时才使用它们。
- 避免过于频繁地抓取: 注意网站的资源,不要太频繁地抓取。调整您的抓取间隔以匹配网站处理请求的能力。
常见问题解答
- Python适合网络爬虫吗?
- 哪个Python网络爬虫库最好?
- 网络爬虫合法吗?
- 会因为爬虫而被封禁吗?
结论
总体而言,Python凭借其简洁性和强大的库,是进行网络爬虫的绝佳选择。像BeautifulSoup这样的工具可以轻松抓取静态网站,而Playwright则非常适合处理动态内容。使用Python进行网络爬虫不会出错。