5.9 Python 网络爬虫基础(requests + BeautifulSoup)
·
一、 前言:什么是网络爬虫?
网络爬虫(Web Crawler) 是一种自动浏览互联网并收集数据的程序。它们被广泛应用于搜索引擎、数据挖掘、价格监测、学术研究等领域。
一个基本的爬虫工作流程包括:
- 发送HTTP请求:获取网页内容。
- 解析内容:从HTML、JSON或其他格式中提取所需数据。
- 存储数据:将提取的数据保存到文件或数据库。
- 跟踪链接(可选):根据策略继续访问其他页面。
Python在爬虫领域有着丰富的生态系统,其中requests用于发送HTTP请求,BeautifulSoup用于解析HTML,是初学者最常用的组合。
二、 爬虫的伦理与法律问题
在编写爬虫之前,必须了解相关的伦理和法律问题:
- 尊重
robots.txt:该文件定义了爬虫被允许和禁止访问的页面。使用robotparser模块可以解析此文件。 - 限制请求频率:过于频繁的请求可能被视为攻击,导致IP被封锁。应添加延迟或使用代理轮换。
- 识别自己:通过User-Agent头部标识爬虫,提供联系方式以便网站管理员联系。
- 遵守版权法:抓取的数据可能受版权保护,未经许可不得用于商业目的。
- 不抓取个人信息:避免抓取用户个人信息,除非获得明确授权。
重要:在抓取任何网站之前,请确保你有权这样做,并遵守网站的使用条款。
三、 环境设置
首先,安装必要的库:
pip install requests beautifulsoup4 lxml
requests:发送HTTP请求。beautifulsoup4:解析HTML和XML。lxml:BeautifulSoup的解析器,比标准库解析器更快更强大。
四、 使用requests获取网页内容
requests库使HTTP请求变得非常简单。以下是一个基本示例:
import requests
url = 'https://example.com'
# 发送GET请求
response = requests.get(url)
# 检查请求是否成功
if response.status_code == 200:
print('成功获取网页内容')
# 响应内容(HTML)
html_content = response.text
print(html_content[:1000]) # 打印前1000个字符
else:
print(f'请求失败,状态码: {response.status_code}')
关键点:
response.status_code:HTTP状态码,200表示成功。response.text:响应内容的字符串形式(自动解码)。response.content:原始字节内容,用于非文本内容(如图片)。
设置请求头:有些网站会检查User-Agent,如果没有设置,可能会拒绝请求。我们可以模拟浏览器:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
response = requests.get(url, headers=headers)
处理异常:网络请求可能会失败,添加异常处理使程序更健壮:
try:
response = requests.get(url, headers=headers, timeout=5)
response.raise_for_status() # 如果状态码不是200,抛出HTTPError异常
except requests.exceptions.RequestException as e:
print(f'请求出错: {e}')
五、 使用BeautifulSoup解析HTML
获得HTML内容后,我们需要从中提取数据。BeautifulSoup将复杂的HTML转换为树形结构,便于遍历和搜索。
1. 创建BeautifulSoup对象
from bs4 import BeautifulSoup
# 使用lxml解析器创建BeautifulSoup对象
soup = BeautifulSoup(html_content, 'lxml')
# prettify()方法可以格式化输出HTML
print(soup.prettify()[:1000])
2. 查找元素
BeautifulSoup提供了多种查找方法,最常用的是find()和find_all()。
- 按标签名查找:
# 查找第一个 <title> 标签
title_tag = soup.find('title')
print(title_tag.text) # 输出标签的文本内容
# 查找所有 <a> 标签
all_links = soup.find_all('a')
for link in all_links:
print(link.get('href')) # 获取href属性
- 按属性查找:
# 查找具有特定class的div
divs = soup.find_all('div', class_='some-class') # 注意class_,因为class是Python关键字
# 查找具有特定id的元素
element = soup.find(id='main-content')
# 使用字典指定多个属性
element = soup.find('a', {'href': 'https://example.com', 'class': 'external'})
- 使用CSS选择器:
select()方法支持CSS选择器语法。
# 选择所有具有class为"article"的div下的所有a标签
links = soup.select('div.article a')
for link in links:
print(link.get('href'))
3. 提取数据
- 文本内容:使用
.text或.get_text() - 属性值:使用
.get('attr_name')
# 假设我们有一个这样的HTML片段:
# <div class="product">
# <h2>产品名称</h2>
# <p class="price">¥100</p>
# <a href="/product/1">链接</a>
# </div>
product_div = soup.find('div', class_='product')
name = product_div.h2.text
price = product_div.find('p', class_='price').text
link = product_div.a.get('href')
print(f'名称: {name}, 价格: {price}, 链接: {link}')
六、 实战项目:爬取图书信息
让我们爬取一个示例网站(http://books.toscrape.com/)的图书信息。该网站专门用于练习爬虫。
目标:获取每本书的标题、价格、评分和库存状态。
import requests
from bs4 import BeautifulSoup
import csv
import time
def scrape_books():
base_url = 'http://books.toscrape.com/'
response = requests.get(base_url)
soup = BeautifulSoup(response.text, 'lxml')
# 查找所有图书 article 标签
books = soup.find_all('article', class_='product_pod')
book_data = []
for book in books:
# 提取标题
title = book.h3.a['title']
# 提取价格
price = book.find('p', class_='price_color').text
# 去掉英镑符号并转换为浮点数
price = float(price[1:])
# 提取评分
rating_class = book.find('p', class_='star-rating')['class']
# 评分在class的第二个元素,例如 'star-rating Three' -> 'Three'
rating = rating_class[1]
# 将评分转换为数字
rating_map = {'One': 1, 'Two': 2, 'Three': 3, 'Four': 4, 'Five': 5}
rating = rating_map.get(rating, 0)
# 提取库存状态
availability = book.find('p', class_='instock availability').text.strip()
book_data.append({
'title': title,
'price': price,
'rating': rating,
'availability': availability
})
return book_data
def save_to_csv(book_data, filename='books.csv'):
with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
fieldnames = ['title', 'price', 'rating', 'availability']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for book in book_data:
writer.writerow(book)
if __name__ == '__main__':
print('开始爬取图书信息...')
book_data = scrape_books()
save_to_csv(book_data)
print(f'共爬取 {len(book_data)} 本书的信息,已保存到 books.csv')
代码解析:
- 我们发送请求到首页并解析HTML。
- 每本书的信息包裹在
<article class="product_pod">中。 - 我们遍历所有这样的article元素,从中提取所需信息。
- 将提取的数据保存到CSV文件中。
七、 处理分页
上面的例子只爬取了第一页。要爬取所有页,我们需要发现分页链接并依次访问。
def scrape_all_pages():
base_url = 'http://books.toscrape.com/'
current_url = base_url
all_books = []
while current_url:
print(f'正在爬取: {current_url}')
response = requests.get(current_url)
soup = BeautifulSoup(response.text, 'lxml')
# 爬取当前页的图书
books = soup.find_all('article', class_='product_pod')
for book in books:
# ... 提取代码同上 ...
all_books.append(book_info)
# 查找下一页链接
next_button = soup.find('li', class_='next')
if next_button:
next_link = next_button.a['href']
# 构建绝对URL
if 'catalogue' in next_link:
current_url = base_url + next_link
else:
current_url = base_url + 'catalogue/' + next_link
else:
current_url = None
# 延迟一下,避免请求过于频繁
time.sleep(1)
return all_books
八、 应对反爬虫机制
网站可能会使用一些技术阻止爬虫:
- User-Agent检测:我们已经通过设置请求头应对。
- IP限制:使用代理IP轮换。
- 验证码:难以自动处理,可能需要人工干预或使用第三方验证码解决服务。
- JavaScript渲染:有些内容由JavaScript动态加载,requests无法获取。此时可使用Selenium或Playwright。
使用代理示例:
proxies = {
'http': 'http://10.10.1.10:3128',
'https': 'http://10.10.1.10:1080',
}
response = requests.get(url, proxies=proxies, timeout=5)
随机延迟:避免固定间隔的请求,使用随机延迟更模拟人类行为。
import random
time.sleep(random.uniform(1, 3))
九、 总结与最佳实践
- 遵守规则:尊重robots.txt,控制请求频率。
- 健壮性:添加异常处理、重试机制。
- 效率:考虑使用多线程或异步提高爬取速度(但需谨慎,避免给服务器造成压力)。
- 持久化:及时保存已爬取的数据,避免重复工作。
- 维护性:网站结构可能变化,定期检查并更新爬虫。
通过本章,你学会了使用requests和BeautifulSoup构建基础爬虫。接下来,你可以学习更高级的框架(如Scrapy)以及如何处理JavaScript渲染的页面。
更多推荐
所有评论(0)