Python 爬虫项目:景点介绍与旅游攻略爬取
2026/6/12 22:50:56 网站建设 项目流程

前言

在文旅行业数字化发展的当下,互联网中汇聚了海量景点介绍、游玩路线、游客评价、出行贴士等旅游攻略类数据,这类数据不仅能为旅游平台内容运营、文旅行业市场分析、出行参考提供核心支撑,也成为数据分析、人工智能训练的重要数据源。传统人工复制整理数据的方式效率低下、成本高昂,且无法实现海量数据的实时更新与批量采集,基于 Python 开发定向爬虫程序,能够自动化完成网页请求、数据解析、内容提取与本地存储工作,大幅提升数据采集效率。

本文围绕旅游类网站景点及攻略数据采集场景展开完整实战开发,从环境配置、核心库功能讲解、网页结构分析、代码编写、逻辑原理拆解、异常处理、数据持久化等维度进行全面讲解,同时结合实战过程中常见的反爬机制、编码问题、分页爬取问题给出解决方案。本次项目开发所依赖的第三方库及官方资源链接如下: Python 官方下载地址、requests 库官方文档、BeautifulSoup4 官方文档、lxml 解析库下载与文档、csv 模块官方说明、time 模块官方文档。

本项目适用于具备 Python 基础语法,想要学习通用静态网页爬虫开发、数据解析与批量采集的开发者,所编写的代码具备良好的可移植性与拓展性,可根据不同旅游网站的页面结构进行简单修改后复用,同时代码中融入工程化的异常捕获、请求延时、编码兼容等设计思路,贴合实际生产环境中的爬虫开发规范。

一、项目整体规划与需求分析

1.1 项目采集目标

本次爬虫项目选定主流旅游资讯类静态网页作为采集目标,核心采集内容分为两大模块,分别为景点基础信息旅游攻略信息,具体采集字段规划如下表所示。

表格

数据分类采集字段字段说明数据类型
景点基础信息景点名称景区、公园、地标等完整名称字符串
景点基础信息所属地区省、市、区县三级地域信息字符串
景点基础信息景点等级A 级景区、网红景点、免费景点等标签字符串
景点基础信息门票价格成人票、优待票、免票等价格信息字符串
景点基础信息开放时间日常营业、节假日调整后的开放时段字符串
景点基础信息景点简介官方介绍、历史背景、特色亮点文本字符串
旅游攻略信息游玩时长推荐游览耗时字符串
旅游攻略信息最佳游玩季节适合出行的月份、季节说明字符串
旅游攻略信息交通指南公共交通、自驾路线等出行方式字符串
旅游攻略信息游玩贴士注意事项、穿搭建议、消费提醒等字符串

1.2 功能需求拆解

结合采集目标,将爬虫程序拆解为多个独立功能模块,各模块各司其职且相互联动,保障采集流程完整运行:

  1. 网络请求模块:模拟浏览器向目标网站发送 HTTP 请求,获取网页原始 HTML 源码,处理请求超时、连接失败、状态码异常等网络问题。
  2. 页面解析模块:基于 HTML 源码定位目标数据节点,提取表格、文本、标签等结构化与非结构化数据,完成数据清洗与格式统一。
  3. 分页遍历模块:识别网站分页规则,自动循环访问不同页码页面,实现全页数据批量采集,避免单页数据遗漏。
  4. 数据存储模块:将解析完成的结构化数据写入本地 CSV 文件,实现数据持久化,保证数据可读性与后续二次处理便利性。
  5. 反爬应对模块:设置请求头伪装浏览器身份、添加随机请求延时,规避目标网站基础反爬策略,降低 IP 被封禁风险。
  6. 日志与异常模块:捕获代码运行过程中的语法异常、解析异常、IO 异常,输出异常提示信息,保障程序持续运行。

1.3 运行环境要求

本项目基于 Python 3.7 及以上版本开发,兼容 Windows、Linux、macOS 全平台操作系统,运行前需完成基础环境与第三方库的安装配置。系统需保证网络正常连通,能够正常访问目标旅游网站,同时本地磁盘具备基础存储空间用于存放采集后的 CSV 数据文件。

