三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Python爬虫实战入门:18个案例从环境搭建到反爬策略

Python爬虫实战入门:18个案例从环境搭建到反爬策略

很多朋友在学习Python时,对爬虫技术充满好奇,但面对零散的教程和复杂的反爬机制,常常感到无从下手。本文旨在提供一个系统、完整的Python爬虫实战入门指南,汇集了18个由浅入深的实战案例,每个案例都配有可直接运行的源码。无论你是编程小白,还是有一定基础想巩固技能的开发者,都能通过本文掌握从环境搭建、基础请求到数据解析、存储乃至应对常见反爬策略的全流程。学完后,你将能够独立完成多种类型网站的爬取任务。

1. Python爬虫核心概念与环境准备

1.1 什么是网络爬虫?

网络爬虫(Web Crawler),是一种按照预设规则,自动抓取互联网信息的程序或脚本。它模拟浏览器行为,向目标网站服务器发送请求,获取返回的HTML、JSON等数据,并从中提取出有价值的信息。爬虫技术广泛应用于搜索引擎、数据分析、价格监控、舆情分析等领域。

对于初学者,需要理解几个关键概念:

  • 请求(Request): 你的程序向服务器索要数据的过程。
  • 响应(Response): 服务器返回给你的数据,包含状态码(如200成功,404未找到)和内容。
  • 解析(Parsing): 从服务器返回的复杂内容(如HTML)中,提取出你需要的结构化数据(如标题、价格、链接)。

1.2 环境搭建与必备库安装

工欲善其事,必先利其器。开始爬虫实战前,需要配置好Python环境并安装核心库。

1. 安装Python访问Python官网下载并安装最新稳定版(如Python 3.8+)。安装时务必勾选“Add Python to PATH”。

2. 安装爬虫核心库打开命令行(CMD或终端),使用pip命令安装以下库,它们是后续所有案例的基石。

# 用于发送HTTP请求 pip install requests # 用于解析HTML和XML文档,提取数据 pip install beautifulsoup4 # 一个强大的解析库,支持XPath和CSS选择器 pip install lxml # 用于处理表格数据,常用于数据清洗和保存 pip install pandas # 一个异步网络框架,用于高性能爬取 pip install aiohttp

3. 选择开发工具推荐使用PyCharm、VS Code或Jupyter Notebook,它们能提供良好的代码提示和调试环境。

2. 爬虫基础:请求与解析

2.1 第一个爬虫:获取网页源代码

我们从一个最简单的例子开始,使用requests库获取一个网页的全部内容。

import requests # 目标URL url = ‘https://httpbin.org/get‘ # 发送GET请求 response = requests.get(url) # 打印HTTP状态码 print(‘状态码:‘, response.status_code) # 打印网页的文本内容(HTML/JSON等) print(‘响应内容:‘, response.text)

代码解析

  • requests.get(url): 向指定的url发送一个HTTP GET请求。
  • response.status_code: 服务器返回的状态码,200表示成功。
  • response.text: 服务器返回的响应体内容,通常是字符串格式的HTML或JSON。

运行结果:你会看到返回了一个JSON格式的数据,其中包含了你的请求头等信息。这是一个用于测试HTTP请求的网站。

2.2 解析HTML:BeautifulSoup入门

获取到HTML后,我们需要从中提取特定信息。BeautifulSoup是最常用的解析库之一。

假设我们有一个简单的HTML字符串:

<html> <head><title>测试页面</title></head> <body> <h1 class=“title”>欢迎来到爬虫世界</h1> <p id=“content”>这是一个段落。</p> <ul> <li>列表项1</li> <li>列表项2</li> </ul> </body> </html>

使用BeautifulSoup进行解析:

from bs4 import BeautifulSoup html_doc = “”“ <html>...(上面的HTML内容)... </html> ”“” # 创建BeautifulSoup对象,指定解析器为‘lxml‘ soup = BeautifulSoup(html_doc, ‘lxml‘) # 1. 通过标签名查找(返回第一个匹配的标签) title_tag = soup.title print(‘标题标签:‘, title_tag) # <title>测试页面</title> print(‘标题文本:‘, title_tag.string) # 测试页面 # 2. 通过属性查找(如class, id) h1_tag = soup.find(‘h1‘, class_=‘title‘) # class是Python关键字,所以用class_ print(‘H1文本:‘, h1_tag.text) # 欢迎来到爬虫世界 p_tag = soup.find(‘p‘, id=‘content‘) print(‘P文本:‘, p_tag.text) # 这是一个段落。 # 3. 查找多个元素 li_tags = soup.find_all(‘li‘) for li in li_tags: print(‘列表项:‘, li.text) # 输出: # 列表项: 列表项1 # 列表项: 列表项2

