课程计划:Python 爬虫
前置知识
0.1 Python 基础:了解基本的 Python 语法和数据结构,如列表、字典和元组。
0.2 HTTP 协议基础:了解 HTTP 请求和响应,状态码以及常见的 HTTP 方法(GET、POST 等)。
0.3 HTML/CSS 基础:了解 HTML 和 CSS 的基本结构,以便于解析网页内容。
0.4 Python 网络库:安装和使用 requests 库进行基本的 HTTP 请求。
0.5 Python 解析库:安装和使用 BeautifulSoup 库解析 HTML 内容。
主课程
1.1 爬虫介绍:什么是爬虫,爬虫的基本原理和应用场景。
1.2 爬虫的基本架构:爬虫的基本组成部分,如 URL 管理器、下载器、解析器和数据存储。
1.3 使用 requests 库:如何使用 requests 库发送 HTTP 请求,处理响应。
1.4 使用 BeautifulSoup 库:如何使用 BeautifulSoup 库解析 HTML 内容,提取所需信息。
1.5 实战案例:实现一个简单的爬虫,抓取指定网站的内容。
1.6 处理动态网页:使用 Selenium 库处理 JavaScript 渲染的动态网页内容。
1.7 数据存储:将爬取的数据存储到 CSV、JSON 或数据库中。
1.8 遵守爬虫规范:了解 robots.txt 文件和用户代理的使用,避免爬虫被封禁。
1.9 多线程和异步爬虫:提升爬虫效率,使用多线程或异步方法实现高效爬虫。
1.10 常见问题和解决方案:处理反爬虫机制、IP 封禁等问题。
1.1 爬虫介绍
爬虫 是一种自动化程序,用于从互联网上抓取数据。爬虫的基本原理是模拟浏览器发送 HTTP 请求,获取网页内容,然后解析并提取所需的信息。爬虫的应用场景非常广泛,包括数据采集、信息检索和搜索引擎等。
关键概念:
- 爬虫的作用:自动化数据收集,节省人力,提高效率。
- 爬虫的结构:通常由 URL 管理器、下载器、解析器和数据存储四部分组成。
- 合法性和道德规范:遵守网站的 robots.txt 文件和用户代理规范,避免给服务器带来负担。
实际应用:
- 搜索引擎:如 Google 和 Bing 使用爬虫抓取和索引网页内容。
- 数据采集:如抓取电商网站的商品信息、社交媒体的用户数据等。
- 学术研究:如分析网络上的文本、图片、视频等数据。
示例:Google 搜索引擎的爬虫工作流程
- URL 提交:用户提交一个查询,Google 的爬虫会从其数据库中提取相关 URL。
- 抓取网页:爬虫访问这些 URL,下载网页内容。
- 解析内容:解析网页中的文本、链接等信息。
- 存储数据:将解析后的数据存储到 Google 的索引数据库中,供用户查询。
1.2 爬虫的基本架构
爬虫通常由四个主要部分组成:URL 管理器、下载器、解析器和数据存储。下面我们详细介绍每个部分:
1. URL 管理器
- 作用:管理需要爬取的 URL 列表,防止重复爬取。
- 实现:可以使用队列或集合数据结构。
2. 下载器
- 作用:发送 HTTP 请求,获取网页内容。
- 实现:常用 Python 的
requests
库来实现。
3. 解析器
- 作用:解析网页内容,提取需要的数据。
- 实现:常用
BeautifulSoup
库来解析 HTML。
4. 数据存储
- 作用:将提取的数据保存到本地文件或数据库中。
- 实现:可以使用 CSV、JSON、SQL 数据库等。
示例:简单的爬虫架构
import requests
from bs4 import BeautifulSoup
import csv
# URL 管理器
start_url = 'http://example.com'
urls = [start_url]
visited_urls = set()
# 数据存储
csv_file = open('data.csv', 'w', newline='')
csv_writer = csv.writer(csv_file)
csv_writer.writerow(['Title', 'URL'])
while urls:
url = urls.pop(0)
if url in visited_urls:
continue
# 下载器
response = requests.get(url)
visited_urls.add(url)
# 解析器
soup = BeautifulSoup(response.text, 'html.parser')
title = soup.find('title').text
csv_writer.writerow([title, url])
# 提取新的 URL
for link in soup.find_all('a'):
new_url = link.get('href')
if new_url and new_url.startswith('http'):
urls.append(new_url)
csv_file.close()
1.3 使用 requests
库
requests
库是一个非常强大且易于使用的 HTTP 库,用于发送 HTTP 请求。我们将介绍如何安装和使用 requests
库来发送基本的 HTTP 请求,并处理响应。
安装 requests
库
首先,你需要安装 requests
库。你可以在命令行中运行以下命令:
pip install requests
使用 requests
库发送 HTTP 请求
下面是一些基本的例子,展示如何使用 requests
库发送 GET 请求和 POST 请求。
发送 GET 请求
import requests
url = 'http://example.com'
response = requests.get(url)
# 检查响应状态码
print(response.status_code)
# 打印响应内容
print(response.text)
发送 POST 请求
import requests
url = 'http://example.com/post'
data = {'key': 'value'}
response = requests.post(url, data=data)
# 检查响应状态码
print(response.status_code)
# 打印响应内容
print(response.text)
常用的 HTTP 方法
- GET: 从服务器请求数据(常用于获取网页内容)。
- POST: 向服务器提交数据(常用于表单提交)。
- PUT: 向服务器发送数据,替换指定资源。
- DELETE: 从服务器删除指定资源。
处理响应
- 响应状态码:
response.status_code
用于检查请求是否成功(如 200 表示成功,404 表示未找到)。 - 响应内容:
response.text
用于获取响应的文本内容,response.json()
用于解析 JSON 响应。
示例:使用 requests
库抓取网页内容
import requests
url = 'http://example.com'
response = requests.get(url)
if response.status_code == 200:
print('请求成功!')
print(response.text)
else:
print('请求失败,状态码:', response.status_code)
1.4 使用 BeautifulSoup
库
BeautifulSoup
是一个非常强大的 Python 库,用于解析 HTML 和 XML 文档。它提供了简单的方法来导航、搜索和修改解析树,使得从网页中提取数据变得容易。
安装 BeautifulSoup
库
首先,你需要安装 BeautifulSoup
和 lxml
解析器。你可以在命令行中运行以下命令:
pip install beautifulsoup4 lxml
使用 BeautifulSoup
库解析 HTML 内容
下面是一些基本的例子,展示如何使用 BeautifulSoup
库解析 HTML 内容,并提取所需的信息。
创建 BeautifulSoup
对象
from bs4 import BeautifulSoup
html_doc = """
<html>
<head>
<title>示例网页</title>
</head>
<body>
<h1>这是一个标题</h1>
<p class="content">这是一些内容。</p>
<a href="http://example.com">这是一个链接</a>
</body>
</html>
"""
soup = BeautifulSoup(html_doc, 'lxml')
print(soup.prettify())
查找元素
你可以使用 find
和 find_all
方法来查找元素。
查找第一个匹配的元素
title = soup.find('title')
print(title.text)
查找所有匹配的元素
paragraphs = soup.find_all('p')
for p in paragraphs:
print(p.text)
查找具有特定属性的元素
link = soup.find('a', href='http://example.com')
print(link.text)
提取元素属性
你可以使用 attrs
属性来提取元素的属性。
link = soup.find('a')
print(link['href'])
示例:使用 BeautifulSoup
解析网页内容
import requests
from bs4 import BeautifulSoup
url = 'http://example.com'
response = requests.get(url)
soup = BeautifulSoup(response.text, 'lxml')
# 查找标题
title = soup.find('title').text
print('标题:', title)
# 查找所有段落
paragraphs = soup.find_all('p')
for p in paragraphs:
print('段落:', p.text)
# 查找链接
link = soup.find('a')['href']
print('链接:', link)
1.5 实战案例:实现一个简单的爬虫
我们将结合之前学习的 requests
和 BeautifulSoup
库,来实现一个简单的爬虫,抓取指定网站的内容。
实战目标
实现一个爬虫,抓取某网站上的所有文章标题和链接,并将它们保存到一个 CSV 文件中。
步骤
- 使用
requests
库发送 HTTP 请求,获取网页内容。 - 使用
BeautifulSoup
库解析网页内容,提取文章标题和链接。 - 将提取的数据保存到 CSV 文件中。
实战代码示例
import requests
from bs4 import BeautifulSoup
import csv
# 目标网站的 URL
url = 'http://example.com/articles'
# 发送 HTTP 请求
response = requests.get(url)
# 检查请求是否成功
if response.status_code == 200:
# 使用 BeautifulSoup 解析网页内容
soup = BeautifulSoup(response.text, 'lxml')
# 打开一个 CSV 文件,准备写入数据
with open('articles.csv', 'w', newline='') as file:
writer = csv.writer(file)
writer.writerow(['Title', 'Link'])
# 查找所有文章标题和链接
articles = soup.find_all('article')
for article in articles:
title = article.find('h2').text
link = article.find('a')['href']
writer.writerow([title, link])
print('数据已成功保存到 articles.csv 文件中。')
else:
print('请求失败,状态码:', response.status_code)
代码详解
- 发送 HTTP 请求:使用
requests.get
发送请求,获取网页内容。 - 解析网页内容:使用
BeautifulSoup
解析网页内容,将 HTML 转换为一个可以方便操作的对象。 - 查找并提取数据:查找所有文章的标题和链接,并将它们提取出来。
- 保存数据:使用 Python 的
csv
模块将数据写入 CSV 文件。
1.6 处理动态网页
有些网页的内容是通过 JavaScript 动态加载的,普通的 requests
库无法抓取到这些内容。为了解决这个问题,我们可以使用 Selenium
库,它可以模拟用户操作浏览器,加载动态内容。
安装 Selenium
库
首先,你需要安装 Selenium
库和浏览器驱动程序(例如 ChromeDriver)。你可以在命令行中运行以下命令:
pip install selenium
下载并安装 ChromeDriver:https://sites.google.com/chromium.org/driver/
使用 Selenium
抓取动态网页内容
下面是一个使用 Selenium
抓取动态网页内容的基本示例。
示例:使用 Selenium
抓取动态网页内容
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from bs4 import BeautifulSoup
# 配置 ChromeDriver 路径
chrome_driver_path = '/path/to/chromedriver'
# 初始化 Chrome 浏览器
chrome_options = Options()
chrome_options.add_argument('--headless') # 设置无头模式
service = Service(chrome_driver_path)
driver = webdriver.Chrome(service=service, options=chrome_options)
# 目标网站的 URL
url = 'http://example.com/dynamic'
# 使用 Selenium 打开网页
driver.get(url)
# 等待动态内容加载完成
try:
element_present = EC.presence_of_element_located((By.ID, 'dynamic-content'))
WebDriverWait(driver, 10).until(element_present)
finally:
# 获取网页内容
html = driver.page_source
driver.quit()
# 使用 BeautifulSoup 解析网页内容
soup = BeautifulSoup(html, 'lxml')
# 查找并提取动态加载的内容
dynamic_content = soup.find(id='dynamic-content')
print(dynamic_content.text)
代码详解
- 初始化 Chrome 浏览器:使用
Selenium
初始化一个 Chrome 浏览器实例,并配置为无头模式。 - 打开目标网站:使用
driver.get
打开目标网站。 - 等待动态内容加载:使用
WebDriverWait
等待特定元素加载完成。 - 获取网页内容:使用
driver.page_source
获取网页内容,并使用BeautifulSoup
解析。
1.7 数据存储
在爬取并解析了网页内容后,下一步是将提取的数据存储起来。我们可以选择多种存储方式,包括 CSV 文件、JSON 文件和数据库(如 SQLite)。
存储到 CSV 文件
CSV(Comma-Separated Values)是一种常用的轻量级数据存储格式,适合存储结构化数据。
示例:存储数据到 CSV 文件
import csv
# 数据列表,每个元素是一个包含标题和链接的元组
data = [
('Title 1', 'http://example.com/1'),
('Title 2', 'http://example.com/2'),
]
# 打开一个 CSV 文件,准备写入数据
with open('data.csv', 'w', newline='') as file:
writer = csv.writer(file)
writer.writerow(['Title', 'Link']) # 写入表头
writer.writerows(data) # 写入数据
存储到 JSON 文件
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,适合存储嵌套和结构化的数据。
示例:存储数据到 JSON 文件
import json
# 数据列表,每个元素是一个包含标题和链接的字典
data = [
{'title': 'Title 1', 'link': 'http://example.com/1'},
{'title': 'Title 2', 'link': 'http://example.com/2'},
]
# 将数据存储到 JSON 文件
with open('data.json', 'w') as file:
json.dump(data, file, indent=4)
存储到 SQLite 数据库
SQLite 是一个轻量级的嵌入式数据库,适合存储结构化数据。
示例:存储数据到 SQLite 数据库
import sqlite3
# 连接到 SQLite 数据库(如果数据库不存在,将会创建一个新的数据库)
conn = sqlite3.connect('data.db')
cursor = conn.cursor()
# 创建一个表
cursor.execute('''
CREATE TABLE IF NOT EXISTS articles (
id INTEGER PRIMARY KEY,
title TEXT,
link TEXT
)
''')
# 数据列表,每个元素是一个包含标题和链接的元组
data = [
('Title 1', 'http://example.com/1'),
('Title 2', 'http://example.com/2'),
]
# 插入数据到表中
cursor.executemany('INSERT INTO articles (title, link) VALUES (?, ?)', data)
# 提交事务并关闭连接
conn.commit()
conn.close()
1.8 遵守爬虫规范
在编写和运行爬虫时,我们需要遵守一些规范和道德准则,以避免对目标网站造成负担或违反法律法规。这包括遵守 robots.txt
文件的规定、使用合适的用户代理、控制爬取速度等。
robots.txt
文件
robots.txt
是一个位于网站根目录的文本文件,用于指示爬虫哪些页面可以或不可以抓取。
示例:读取 robots.txt
文件
import requests
url = 'http://example.com/robots.txt'
response = requests.get(url)
print(response.text)
用户代理
用户代理字符串(User-Agent)用于标识请求是由哪种浏览器或爬虫发送的。通过设置合适的用户代理,可以避免被网站屏蔽。
示例:设置用户代理
import requests
url = 'http://example.com'
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)
print(response.status_code)
控制爬取速度
为了避免给目标网站造成过大负担,我们需要控制爬取速度。可以使用 time.sleep
函数在每次请求之间添加延迟。
示例:控制爬取速度
import time
import requests
from bs4 import BeautifulSoup
urls = ['http://example.com/page1', 'http://example.com/page2']
for url in urls:
response = requests.get(url)
soup = BeautifulSoup(response.text, 'lxml')
print(soup.title.text)
# 在每次请求之间添加延迟
time.sleep(1) # 延迟 1 秒
总结
- 遵守
robots.txt
文件:在抓取前检查并遵守网站的robots.txt
文件规定。 - 使用合适的用户代理:设置合适的用户代理字符串,避免被屏蔽。
- 控制爬取速度:添加请求间隔,避免对目标网站造成负担。
1.9 多线程和异步爬虫
为了提升爬虫的效率,可以使用多线程或异步方法来同时处理多个请求。这有助于加快爬取速度,尤其是在处理大量网页时非常有用。
多线程爬虫
使用多线程可以并发地执行多个爬取任务,减少总的爬取时间。我们可以使用 concurrent.futures
模块来实现多线程爬虫。
示例:使用多线程实现爬虫
import concurrent.futures
import requests
from bs4 import BeautifulSoup
# 要爬取的 URL 列表
urls = [
'http://example.com/page1',
'http://example.com/page2',
'http://example.com/page3'
]
# 爬取函数
def fetch_url(url):
response = requests.get(url)
soup = BeautifulSoup(response.text, 'lxml')
return soup.title.text
# 使用 ThreadPoolExecutor 实现多线程
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
results = list(executor.map(fetch_url, urls))
for result in results:
print(result)
异步爬虫
使用异步编程可以在等待 I/O 操作时执行其他任务,从而提高效率。我们可以使用 aiohttp
和 asyncio
库来实现异步爬虫。
示例:使用异步方法实现爬虫
import aiohttp
import asyncio
from bs4 import BeautifulSoup
# 要爬取的 URL 列表
urls = [
'http://example.com/page1',
'http://example.com/page2',
'http://example.com/page3'
]
# 异步爬取函数
async def fetch_url(session, url):
async with session.get(url) as response:
text = await response.text()
soup = BeautifulSoup(text, 'lxml')
return soup.title.text
# 主异步函数
async def main():
async with aiohttp.ClientSession() as session:
tasks = [fetch_url(session, url) for url in urls]
results = await asyncio.gather(*tasks)
for result in results:
print(result)
# 运行异步爬虫
asyncio.run(main())
代码详解
- 多线程爬虫:使用
concurrent.futures.ThreadPoolExecutor
创建线程池,并使用executor.map
并发地执行爬取任务。 - 异步爬虫:使用
aiohttp
创建异步 HTTP 请求,使用asyncio
管理异步任务,使用await
等待 I/O 操作完成。
1.10 常见问题和解决方案
在编写和运行爬虫时,你可能会遇到一些常见的问题和挑战。以下是一些常见问题及其解决方案:
1. 反爬虫机制
许多网站会使用各种方法来检测并阻止爬虫。常见的反爬虫机制包括:
- IP 封禁:当同一个 IP 地址在短时间内发送过多请求时,网站可能会封禁该 IP。
- CAPTCHA:网站可能会要求用户输入 CAPTCHA 以验证是否为人类用户。
- 动态内容加载:使用 JavaScript 动态加载内容,防止简单的 HTTP 请求抓取数据。
解决方案
- 使用代理池:通过使用代理池来轮换 IP 地址,减少被封禁的风险。
- 模拟人类行为:添加延迟、随机化请求间隔、设置合适的用户代理。
- 处理 CAPTCHA:使用第三方服务或手动解决 CAPTCHA。
- 使用 Selenium:处理动态加载内容。
2. 数据解析错误
有时,网页结构复杂或不一致,可能会导致解析错误。
解决方案
- 检查网页结构:使用浏览器的开发者工具检查网页的 HTML 结构。
- 使用 try-except 块:在解析代码中使用 try-except 块来捕获和处理解析错误。
3. 数据存储问题
存储大量数据可能会导致性能问题或数据丢失。
解决方案
- 选择合适的存储方式:根据数据量和访问频率选择合适的存储方式,如 CSV、JSON 或数据库。
- 定期备份:定期备份数据,防止数据丢失。
示例:使用代理池
import requests
from itertools import cycle
# 代理列表
proxies = [
'http://proxy1.com',
'http://proxy2.com',
'http://proxy3.com'
]
proxy_pool = cycle(proxies)
url = 'http://example.com'
for i in range(10):
proxy = next(proxy_pool)
response = requests.get(url, proxies={'http': proxy, 'https': proxy})
print(response.status_code)
示例:使用 try-except 块处理解析错误
import requests
from bs4 import BeautifulSoup
url = 'http://example.com'
response = requests.get(url)
try:
soup = BeautifulSoup(response.text, 'lxml')
title = soup.find('title').text
print('标题:', title)
except AttributeError as e:
print('解析错误:', e)