二、开发环境与依赖库安装详解

2.1 Python 基础环境配置

首先完成 Python 解释器的安装,进入前文提供的Python 官方下载地址,根据自身操作系统选择对应安装包,Windows 系统推荐选择 Windows Installer 版本,Linux 与 macOS 可选择对应源码包或系统适配安装包。安装过程中勾选 “Add Python to PATH” 自动配置环境变量,安装完成后打开系统命令行工具,输入python --version,若正常输出版本号则代表基础环境配置成功。

2.2 核心第三方库介绍与安装

本项目主要使用requestsbs4(BeautifulSoup4)lxml三大核心第三方库,搭配 Python 内置csvtimerandom模块完成全功能开发,下文逐一说明库的作用、安装命令与核心特性。

2.2.1 requests 库

requests是 Python 生态中主流的 HTTP 请求库,相较于 Python 内置的urllib模块,其语法简洁、功能强大,支持 GET、POST 等主流请求方式,同时简化了请求头设置、Cookie 携带、超时配置、编码处理等操作,是爬虫开发的首选网络请求库。

打开命令行,执行以下 pip 安装命令完成安装:

plaintext

pip install requests

安装完成后,可在命令行输入pip show requests查看库版本、安装路径等信息,验证安装结果。参考requests 库官方文档可学习该库的高级用法,如会话保持、代理设置、文件上传等。

2.2.2 BeautifulSoup4 与 lxml 解析库

BeautifulSoup4简称 bs4,是专门用于 HTML、XML 文档解析的库,能够将复杂的网页源码转换为树形结构,开发者可通过标签名、属性、选择器等方式快速定位页面元素、提取文本与属性值。BeautifulSoup4本身不具备解析能力,需要依赖解析器工作,lxml是目前性能最优、兼容性最强的 HTML/XML 解析器,解析速度远高于 Python 内置的 html.parser,因此本项目组合使用bs4 + lxml作为解析方案。

依次执行以下两条安装命令:

plaintext

pip install beautifulsoup4 pip install lxml

安装完成后,可查阅BeautifulSoup4 官方文档与lxml 解析库下载与文档掌握节点定位、数据提取等核心语法。

2.2.3 Python 内置模块说明

项目中还使用三个 Python 标准内置模块,无需额外安装,随 Python 解释器自带:

  1. csv 模块:用于创建、写入 CSV 格式文件,CSV 文件可直接使用 Excel、WPS 打开,适合存储结构化表格数据,详细用法参考csv 模块官方说明。
  2. time 模块:实现程序延时功能,模拟人类浏览网页的间隔时间,规避网站反爬,详情参考time 模块官方文档。
  3. random 模块:配合 time 模块生成随机延时时间,进一步提升请求行为的模拟度。

三、目标网页结构分析

爬虫开发的核心前提是精准分析目标网页的 HTML 结构,明确数据所在的标签、类名、ID、层级关系,才能编写对应的解析规则。本次选取典型静态旅游攻略网页作为分析对象,整体页面分为顶部导航区、分页控制区、景点列表区、单景点详情区四大板块。

3.1 网页请求方式分析

打开浏览器进入目标旅游页面,按下 F12 调出开发者工具,切换至 Network 网络面板,刷新页面后查看请求记录。列表中第一条请求即为当前页面的主请求,请求方式为GET 请求,请求 URL 存在明显的分页参数,格式规则为https://xxx.com/travel?page=页码,页码从 1 开始依次递增,这是实现分页爬取的核心依据。

查看请求的 Request Headers 请求头信息,网站会通过 User-Agent 字段识别客户端类型,若检测到请求来自 Python 程序默认标识,会直接拒绝访问,因此爬虫程序中必须手动配置 User-Agent 伪装成浏览器。

3.2 景点列表节点分析

页面主体区域为景点列表,每一个景点对应一组独立的 HTML 标签组合,所有景点标签统一包裹在一个父级 div 标签内。单个景点的基础信息(名称、地区、等级、门票、开放时间)全部存放在 class 属性固定的 div、span 标签中,标签层级固定、属性唯一,可通过 BeautifulSoup 的类选择器精准定位。