3. 实战案例一:静态网页爬取(豆瓣电影Top250)

这是一个经典的静态网页爬虫案例,目标网站结构清晰,适合新手练习。

3.1 需求分析

爬取豆瓣电影Top250(https://movie.douban.com/top250)每一页的电影名称、评分、简介(引言)。

3.2 代码实现

import requests from bs4 import BeautifulSoup import time import pandas as pd def crawl_douban_top250(): 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‘ } base_url = ‘https://movie.douban.com/top250‘ movie_list = [] for start in range(0, 250, 25): # 总共10页,每页25条 url = f‘{base_url}?start={start}‘ print(f‘正在爬取:{url}‘) try: response = requests.get(url, headers=headers) response.raise_for_status() # 如果状态码不是200,抛出异常 response.encoding = ‘utf-8‘ soup = BeautifulSoup(response.text, ‘lxml‘) # 找到所有电影信息所在的div items = soup.find_all(‘div‘, class_=‘item‘) for item in items: # 电影标题(包含中文名和原名) title_div = item.find(‘div‘, class_=‘hd‘) title = title_div.find(‘span‘, class_=‘title‘).text.strip() if title_div else ‘N/A‘ # 评分 rating_span = item.find(‘span‘, class_=‘rating_num‘) rating = rating_span.text.strip() if rating_span else ‘N/A‘ # 简介/引言 quote_span = item.find(‘span‘, class_=‘inq‘) quote = quote_span.text.strip() if quote_span else ‘N/A‘ movie_list.append({ ‘标题‘: title, ‘评分‘: rating, ‘引言‘: quote }) except requests.RequestException as e: print(f‘请求出错:{e}‘) break # 礼貌爬取,避免请求过快 time.sleep(2) return movie_list if __name__ == ‘__main__‘: movies = crawl_douban_top250() print(f‘共爬取到 {len(movies)} 部电影信息。‘) # 使用pandas保存为CSV文件 df = pd.DataFrame(movies) df.to_csv(‘douban_top250.csv‘, index=False, encoding=‘utf_8_sig‘) # 编码确保中文正常 print(‘数据已保存至 douban_top250.csv‘) # 打印前5条看看 print(df.head())

3.3 关键点解析

  1. 请求头(Headers): 添加User-Agent模拟浏览器访问,是绕过基础反爬的第一步。
  2. 分页处理: 观察URL规律,?start=参数控制起始条目,实现循环翻页。
  3. 异常处理: 使用try-except捕获网络请求异常,增强程序健壮性。
  4. 数据存储: 使用pandas将列表数据轻松保存为CSV文件,便于后续分析。
  5. 延迟(time.sleep): 在循环中增加延时,是对目标网站友好的做法,避免IP被封。

4. 实战案例二:动态数据爬取(Ajax请求分析)

许多现代网站(如电商、社交媒体)使用Ajax技术动态加载数据,直接爬取HTML得不到内容。这时需要分析网络请求。

4.1 案例:爬取某新闻网站的热榜标题

假设一个新闻网站的热榜数据是通过Ajax接口/api/news/hotlist以JSON格式返回的。

步骤

  1. 打开浏览器开发者工具(F12),切换到Network(网络)选项卡。
  2. 刷新页面或点击加载更多,观察出现的网络请求。
  3. 找到一个返回JSON数据的请求(Type可能是xhrfetch),其Response正是我们需要的数据。
  4. 复制这个请求的URL和必要的Headers(如可能需要的AuthorizationReferer)。

4.2 代码实现

import requests import json def crawl_ajax_news(): # 通过分析找到的Ajax接口URL ajax_url = ‘https://example.com/api/news/hotlist‘ # 此处为示例URL,需替换 headers = { ‘User-Agent‘: ‘Mozilla/5.0...‘, ‘Referer‘: ‘https://example.com/‘, # 有时需要添加来源页 ‘X-Requested-With‘: ‘XMLHttpRequest‘ # 声明是Ajax请求 } # 如果有分页或查询参数 params = { ‘page‘: 1, ‘size‘: 20 } try: response = requests.get(ajax_url, headers=headers, params=params) data = response.json() # 直接将响应内容解析为JSON字典/列表 # 假设返回的JSON结构为 {“code“:0, “data“: {“list“: [...]}} news_list = data.get(‘data‘, {}).get(‘list‘, []) for news in news_list: title = news.get(‘title‘) source = news.get(‘source‘) print(f‘标题:{title} | 来源:{source}‘) except requests.RequestException as e: print(f‘请求失败:{e}‘) except json.JSONDecodeError as e: print(f‘JSON解析失败:{e}‘) print(‘原始响应:‘, response.text[:200]) # 打印前200字符辅助调试 if __name__ == ‘__main__‘: crawl_ajax_news()

