Python 爬取tongcheng旅游网站景点评论实战:从代码到落地全解析

内容分享2小时前发布
0 0 0

本科毕设选题:基于文本挖掘的****网评论数据情感分析研究

在数据时代,景点评论数据不仅能帮助游客决策,还能为旅游分析提供支撑。今天就带大家用 Python 实现旅游网站景点评论的爬取,从环境搭建到代码优化,全程手把手教学,新手也能轻松上手!

一、项目背景与目标

1. 需求场景

我们以某旅游平台(示例链接为景点购票页)为目标,需要爬取该景点下所有用户的用户名评论内容,并按景点名称分类存储到 CSV 文件中,方便后续数据整理与分析。

2. 技术栈选择

Selenium:模拟浏览器操作,解决动态加载页面(评论区、分页按钮需点击触发)的爬取问题,避免直接请求接口的复杂验证。BeautifulSoup:解析 HTML 页面,提取所需的用户名、评论、景点名称等关键信息,语法简洁易上手。CSV 模块:将爬取到的数据规范存储到本地文件,支持 Excel 直接打开,便于后续使用。Firefox 浏览器:配合 Selenium 使用,也可替换为 Chrome(需对应浏览器驱动)。

二、环境搭建步骤

在写代码前,必须先搞定环境配置,这是爬取成功的基础!

1. 安装依赖库

打开命令行,执行以下命令安装所需 Python 库:

 

bash



# 安装 Selenium(浏览器自动化)
pip install selenium
# 安装 BeautifulSoup(HTML 解析)
pip install beautifulsoup4
# 安装 lxml(可选,BeautifulSoup 可指定该解析器,速度更快)
pip install lxml

2. 配置浏览器驱动

Selenium 需要浏览器驱动才能控制浏览器,这里以 Firefox 为例:

 

查看本地 Firefox 版本(打开浏览器 → 右上角菜单 → 帮助 → 关于 Firefox)。下载对应版本的 geckodriver(驱动下载地址:Mozilla/geckodriver)。解压驱动文件,将 
geckodriver.exe
(Windows 系统)放到 Python 安装目录下(或添加到系统环境变量 PATH 中)。

 

如果用 Chrome 浏览器,同理下载 chromedriver 并配置即可。

三、核心代码解析

接下来逐模块拆解代码逻辑,理解每个函数的作用,才能灵活修改适配其他网站。

1. 导入所需模块

首先导入项目依赖的库,每个库的作用已标注:

 

python

运行



import random  # 后续可扩展:随机休眠,避免被反爬
from selenium import webdriver  # 控制浏览器
from selenium.webdriver.support import expected_conditions as ECS  # 等待条件
import selenium.common.exceptions as exc  # 捕获 Selenium 异常
from selenium.webdriver.common.by import By  # 元素定位方式
from selenium.webdriver.support.ui import WebDriverWait  # 显式等待
from time import sleep  # 固定休眠,等待页面加载
from bs4 import BeautifulSoup  # 解析 HTML
import csv  # 操作 CSV 文件
import os  # 处理文件路径、创建文件夹

2. 浏览器操作:按钮点击函数

爬取过程中需要点击「进入评论区」和「下一页」按钮,封装一个通用点击函数,减少重复代码:

 

python

运行



def click_button(button_xpath):
    try:
        # 显式等待:最多等 3 秒,直到按钮元素出现(避免页面未加载完就点击)
        WebDriverWait(driver, 3).until(ECS.presence_of_element_located((By.XPATH, button_xpath)))
    except exc.TimeoutException:
        # 捕获超时异常:按钮未找到时提示
        print(f"按钮(XPATH:{button_xpath})未找到,可能页面结构变化!")
    finally:
        # 无论是否超时,都尝试点击(兼容部分慢加载场景)
        driver.find_element(By.XPATH, button_xpath).click()

 

关键知识点

 

显式等待(
WebDriverWait
):比固定休眠(
sleep
)更智能,元素出现后立即执行,减少等待时间。XPATH 定位:通过元素的 XPATH 路径找到按钮,获取方式:浏览器 F12 检查元素 → 右键元素 → Copy → Copy XPATH。

3. 页面解析:提取评论数据

用 BeautifulSoup 解析浏览器页面源码,提取景点名称、用户名、评论内容:

 

python

运行



def parse_page(page_source):
    result = []  # 存储当前页的评论数据
    soup = BeautifulSoup(page_source, 'html.parser')  # 初始化解析器
    
    # 1. 提取景点名称(用于命名 CSV 文件)
    place_name_tag = soup.find('h3', class_='s_name')  # 找到景点名称标签
    # 清除标签内的 span 子元素(避免多余字符)
    for span in place_name_tag.find_all('span'):
        span.decompose()
    # 生成 CSV 文件名(注意:原代码后缀是 .cvs,这里修正为 .csv!)
    place_name_file = place_name_tag.text.strip() + ".csv"
 
    # 2. 提取当前页所有评论
    info_lists = soup.find_all('div', {'class': 'info_list mtop'})  # 找到所有评论容器
    for info in info_lists:
        # 提取用户名(通过 class="tel" 的 span 标签)
        name = info.find('span', class_='tel').text
        # 提取评论内容(通过 class="dpdetail" 的 p 标签)
        comment = info.find('p', class_='dpdetail').text
        result.append([name, comment])  # 将数据添加到列表
    
    # 3. 调用写 CSV 函数,保存当前页数据
    write_to_csv(result, place_name_file)

 