景点简介、游玩攻略等长文本内容,部分直接展示在列表页,部分需要进入景点详情页获取。本文分为两个采集阶段,第一阶段采集列表页可见的基础字段,第二阶段通过列表页中的详情链接,二次请求详情页获取完整攻略文本。

3.3 分页规则分析

页面底部存在分页按钮,点击不同页码观察 URL 变化,可总结出分页参数规律:URL 末尾page=后的数字代表当前页码,页面无总页码限制时,可设置循环区间实现批量爬取;若存在最大页码,可先解析页面底部页码标签获取总页数,再遍历全部页码,避免无效请求。

3.4 数据格式与干扰项分析

网页中存在大量空白字符、换行符、HTML 转义字符、广告标签、无关链接等干扰内容,原始提取的文本数据需要做清洗处理。例如文本前后多余空格、段落之间换行符、 空格转义字符等,需通过字符串替换、去除首尾空格等操作统一数据格式,保证存储后数据整洁规范。

四、完整爬虫代码实现与逐行原理解析

结合前文的环境配置、页面分析、功能规划,分模块编写完整爬虫代码,代码分为基础配置模块、请求函数模块、列表页解析模块、详情页解析模块、数据存储模块、主程序分页调度模块六大板块,每段代码后附带详细原理讲解、功能说明与注意事项。

4.1 整体代码框架与导入模块

首先导入项目所需的全部库与模块,这是所有代码运行的入口,完整代码如下:

python

运行

# 导入网络请求库 import requests # 导入网页解析库 from bs4 import BeautifulSoup # 导入CSV文件操作模块 import csv # 导入时间模块,实现请求延时 import time # 导入随机数模块,生成随机延时 import random

代码原理解析

  1. import requests:引入第三方 HTTP 请求库,后续所有网页访问、数据获取操作均依赖该库完成。
  2. from bs4 import BeautifulSoup:从 bs4 库中单独导入 BeautifulSoup 解析类,该类是解析 HTML 源码的核心入口,无需导入 bs4 全库,精简代码体积。
  3. import csv:调用 Python 内置 CSV 模块,负责创建文件、写入表头、逐行存储采集数据。
  4. import timeimport random:两个模块配合使用,在两次网页请求之间添加随机间隔时间,模拟真人浏览网页的行为,降低目标网站反爬机制的触发概率,是中小型爬虫必备的反爬基础手段。

4.2 全局参数与请求头配置

定义项目全局变量,包含基础 URL、请求头、文件存储路径、请求延时区间等内容,代码如下:

python

运行

# 目标网站基础URL,分页参数后续拼接 BASE_URL = "https://www.traveldemo.com/scenic?page=" # 配置请求头,伪装成谷歌浏览器,规避基础反爬 HEADERS = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" } # 定义数据存储的CSV文件路径 SAVE_FILE = "旅游景点与攻略数据.csv" # 随机延时区间:1-3秒 SLEEP_MIN = 1 SLEEP_MAX = 3 # 设置爬取页码范围,根据需求修改起止页码 START_PAGE = 1 END_PAGE = 10

代码原理解析

  1. BASE_URL:定义网站基础链接,将可变的页码参数拆分出来,后续循环时只需要拼接数字即可生成完整分页 URL,提升代码复用性,若更换同结构网站,仅需修改基础 URL 即可。
  2. HEADERS请求头字典:核心字段为User-Agent,该字段用于标识客户端类型。Python requests 默认的 UA 标识会被多数旅游网站拦截,因此手动填入真实浏览器的 UA 字符串,欺骗服务器将爬虫识别为正常浏览器访问。除 UA 外,部分网站还会校验 Referer、Cookie 等字段,可根据实际反爬强度补充。
  3. SAVE_FILE:指定本地存储文件名称与路径,文件后缀为.csv,后续 csv 模块会按照标准 CSV 格式写入数据。
  4. SLEEP_MINSLEEP_MAX:限定随机延时的上下区间,单位为秒,每完成一次网页请求后,程序会暂停随机时长,避免短时间内高频请求造成服务器压力,同时防止 IP 被临时封禁。
  5. START_PAGEEND_PAGE:定义本次需要爬取的起始页码和结束页码,开发者可根据数据量自由调整区间,实现指定范围页码的定向采集。

