在这篇博客中,我们将学习如何使用 Python 编写一个网络爬虫,从植物数据库网站中获取植物信息。我们将使用 requests
、BeautifulSoup
和 pandas
库来实现这个功能。文章将包括以下内容:
-
目录
1. 爬虫的基本概念
网络爬虫是一种自动访问互联网并获取信息的程序。简单来说,它就像一个虚拟的“蜘蛛”,在互联网的“网”上爬行,从一个链接到另一个链接,获取它所需的数据。在这个例子中,我们将使用 Python 编写一个网络爬虫,从植物数据库网站中获取植物信息。
2. 使用 requests
库获取网页内容
首先,我们需要使用 Python 的 requests
库来获取网页内容。requests
是一个简单易用的 HTTP 库,可以帮助我们发送 HTTP 请求并获取响应。首先,我们需要安装 requests
库:
pip install requests
安装完成后,我们可以使用以下代码获取网页内容:
import requests
url = 'https://www.example.com/plant_database'
response = requests.get(url)
if response.status_code == 200:
html_content = response.text
else:
print(f'Error {response.status_code}: Could not fetch the webpage.')
3. 使用 BeautifulSoup
解析 HTML
接下来,我们需要解析获取到的 HTML 内容。为此,我们将使用 BeautifulSoup
库。首先,我们需要安装 beautifulsoup4
和 lxml
:
pip install beautifulsoup4 lxml
安装完成后,我们可以使用以下代码解析 HTML:
from bs4 import BeautifulSoup
soup = BeautifulSoup(html_content, 'lxml')
4. 从植物数据库网站中提取植物信息
现在我们已经获取并解析了网页内容,接下来我们需要从中提取植物信息。这需要分析目标网站的 HTML 结构,并找到其中包含植物信息的元素。以下是一个示例代码,用于提取植物名称、科名、分布地区和图片链接:
def extract_plants(soup):
plant_list = []
for plant in soup.find_all('div', class_='plant'):
common_name = plant.find('h2', class_='common-name').text.strip()
scientific_name = plant.find('h2', class_='scientific-name').text.strip()
distribution = plant.find('div', class_='distribution').text.strip()
image_link = plant.find('img')['src']
plant_data = {
'common_name': common_name,
'scientific_name': scientific_name,
'distribution': distribution,
'image_link': image_link
}
plant_list.append(plant_data)
return plant_list
plants = extract_plants(soup)
5. 将获取的数据存储到 CSV 文件中
获取到植物信息后,我们可以将它们存储到 CSV 文件中。以下是一个示例代码,用于将数据写入 CSV 文件:
import csv
def write_plants_to_csv(plants, filename):
with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
fieldnames = ['common_name', 'scientific_name', 'distribution', 'image_link']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for plant in plants:
writer.writerow(plant)
write_plants_to_csv(plants, 'plant_database.csv')
6. 爬虫的优化和改进
尽管我们已经成功地从植物数据库网站获取了植物信息并将其存储到 CSV 文件中,但仍然有一些优化和改进的空间。以下是一些建议:
-
错误处理:在实际应用中,网络爬虫可能会遇到各种错误,例如网络错误、解析错误等。为了提高爬虫的健壮性,我们需要在代码中添加适当的错误处理机制。
-
分页爬取:在这个例子中,我们只从一个页面获取了植物信息。然而,实际上植物数据库可能包含多个页面。因此,我们需要修改爬虫以支持分页爬取。
-
限制爬取速度:为了遵守网站的爬虫政策并避免给服务器带来过多负担,我们应该限制爬虫的爬取速度。这可以通过在请求之间添加延迟来实现。
-
使用代理和 User-Agent:为了更好地遵守网站的爬虫政策以及避免被封 IP,我们可以使用代理和随机 User-Agent。
-
数据清洗和验证:在将数据存储到 CSV 文件之前,我们需要对数据进行清洗和验证,以确保数据的准确性和完整性。
以下是一个完整的示例代码,包含了上述改进:
import time
import random
import requests
from bs4 import BeautifulSoup
import csv
HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
}
def get_webpage(url):
try:
response = requests.get(url, headers=HEADERS)
if response.status_code == 200:
return response.text
else:
print(f'Error {response.status_code}: Could not fetch the webpage.')
return None
except requests.RequestException as e:
print(f'Error: {e}')
return None
def parse_webpage(html_content):
return BeautifulSoup(html_content, 'lxml')
def extract_plants(soup):
plant_list = []
for plant in soup.find_all('div', class_='plant'):
common_name = plant.find('h2', class_='common-name').text.strip()
scientific_name = plant.find('h2', class_='scientific-name').text.strip()
distribution = plant.find('div', class_='distribution').text.strip()
image_link = plant.find('img')['src']
plant_data = {
'common_name': common_name,
'scientific_name': scientific_name,
'distribution': distribution,
'image_link': image_link
}
plant_list.append(plant_data)
return plant_list
def write_plants_to_csv(plants, filename):
with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
fieldnames = ['common_name', 'scientific_name', 'distribution', 'image_link']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for plant in plants:
writer.writerow(plant)
def main():
base_url = 'https://www.example.com/plant_database?page='
page_number = 1
all_plants = []
while True:
url = base_url + str(page_number)
html_content = get_webpage(url)
if html_content is None:
break
soup = parse_webpage(html_content)
plants = extract_plants(soup)
if not plants:
break
all_plants.extend(plants)
print(f'Fetched {len(plants)} plants from page {page_number}.')
page_number += 1
time.sleep(random.uniform(1, 3))
write_plants_to_csv(all_plants, 'plant_database.csv')
print(f'Finished! A total of {len(all_plants)} plants were fetched.')
if __name__ == '__main__':
main()