4.3 核心技巧

  • 使用.json()方法: 当接口返回JSON时,直接使用response.json()解析成Python数据结构。
  • 分析请求参数: 仔细查看HeadersQuery Parameters,缺失关键参数可能导致请求失败。
  • 处理复杂认证: 有些接口需要CookieTokenSign签名,这些需要从首次页面请求中获取或模拟生成。

5. 实战案例三:处理登录与会话(Session)

爬取需要登录才能访问的页面(如个人中心)时,必须维持登录状态。requests.Session()可以帮我们自动管理Cookies。

5.1 模拟登录流程

import requests from bs4 import BeautifulSoup def login_with_session(): login_url = ‘https://example.com/login‘ target_url = ‘https://example.com/dashboard‘ # 创建一个会话对象 session = requests.Session() # 1. 首先,可能需要获取登录页面的token或验证码(如果需要) # 这里以获取一个简单的CSRF token为例 login_page_resp = session.get(login_url) soup = BeautifulSoup(login_page_resp.text, ‘lxml‘) # 假设token在name=‘csrf_token‘的input标签的value里 csrf_token = soup.find(‘input‘, {‘name‘: ‘csrf_token‘}).get(‘value‘, ‘‘) # 2. 构造登录数据 login_data = { ‘username‘: ‘your_username‘, # 替换为你的用户名 ‘password‘: ‘your_password‘, # 替换为你的密码 ‘csrf_token‘: csrf_token, ‘remember‘: ‘on‘ } # 3. 发送登录请求 login_response = session.post(login_url, data=login_data) # 检查登录是否成功(根据实际情况判断,如跳转、返回特定文本) if ‘登录成功‘ in login_response.text or login_response.url == target_url: print(‘登录成功!‘) else: print(‘登录可能失败。‘) return # 4. 使用同一个session访问需要登录的页面,会自动携带登录后的cookies dashboard_response = session.get(target_url) # 解析dashboard页面内容... # soup = BeautifulSoup(dashboard_response.text, ‘lxml‘) # ... 你的解析代码 print(‘已成功访问受保护页面。‘) if __name__ == ‘__main__‘: login_with_session()

6. 实战案例四:存储到数据库(SQLite)

将爬取的数据存入数据库便于管理和复杂查询。SQLite是一个轻量级数据库,无需安装服务器。

6.1 创建数据库与表

import sqlite3 import pandas as pd from datetime import datetime def init_database(): # 连接到数据库(如果不存在则创建) conn = sqlite3.connect(‘spider_data.db‘) cursor = conn.cursor() # 创建电影信息表 create_table_sql = “”“ CREATE TABLE IF NOT EXISTS movies ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, rating REAL, quote TEXT, crawl_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ”“” cursor.execute(create_table_sql) conn.commit() print(‘数据库表创建成功!‘) conn.close() def save_to_database(movie_list): conn = sqlite3.connect(‘spider_data.db‘) cursor = conn.cursor() for movie in movie_list: insert_sql = “”“ INSERT INTO movies (title, rating, quote) VALUES (?, ?, ?) ”“” # 安全地插入数据,防止SQL注入 cursor.execute(insert_sql, (movie[‘标题‘], movie[‘评分‘], movie[‘引言‘])) conn.commit() print(f‘成功插入 {len(movie_list)} 条数据。‘) conn.close() # 假设我们有一个从豆瓣爬取的movie_list # init_database() # save_to_database(movie_list) # 从数据库读取数据到pandas DataFrame def read_from_database(): conn = sqlite3.connect(‘spider_data.db‘) # 直接使用pandas的read_sql查询非常方便 df = pd.read_sql_query(‘SELECT * FROM movies‘, conn) conn.close() return df

7. 常见反爬策略与应对措施

7.1 User-Agent检测

现象: 请求被拒绝,返回403或简单提示。解决: 在请求头中设置常见的浏览器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)

7.2 IP频率限制

现象: 短时间内请求过多,IP被暂时或永久封禁。解决

  1. 降低请求频率: 在循环中使用time.sleep(random.uniform(1, 3))增加随机延时。
  2. 使用代理IP池: 通过轮换不同IP来发送请求。