4.3 通用网页请求函数封装

将网页请求逻辑封装为独立函数,统一处理请求、超时、异常等问题,代码如下:

python

运行

def get_html(url): """ 发送GET请求,获取网页HTML源码 :param url: 目标网页链接 :return: 成功返回HTML源码,失败返回None """ try: # 发送GET请求,设置超时时间为10秒 response = requests.get(url, headers=HEADERS, timeout=10) # 设置网页编码为UTF-8,解决中文乱码问题 response.encoding = "utf-8" # 判断响应状态码,200代表请求成功 if response.status_code == 200: return response.text else: print(f"请求失败,状态码:{response.status_code},链接:{url}") return None except requests.exceptions.Timeout: print(f"链接 {url} 请求超时") return None except requests.exceptions.ConnectionError: print(f"链接 {url} 连接失败,请检查网络或链接有效性") return None except Exception as e: print(f"链接 {url} 出现未知异常:{str(e)}") return None

代码原理解析

  1. 函数get_html接收唯一参数url,即待访问的网页链接,函数设计为通用请求接口,列表页、详情页均可调用此函数获取源码,实现代码解耦。
  2. requests.get():发送 HTTP GET 请求,传入三个核心参数:url为请求地址,headers为伪装请求头,timeout=10设置请求超时时间,若 10 秒内未收到服务器响应,直接判定为请求超时,避免程序卡死。
  3. response.encoding = "utf-8":手动指定响应内容编码格式。多数中文网站使用 UTF-8 编码,若不手动设置,requests 会自动推测编码,极易出现中文乱码,这是中文网页爬虫必须处理的编码问题。
  4. 状态码判断:HTTP 协议中,状态码200 OK代表请求完全成功,服务器正常返回数据;404 代表页面不存在、403 代表权限不足被拦截、500 代表服务器内部错误,代码中对非 200 状态码进行打印提示,方便开发者定位问题。
  5. 多层异常捕获:使用try...except异常捕获结构,分类处理不同类型错误:
    • requests.exceptions.Timeout:捕获请求超时异常,多由网络延迟、服务器响应缓慢导致;
    • requests.exceptions.ConnectionError:捕获连接失败异常,常见于链接失效、网络断开、IP 被封禁;
    • 通用Exception:捕获其余所有未知异常,保证单个链接出错不会导致整个爬虫程序终止运行,提升程序健壮性。
  6. 函数返回值:请求成功返回网页 HTML 文本源码,各类异常场景统一返回None,上层调用函数可根据返回值判断是否继续解析数据。

4.4 列表页数据解析函数封装

编写列表页解析函数,提取景点名称、地区、等级、门票、开放时间、详情页链接等数据,代码如下:

python

运行

def parse_list_page(html): """ 解析景点列表页,提取基础信息与详情链接 :param html: 列表页HTML源码 :return: 景点信息列表,每条数据为字典格式 """ # 实例化BeautifulSoup对象,指定解析器为lxml soup = BeautifulSoup(html, "lxml") # 定位所有景点条目父标签 scenic_list = soup.find_all("div", class_="scenic-item") # 定义列表存储单页所有景点数据 page_data = [] # 遍历每一个景点条目 for item in scenic_list: # 定义字典存储单个景点数据 scenic_info = {} try: # 提取景点名称 name = item.find("h3", class_="scenic-name").get_text(strip=True) scenic_info["景点名称"] = name # 提取所属地区 area = item.find("span", class_="scenic-area").get_text(strip=True) scenic_info["所属地区"] = area # 提取景点等级 level = item.find("span", class_="scenic-level").get_text(strip=True) scenic_info["景点等级"] = level # 提取门票价格 price = item.find("div", class_="scenic-price").get_text(strip=True) scenic_info["门票价格"] = price # 提取开放时间 open_time = item.find("div", class_="scenic-time").get_text(strip=True) scenic_info["开放时间"] = open_time # 提取详情页链接,拼接完整URL detail_href = item.find("a", class_="detail-link")["href"] if detail_href.startswith("http"): detail_url = detail_href else: detail_url = "https://www.traveldemo.com" + detail_href scenic_info["详情链接"] = detail_url # 将单条数据加入列表 page_data.append(scenic_info) except Exception as e: print(f"解析单条景点数据失败,异常信息:{str(e)}") continue return page_data