避坑提示:原代码中 CSV 文件后缀写为 
.cvs
(错误),实际应改为 
.csv
,否则 Excel 可能无法正常打开!

4. 数据存储:写入 CSV 文件

将解析后的评论数据写入本地 CSV 文件,按景点分类存储,并自动创建文件夹:

 

python

运行



# 全局变量:标记是否已写入 CSV 表头(避免每页都写表头)
header_written = False
 
def write_to_csv(data, filename):
    global header_written  # 引用全局变量
    # 1. 自动创建 ./comment 文件夹(exist_ok=True:文件夹已存在则不报错)
    os.makedirs("./comment", exist_ok=True)
    # 2. 打开 CSV 文件(mode='a':追加模式,避免覆盖历史数据)
    with open(f"./comment/{filename}", mode='a', encoding='utf_8_sig', newline='') as file:
        writer = csv.writer(file)
        # 3. 第一次写入时添加表头(用户名、评论)
        if not header_written:
            writer.writerow(['用户名', '评论'])
            header_written = True  # 标记为已写表头
        # 4. 写入当前页的所有评论数据
        writer.writerows(data)

 

编码说明:使用 
utf_8_sig
 编码而非 
utf-8
,是为了避免 CSV 文件在 Excel 中打开时出现中文乱码。

5. 主函数:串联整个爬取流程

主函数是程序的入口,负责初始化浏览器、调用上述函数、控制爬取循环:

 

python

运行



if __name__ == '__main__':
    # 1. 初始化 Firefox 浏览器(若用 Chrome,替换为 webdriver.Chrome())
    driver = webdriver.Firefox()
    # 2. 打开目标景点页面(可替换为其他景点的 URL)
    target_url = "https://www.ly.com/scenery/BookSceneryTicket_31243.html?spm=28.248030045.16699.1"
    driver.get(target_url)
    
    # 3. 定义按钮 XPATH(需根据实际页面调整,避免页面更新后失效)
    button_comment_xpath = "//*[@id='content']/div[3]/div[2]/div[5]/p/a"  # 进入评论区按钮
    button_next_xpath = "//*[@id='pageNum_title']/div[2]/div/a[10]"  # 下一页按钮
 
    # 4. 开始爬取:先进入评论区
    click_button(button_comment_xpath)
    parse_page(driver.page_source)  # 解析第一页评论
    
    # 5. 循环爬取后续页面(直到没有下一页)
    while True:
        click_button(button_next_xpath)  # 点击下一页
        try:
            sleep(2)  # 等待下一页加载(动态页面建议加休眠)
            parse_page(driver.page_source)  # 解析当前页评论
        except exc.InvalidArgumentException:
            # 捕获异常:当没有下一页时,可能触发无效参数异常
            print("已爬取到最后一页,程序结束!")
            break  # 退出循环
        except Exception as e:
            # 捕获其他未知异常,避免程序直接崩溃
            print(f"爬取过程中出现未知错误:{e}")
            continue
    # 6. 爬取结束后关闭浏览器
    driver.quit()

四、优化与反爬建议

原代码能实现基础爬取,但直接运行可能会被网站识别为爬虫,导致 IP 被限制。这里提供 3 个优化方向:

1. 增加随机休眠,模拟真人操作

在点击按钮或解析页面后,加入随机休眠时间,避免操作过于规律:

 

python

运行



import random  # 已导入,无需重复导入
 
# 在 click_button 函数或主循环中添加:
sleep(random.uniform(1, 3))  # 随机休眠 1-3 秒(均匀分布)

2. 更换 User-Agent,伪装浏览器

默认 Selenium 的 User-Agent 会暴露是自动化工具,可手动设置为正常浏览器的 User-Agent:

 

python

运行



# 初始化浏览器时添加配置
options = webdriver.FirefoxOptions()
# 设置 User-Agent(可从浏览器 F12 → Network → 任意请求 → Request Headers 中复制)
options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0")
# 应用配置
driver = webdriver.Firefox(options=options)

3. 处理「下一页」按钮失效问题

原代码通过异常捕获判断最后一页,但不够稳定。更可靠的方式是:检查「下一页」按钮是否可点击(如是否有 
disabled
 属性),若不可点击则退出循环。

五、运行效果与后续拓展

1. 预期效果

运行程序后,会自动打开 Firefox 浏览器,访问目标景点页面。自动点击「进入评论区」,解析第一页评论,保存到 
./comment/景点名称.csv
。循环点击「下一页」,直到爬完所有评论,最后关闭浏览器。打开 CSV 文件,可看到规范的「用户名 – 评论」数据,无中文乱码。