proxies = { ‘http‘: ‘http://your_proxy_ip:port‘, ‘https‘: ‘https://your_proxy_ip:port‘, } # 使用免费代理需谨慎,稳定性和安全性无保障 response = requests.get(url, proxies=proxies, timeout=5)

7.3 验证码

现象: 登录或频繁访问后需要输入验证码。解决

  1. 识别简单图形验证码: 使用pytesseract(OCR库)或django-simple-captcha等,但成功率有限。
  2. 使用打码平台: 付费调用第三方API进行识别。
  3. 最佳实践: 优化爬虫策略,避免触发验证码。

7.4 数据动态加载(JavaScript渲染)

现象: 在浏览器中能看到数据,但爬取的HTML中没有。解决

  1. 分析Ajax接口: 如案例二所述,找到数据接口。
  2. 使用Selenium或Playwright: 自动化浏览器,直接获取渲染后的页面。
from selenium import webdriver from selenium.webdriver.common.by import By driver = webdriver.Chrome() # 需要下载对应浏览器的WebDriver driver.get(url) # 等待页面加载完成 driver.implicitly_wait(10) # 获取渲染后的页面源码 html = driver.page_source # 然后用BeautifulSoup解析html driver.quit()

注意:Selenium速度慢,资源消耗大,仅作为最后手段。

8. 18个实战案例概览与源码指引

以下是本文涵盖的18个实战案例方向,每个都提供了核心解决思路。完整源码因篇幅限制无法全部贴出,但已按主题整理成可运行的脚本。

基础入门系列

  1. 获取网页标题与链接: 练习requestsBeautifulSoup基础。
  2. 爬取静态表格数据: 如爬取全国城市天气信息。
  3. 下载图片到本地: 爬取壁纸网站图片并保存。
  4. 爬取分页列表信息: 如爬取博客文章列表(标题、发布时间、阅读量)。
  5. 解析JSON格式API: 爬取公开的天气API或汇率API数据。

中级应用系列: 6.模拟登录并爬取个人信息: 使用Session维持登录态。 7.爬取Ajax动态加载的数据: 如某电商网站滚动加载的商品列表。 8.使用Selenium爬取JavaScript渲染页面: 爬取需要JS交互才能显示的内容。 9.爬取数据并存入CSV/Excel: 使用pandas进行数据清洗和保存。 10.爬取数据并存入SQLite/MySQL数据库: 建立持久化存储。 11.定时爬虫任务: 使用scheduleAPScheduler库定时执行爬虫。 12.爬取带有验证码的网站(简单处理): 介绍绕过思路和打码平台集成。

综合实战与进阶系列: 13.爬虫框架Scrapy入门项目: 创建第一个Scrapy爬虫,体验框架的高效。 14.爬取图片并生成PDF: 综合应用爬虫、图片处理和PDF生成。 15.爬取股票数据并简单可视化: 使用mplfinance绘制K线图。 16.爬取二手房信息并进行数据分析: 数据清洗、聚合与可视化。 17.构建简单的代理IP池: 从免费网站抓取并验证代理IP。 18.分布式爬虫概念与Celery初步应用: 了解如何利用消息队列分发爬虫任务。

9. 工程最佳实践与注意事项

9.1 代码组织与可维护性

  • 模块化: 将请求函数、解析函数、存储函数分离到不同模块或类中。
  • 配置文件: 将数据库连接信息、请求头、URL等配置项写入config.pysettings.ini
  • 日志记录: 使用logging模块记录程序运行状态、错误信息,便于排查。
import logging logging.basicConfig(level=logging.INFO, format=‘%(asctime)s - %(levelname)s: %(message)s‘)

9.2 健壮性与错误处理

  • 网络异常: 对所有网络请求使用try-except包裹,捕获requests.exceptions下的各种异常(如超时、连接错误)。
  • 数据缺失: 在解析时使用.get()方法或判断标签是否存在,避免因个别数据缺失导致程序崩溃。
  • 设置超时requests.get(url, timeout=10),防止程序长时间卡住。

9.3 遵守Robots协议与法律法规

  • 尊重Robots.txt: 在爬取前,访问网站域名/robots.txt,查看该网站是否允许爬虫爬取目标路径。
  • 控制爬取速度: 避免对目标网站服务器造成过大压力。
  • 注意数据版权与隐私: 爬取的数据仅用于个人学习与研究,不得用于商业用途或侵犯他人隐私。切勿爬取敏感个人信息。
  • 明确禁止则不为: 对于明确声明禁止爬取的网站,或需要付费获取的数据接口,应主动规避。

9.4 性能优化方向

  • 并发请求: 对于大量独立URL,可以使用concurrent.futures线程池或aiohttp异步库提高效率。
  • 缓存机制: 对不变或更新慢的页面,可以将响应内容缓存到本地或数据库,避免重复请求。
  • 增量爬取: 只爬取新增或更新的内容,而不是每次都全量爬取。

学习爬虫是一个从模仿到理解,再到创新的过程。建议从本文的简单案例入手,运行每一段代码,观察结果,并尝试修改代码去爬取你感兴趣的网站。遇到问题时,善用搜索引擎和开发者工具进行调试。记住,耐心和细心是爬虫工程师最重要的品质。

← 返回列表