代码原理解析

  1. BeautifulSoup(html, "lxml"):传入网页源码与解析器名称,创建解析对象soup,此时 HTML 字符串会被转换为可遍历的树形节点结构,所有标签、文本、属性都可以通过节点方法访问。
  2. soup.find_all("div", class_="scenic-item")find_all方法用于批量查找所有符合条件的标签,第一个参数为标签名,class_参数根据 CSS 类名筛选标签(使用 class_是因为 class 为 Python 保留关键字,无法直接作为参数名)。该语句会获取当前页面所有景点条目标签,返回标签列表。
  3. 循环遍历景点条目:对每一个景点标签item单独解析,使用字典scenic_info存储单个景点的所有字段,字典键名对应采集字段,方便后续写入 CSV 文件。
  4. find()方法:与find_all不同,find()只查找第一个符合条件的标签,适用于单个字段提取。.get_text(strip=True)用于提取标签内的纯文本内容,strip=True会自动去除文本首尾的空格、换行符、制表符等空白字符,完成基础数据清洗。
  5. 详情链接拼接:网页中部分链接为相对路径(不以 http 开头),无法直接访问,因此增加路径判断逻辑:绝对路径直接使用,相对路径拼接网站域名生成完整 URL,保证详情链接可正常跳转。
  6. 内部异常捕获:单条景点数据解析失败时,仅跳过当前条目并打印异常,不会终止整个循环,避免因某一个标签缺失导致整页数据采集中断。
  7. 函数返回值:返回当前页面所有景点的数据集,格式为列表嵌套字典,是结构化数据格式,便于后续统一处理。

4.5 详情页攻略数据解析函数封装

通过列表页获取的详情链接,二次请求详情页,提取景点简介、游玩时长、最佳季节、交通指南、游玩贴士等攻略信息,代码如下:

python

运行

def parse_detail_page(detail_url): """ 解析景点详情页,提取完整旅游攻略信息 :param detail_url: 景点详情页链接 :return: 攻略信息字典 """ # 调用请求函数获取详情页源码 detail_html = get_html(detail_url) # 延时等待,降低请求频率 time.sleep(random.uniform(SLEEP_MIN, SLEEP_MAX)) # 初始化攻略字典 travel_strategy = { "景点简介": "", "游玩时长": "", "最佳游玩季节": "", "交通指南": "", "游玩贴士": "" } # 若源码获取失败,直接返回空字典 if not detail_html: return travel_strategy try: soup = BeautifulSoup(detail_html, "lxml") # 提取景点简介 intro = soup.find("div", class_="scenic-intro").get_text(strip=True) travel_strategy["景点简介"] = intro # 提取游玩时长 play_time = soup.find("span", class_="play-time").get_text(strip=True) travel_strategy["游玩时长"] = play_time # 提取最佳游玩季节 best_season = soup.find("span", class_="best-season").get_text(strip=True) travel_strategy["最佳游玩季节"] = best_season # 提取交通指南 traffic = soup.find("div", class_="traffic-guide").get_text(strip=True) travel_strategy["交通指南"] = traffic # 提取游玩贴士 tips = soup.find("div", class_="travel-tips").get_text(strip=True) travel_strategy["游玩贴士"] = tips except Exception as e: print(f"解析详情页 {detail_url} 失败,异常:{str(e)}") return travel_strategy