2. 后续拓展方向

多景点爬取:将目标 URL 改为景点列表页,循环爬取每个景点的评论。数据清洗:用 pandas 处理评论数据(如去除空评论、过滤敏感词)。可视化分析:用 matplotlib 或 wordcloud 生成评论词云,分析游客评价关键词。分布式爬取:结合 Scrapy + Selenium 框架,提高爬取效率(适合大量数据)。

六、注意事项

合规性:爬取前需阅读网站的 
robots.txt
 协议,避免爬取敏感数据或高频请求影响网站正常运行。页面更新:若网站更新页面结构(如按钮 XPATH 变化、评论容器 class 变更),需重新获取元素定位信息。异常处理:原代码已覆盖部分异常,但实际爬取中可能遇到新问题(如验证码、IP 封禁),需根据情况补充异常处理逻辑。

 

通过这个项目,不仅能掌握 Selenium + BeautifulSoup 的核心用法,还能理解爬虫的完整流程(请求 – 解析 – 存储 – 优化)。如果在实践中遇到问题,欢迎在评论区交流,一起解决!

 

最后,附上完整的优化后代码(已修正 CSV 后缀、增加随机休眠),直接复制即可运行:

 

python

运行



import random
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as ECS
import selenium.common.exceptions as exc
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from time import sleep
from bs4 import BeautifulSoup
import csv
import os
 
header_written = False
 
 
def click_button(button_xpath, driver):
    try:
        WebDriverWait(driver, 5).until(ECS.presence_of_element_located((By.XPATH, button_xpath)))
        sleep(random.uniform(1, 2))  # 随机休眠,模拟真人操作
    except exc.TimeoutException:
        print(f"按钮(XPATH:{button_xpath})超时未找到,可能页面结构变更!")
        return False
    try:
        driver.find_element(By.XPATH, button_xpath).click()
        sleep(random.uniform(1.5, 3))  # 点击后等待页面加载
        return True
    except Exception as e:
        print(f"点击按钮失败:{e}")
        return False
 
 
def parse_page(page_source):
    result = []
    soup = BeautifulSoup(page_source, 'html.parser')
    
    # 提取景点名称
    place_name_tag = soup.find('h3', class_='s_name')
    if not place_name_tag:
        print("未找到景点名称,使用默认文件名")
        return result, "默认景点.csv"
    for span in place_name_tag.find_all('span'):
        span.decompose()
    place_name = place_name_tag.text.strip()
    place_name_file = f"{place_name}.csv"
 
    # 提取评论
    info_lists = soup.find_all('div', {'class': 'info_list mtop'})
    if not info_lists:
        print("当前页未找到评论数据")
        return result, place_name_file
    
    for info in info_lists:
        name_tag = info.find('span', class_='tel')
        comment_tag = info.find('p', class_='dpdetail')
        if name_tag and comment_tag:
            name = name_tag.text.strip()
            comment = comment_tag.text.strip()
            result.append([name, comment])
    return result, place_name_file
 
 
def write_to_csv(data, filename):
    global header_written
    os.makedirs("./comment", exist_ok=True)
    file_path = f"./comment/{filename}"
    with open(file_path, mode='a', encoding='utf_8_sig', newline='') as file:
        writer = csv.writer(file)
        if not header_written and data:  # 有数据才写表头
            writer.writerow(['用户名', '评论'])
            header_written = True
        if data:
            writer.writerows(data)
            print(f"成功写入 {len(data)} 条评论到 {file_path}")
 
 
if __name__ == '__main__':
    # 浏览器配置
    options = webdriver.FirefoxOptions()
    options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0")
    driver = webdriver.Firefox(options=options)
    driver.maximize_window()  # 最大化窗口,避免元素被遮挡
    
    # 目标 URL(可替换为其他景点 URL)
    target_url = "https://www.ly.com/scenery/BookSceneryTicket_31243.html?spm=28.248030045.16699.1"
    driver.get(target_url)
    sleep(3)  # 初始页面加载等待
 
    # 按钮 XPATH(需根据实际页面调整)
    button_comment_xpath = "//*[@id='content']/div[3]/div[2]/div[5]/p/a"
    button_next_xpath = "//*[@id='pageNum_title']/div[2]/div/a[10]"
 
    # 进入评论区
    if not click_button(button_comment_xpath, driver):
        print("进入评论区失败,程序退出")
        driver.quit()
        exit()
    
    # 解析第一页
    first_page_data, filename = parse_page(driver.page_source)
    write_to_csv(first_page_data, filename)
 
    # 循环爬取下一页
    page_num = 2
    while True:
        print(f"
开始爬取第 {page_num} 页")
        if not click_button(button_next_xpath, driver):
            print("无法点击下一页,可能已到最后一页")
            break
        
        # 解析当前页
        page_data, _ = parse_page(driver.page_source)
        if not page_data:
            print

 

© 版权声明

相关文章

暂无评论

none
暂无评论...