代码原理解析

  1. 函数接收详情页链接作为参数,首先调用已封装的get_html函数获取详情页 HTML 源码,实现功能复用。
  2. time.sleep(random.uniform(SLEEP_MIN, SLEEP_MAX))random.uniform(a,b)生成 [a,b] 区间内的随机浮点数,配合time.sleep()实现随机时长延时。列表页与详情页属于连续请求,增加延时可以进一步规避反爬检测。
  3. 初始化攻略字典:提前定义所有攻略字段并赋值为空字符串,即使详情页解析失败,最终数据字段也不会缺失,保证 CSV 文件列数统一。
  4. 空值判断:若get_html返回None(请求失败),直接返回初始化后的空字典,跳过解析逻辑。
  5. 详情页节点解析:沿用 BeautifulSoup 的find方法定位详情页专属标签,提取攻略类长文本数据。长文本内容通常包含多段文字,.get_text()会自动将标签内所有文本拼接为完整字符串,满足简介、指南类文本采集需求。
  6. 异常捕获:捕获详情页解析过程中的各类错误,打印异常链接与信息,便于后续人工核查失效链接。

4.6 CSV 数据存储函数封装

封装数据写入函数,统一管理 CSV 文件的创建、表头写入、逐行数据追加,代码如下:

python

运行

def save_to_csv(all_data): """ 将采集完成的全量数据写入本地CSV文件 :param all_data: 整合后的完整数据集 """ # 定义CSV文件表头,与采集字段一一对应 csv_header = [ "景点名称", "所属地区", "景点等级", "门票价格", "开放时间", "景点简介", "游玩时长", "最佳游玩季节", "交通指南", "游玩贴士" ] # 以写入模式打开CSV文件,指定编码为utf-8-sig,解决Excel中文乱码 with open(SAVE_FILE, "w", newline="", encoding="utf-8-sig") as f: # 创建CSV写入器 writer = csv.DictWriter(f, fieldnames=csv_header) # 写入表头 writer.writeheader() # 遍历完整数据集,逐行写入数据 for data in all_data: writer.writerow(data) print(f"数据采集完成,共采集 {len(all_data)} 条景点数据,文件已保存至:{SAVE_FILE}")

代码原理解析

  1. csv_header列表:定义 CSV 文件的列名,顺序与前文规划的采集字段完全一致,是表格文件的表头。
  2. with open(...)上下文管理器:Python 推荐的文件操作方式,代码执行完毕后会自动关闭文件流,无需手动调用close()方法,避免文件占用、数据写入不全等问题。
  3. 打开参数说明:
    • "w":文件打开模式为写入模式,若文件已存在则清空原有内容,重新写入;若文件不存在则自动创建新文件。
    • newline="":CSV 文件专用参数,用于消除 Windows 系统下换行符多余空行的问题,是 CSV 写入的标准配置。
    • encoding="utf-8-sig":编码设置,utf-8编码的文件直接用 Excel 打开会出现中文乱码,utf-8-sig会在文件头部添加编码标识,完美兼容 Excel、WPS 等办公软件。
  4. csv.DictWriter:字典写入器,专门用于将字典格式的数据写入 CSV 文件,fieldnames参数绑定表头列表,字典的键必须与表头名称一一对应,才能完成数据匹配写入。
  5. writer.writeheader():执行表头写入操作,仅在文件开头执行一次。
  6. writer.writerow(data):逐行写入单条景点数据,循环遍历全量数据集,完成批量存储。
  7. 收尾提示:统计总数据条数并打印保存路径,让开发者直观了解采集结果。

4.7 主程序分页调度逻辑

整合所有函数,实现分页循环、数据整合、全流程调度,这是程序的入口逻辑,代码如下:

python

运行

def main(): """主函数,程序入口""" # 定义列表存储所有景点完整数据 total_data = [] # 遍历指定页码区间 for page in range(START_PAGE, END_PAGE + 1): print(f"正在采集第 {page} 页数据...") # 拼接完整分页URL page_url = BASE_URL + str(page) # 获取列表页HTML源码 page_html = get_html(page_url) if not page_html: print(f"第 {page} 页请求失败,跳过当前页码") continue # 解析列表页数据 page_scenic = parse_list_page(page_html) # 遍历当前页每一个景点 for scenic in page_scenic: # 获取当前景点详情链接 detail_url = scenic["详情链接"] # 解析详情页攻略数据 strategy_data = parse_detail_page(detail_url) # 合并基础信息与攻略信息,去除详情链接字段 scenic.pop("详情链接") scenic.update(strategy_data) # 将整合后的数据加入总数据集 total_data.append(scenic) # 每页采集完成后添加随机延时 time.sleep(random.uniform(SLEEP_MIN, SLEEP_MAX)) # 所有页码采集完毕,写入CSV文件 save_to_csv(total_data) # 程序运行入口判断 if __name__ == "__main__": main()

代码原理解析

  1. main()为主函数,是整个爬虫程序的调度中心,所有功能函数均由主函数调用执行。
  2. total_data全局数据集列表:用于存储所有页码、所有景点的完整整合数据,贯穿整个采集流程。
  3. 分页循环:range(START_PAGE, END_PAGE + 1)生成连续页码数字,+1是因为 Python 的 range 区间为左闭右开,保证结束页码能够被正常遍历。
  4. URL 拼接:将基础 URL 与页码数字拼接,生成每一页的完整访问链接。
  5. 页码容错处理:若当前页码请求失败(page_html为 None),打印提示并使用continue跳过当前循环,直接进入下一页采集。
  6. 数据合并逻辑:
    • 列表页解析得到的scenic字典包含景点基础信息与详情链接;
    • scenic.pop("详情链接"):删除不需要存储的详情链接字段,精简最终数据;
    • scenic.update(strategy_data):将详情页解析的攻略字典合并到基础信息字典中,实现单条数据完整整合。
  7. 页间延时:单页所有景点采集完成后,添加随机延时,区分页内请求与页间请求,模拟真实浏览行为。
  8. 程序入口判断:if __name__ == "__main__"是 Python 标准入口写法,只有直接运行当前文件时,才会执行main()函数;若该文件被其他代码作为模块导入,则不会自动运行,提升代码复用性。

4.8 代码整体运行流程梳理

将所有模块串联,完整运行流程分为十个步骤:

  1. 程序启动,执行main()主函数,初始化总数据列表;
  2. 根据起止页码,循环生成每一页的分页 URL;
  3. 调用get_html函数发送 GET 请求,获取列表页 HTML 源码,处理网络异常;
  4. 调用parse_list_page解析列表页,提取景点基础信息与详情链接;
  5. 遍历当前页每一个景点,取出详情链接;
  6. 调用parse_detail_page二次请求并解析详情页,提取旅游攻略数据;
  7. 合并基础信息与攻略数据,剔除冗余字段,存入总数据集;
  8. 当前页所有景点采集完毕,执行页间随机延时,进入下一页;
  9. 所有页码遍历完成后,调用save_to_csv函数,将全量数据写入本地 CSV 文件;
  10. 打印采集结果与文件路径,程序运行结束。

五、数据清洗与进阶优化方案

5.1 原始数据常见问题与清洗方法

网页原始数据提取后,常会出现格式杂乱、内容冗余、特殊符号过多等问题,结合旅游数据场景,整理常见问题与对应的 Python 字符串处理方案,可在解析函数中追加清洗逻辑。

  1. 多余空白字符:标签嵌套导致文本出现大量空格、换行、制表符,除了.get_text(strip=True)基础清洗外,可使用replace()方法二次清理:

python

运行

text = text.replace("\n", "").replace("\t", "")
  1. HTML 转义字符:网页中 &等转义字符,可使用html内置模块进行反转义:

python

运行

import html text = html.unescape(text)
  1. 无效广告文本:部分简介、贴士中夹杂广告话术,可通过关键字匹配剔除指定内容,结合字符串切片截取有效文本。
  2. 空数据字段:部分小众景点无门票、无游玩贴士,字段为空属于正常现象,代码中已提前初始化空字符串,无需额外处理。

5.2 反爬策略进阶优化

本项目使用 UA 伪装 + 随机延时应对基础反爬,针对防护等级更高的旅游网站,可叠加以下优化方案:

  1. 多 UA 轮换:构建 UA 列表,每次请求随机选取一个 UA,避免单一标识被识别。
  2. Cookie 携带:部分网站需要登录 Cookie 才能访问完整内容,可手动抓取 Cookie 并加入请求头。
  3. 代理 IP 设置:高频采集场景下,使用代理 IP 池轮换 IP 地址,解决 IP 封禁问题,requests库支持 proxies 参数配置代理。
  4. 请求方式模拟:增加 Referer、Accept、Accept-Language 等请求头字段,完整模拟浏览器请求特征。

5.3 功能拓展方向

基于当前基础爬虫框架,可根据业务需求拓展更多实用功能:

  1. 自动获取总页码:解析页面分页标签,动态获取网站最大页码,无需手动修改END_PAGE,实现全站自动爬取。
  2. 增量爬取:读取本地已存在的 CSV 文件,对比数据,仅采集新增景点信息,避免重复采集。
  3. 数据入库:将 CSV 存储改为 MySQL、SQLite、MongoDB 数据库存储,适配大数据量存储与后续数据分析场景。
  4. 多线程爬取:引入threading多线程模块,并发请求多个页面,大幅提升采集速度,适合海量数据场景。

六、项目测试与问题排查

6.1 本地运行测试步骤

  1. 确认所有依赖库安装完成,网络可正常访问目标旅游网站;
  2. 根据实际网页的标签名、class 类名,修改代码中findfind_all方法内的标签参数,适配真实页面结构;
  3. 修改BASE_URL为真实网站 URL,调整START_PAGEEND_PAGE设置测试页码区间;
  4. 运行代码,观察控制台输出日志,查看每一页的采集状态、异常提示;
  5. 程序运行结束后,在代码同级目录找到生成的 CSV 文件,使用办公软件打开,核对字段完整性、文本格式、中文显示是否正常。

6.2 高频问题排查与解决方案

表格

问题现象排查原因解决方案
网页请求返回 403 状态码未配置 UA,被服务器识别为爬虫补充完整浏览器 User-Agent 请求头
采集文本中文乱码编码设置错误请求处设置response.encoding="utf-8",文件写入使用utf-8-sig
解析不到任何数据标签名、class 类名写错,页面结构变动重新查看网页源码,核对节点属性
程序运行卡死无响应网络卡顿,请求超时未配置增加 timeout 超时参数,优化异常捕获
详情链接无法访问相对路径未拼接域名增加路径判断逻辑,拼接网站根域名
CSV 文件出现大量空行文件打开未设置 newline 参数打开文件时添加newline=""参数

七、项目总结

本文完成了一套完整的景点介绍与旅游攻略爬虫项目开发,从环境部署、网页分析、代码编写、原理拆解、数据存储、优化拓展到问题排查,覆盖了静态网页爬虫从 0 到 1 的全流程开发逻辑。项目采用模块化编程思想,将请求、解析、存储、调度功能拆分,代码结构清晰、耦合度低,具备较强的通用性与可维护性。

从技术层面来看,项目熟练运用requests完成网络请求、BeautifulSoup4+lxml实现 HTML 节点解析,结合 Python 内置模块完成延时控制、文件读写与异常处理,同时针对中文乱码、反爬拦截、链接路径、数据清洗等爬虫开发经典问题给出标准化解决方案。从业务层面来看,项目实现了旅游行业景点基础信息与攻略文本的结构化采集,采集后的数据可直接用于文旅内容运营、出行推荐、行业数据分析等实际场景。

本项目作为通用静态网页爬虫实战案例,其核心开发思路不仅适用于旅游类网站,经过简单修改标签规则、URL 参数、采集字段后,可迁移至资讯、电商、生活服务等各类静态网页数据采集场景。开发者可基于现有框架,结合多线程、代理 IP、数据库存储、增量爬取等进阶技术持续迭代,打造功能更完善、性能更强的工业级爬虫程序。同时在爬虫使用过程中,需遵守目标网站的 robots 协议与网络安全相关法律法规,合理控制请求频率,做到合规采集数据。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询