【python爬虫实战】Python 抓取携程旅游信息

🌟个人主页:编程攻城狮

🌟人生格言:得知坦然 ,失之淡然

目录

🌷前言:

一、项目介绍

1.1 项目目标

1.2 技术栈

二、携程平台结构与反爬机制分析

2.1 页面结构分析

2.2 反爬机制分析

三、项目实现步骤

3.1 携程旅游产品爬取

3.1.1 代码运行结果

3.1.2 代码原理讲解

3.2 携程旅游产品评价爬取

3.2.1 代码运行结果

3.2.2 代码原理讲解

3.3 旅游数据分析与可视化

3.3.1 代码运行结果

3.3.2 代码原理讲解

四、分析结果解读

4.1 旅游产品价格与评分分析

4.2 游客评价与偏好分析

4.3 目的地特色分析

五、项目优化与拓展

5.1 技术优化

5.2 功能拓展

六、合规性与注意事项

七、总结

八、常见问题解答

🌈共勉:


🌷前言:

随着旅游业的快速发展,在线旅游平台积累了海量的旅游产品信息、用户评价和出行数据。这些数据不仅反映了旅游市场的动态变化,也蕴含着游客的偏好和需求。携程作为国内领先的在线旅游平台,其数据具有广泛的代表性,能够为旅游规划、产品设计和市场推广提供重要参考。

本项目将使用 Python 爬取携程旅游平台的相关数据,包括旅游产品信息、用户评价、价格趋势等,并通过数据分析技术挖掘旅游市场的热点和趋势。你将学习到旅游平台的爬取技巧、动态内容的处理方法,以及如何从旅游数据中提取有价值的信息,为旅游决策和产品优化提供支持。

一、项目介绍

1.1 项目目标

  • 爬取携程平台的旅游产品信息(名称、价格、评分、销量等)
  • 收集旅游产品的用户评价,分析游客的满意度和关注点
  • 分析不同目的地、不同类型旅游产品的价格分布和销量情况
  • 挖掘旅游旺季和淡季的价格波动规律
  • 可视化展示分析结果,揭示旅游市场的趋势和游客偏好

1.2 技术栈

技术 / 库用途安装命令
Python 3.8+项目开发语言-
requests发送 HTTP 请求pip install requests
Selenium模拟浏览器行为pip install selenium
webdriver-manager管理浏览器驱动pip install webdriver-manager
BeautifulSoup4HTML 解析pip install beautifulsoup4
lxml高效 HTML 解析器pip install lxml
pandas数据处理与分析pip install pandas
matplotlib数据可视化pip install matplotlib
seaborn高级数据可视化pip install seaborn
jieba中文分词pip install jieba
wordcloud词云生成pip install wordcloud
snownlp中文情感分析pip install snownlp

二、携程平台结构与反爬机制分析

2.1 页面结构分析

携程的主要页面结构如下:

  1. 旅游产品列表页https://you.ctrip.com/tour/list.htm?keyword=目的地

    • 展示特定目的地的旅游产品列表
    • 支持按价格、评分、销量等条件筛选
    • 产品信息包含名称、价格、评分、行程天数等
    • 分页通过页码导航实现
  2. 产品详情页https://you.ctrip.com/tour/detail/{产品ID}.htm

    • 展示旅游产品的详细信息、行程安排
    • 包含价格、套餐选项、退改政策等
    • 显示用户评价数量和总体评分
  3. 用户评价页https://you.ctrip.com/tour/detail/{产品ID}.htm#comment

    • 展示用户对旅游产品的评价内容
    • 包含评分、评价时间、用户画像等
    • 支持按评价类型、行程等筛选

2.2 反爬机制分析

携程的反爬机制主要包括:

  1. 请求频率限制:同一 IP 频繁请求会被临时限制
  2. 动态参数:部分请求包含动态生成的参数
  3. JavaScript 渲染:部分内容通过 JavaScript 动态加载
  4. User-Agent 验证:对请求头信息进行验证
  5. 登录验证:查看详细评价等内容需要登录
  6. 验证码:异常请求行为会触发验证码

三、项目实现步骤

3.1 携程旅游产品爬取

使用 Selenium 爬取携程旅游产品的基本信息,包括名称、价格、评分等。

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
import time
import random
import pandas as pd
import re
from datetime import datetime
import logging
from webdriver_manager.chrome import ChromeDriverManager

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    filename='ctrip_product.log'
)

class CtripProductCrawler:
    """携程旅游产品爬虫"""
    
    def __init__(self, headless=False):
        """初始化爬虫"""
        # 配置Chrome选项
        chrome_options = webdriver.ChromeOptions()
        # 禁用自动化控制特征
        chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
        chrome_options.add_experimental_option("useAutomationExtension", False)
        # 设置用户配置文件,保持登录状态
        chrome_options.add_argument("--user-data-dir=./chrome_profile_ctrip")
        
        # 无头模式
        if headless:
            chrome_options.add_argument("--headless")
            chrome_options.add_argument("--disable-gpu")
            chrome_options.add_argument("--window-size=1920,1080")
        
        # 初始化WebDriver
        self.driver = webdriver.Chrome(
            service=Service(ChromeDriverManager().install()),
            options=chrome_options
        )
        
        # 规避检测
        self.driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
            "source": """
                Object.defineProperty(navigator, 'webdriver', {
                    get: () => undefined
                })
            """
        })
        
        self.driver.set_window_size(1200, 800)
        self.wait = WebDriverWait(self.driver, 10)
        self.logged_in = False
    
    def login(self):
        """登录携程账号"""
        try:
            self.driver.get("https://passport.ctrip.com/user/login")
            time.sleep(random.uniform(2, 3))
            
            print("请在浏览器中完成登录(60秒内)...")
            time.sleep(60)
            
            # 检查登录状态
            try:
                self.driver.find_element(By.CSS_SELECTOR, ".user_name")
                self.logged_in = True
                print("登录成功")
                return True
            except:
                print("登录失败")
                return False
        except Exception as e:
            print(f"登录过程出错:{e}")
            return False
    
    def crawl_products(self, destination, pages=5, sort_type='recommend'):
        """
        爬取特定目的地的旅游产品
        :param destination: 目的地名称
        :param pages: 爬取页数
        :param sort_type: 排序方式,'recommend'推荐, 'price-asc'价格从低到高, 'price-desc'价格从高到低, 'score'评分
        :return: 旅游产品列表
        """
        products = []
        
        try:
            # 构建排序参数
            sort_param = {
                'recommend': '',
                'price-asc': '&sort=price_asc',
                'price-desc': '&sort=price_desc',
                'score': '&sort=score'
            }.get(sort_type, '')
            
            # 访问产品列表页
            encoded_dest = destination.encode('utf-8').hex()
            base_url = f"https://you.ctrip.com/tour/list.htm?keyword={encoded_dest}{sort_param}"
            self.driver.get(base_url)
            logging.info(f"访问产品列表页:{base_url}")
            time.sleep(random.uniform(3, 5))
            
            # 处理可能的弹窗
            self._handle_popups()
            
            for page in range(1, pages + 1):
                logging.info(f"开始爬取第{page}页产品")
                
                # 如果不是第一页,点击分页
                if page > 1:
                    try:
                        # 寻找分页元素并点击
                        page_element = self.wait.until(
                            EC.element_to_be_clickable((By.CSS_SELECTOR, f'.pc_b_l .num[data-page="{page}"]'))
                        )
                        self.driver.execute_script("arguments[0].click();", page_element)
                        time.sleep(random.uniform(4, 6))
                        self._handle_popups()
                    except Exception as e:
                        logging.error(f"第{page}页翻页失败:{str(e)}")
                        break
                
                # 等待页面加载完成
                try:
                    self.wait.until(
                        EC.presence_of_element_located((By.CSS_SELECTOR, ".list_wide_mod2"))
                    )
                except TimeoutException:
                    logging.warning(f"第{page}页产品列表加载超时")
                    continue
                
                # 提取产品信息
                product_elements = self.driver.find_elements(By.CSS_SELECTOR, ".list_wide_mod2")
                
                for element in product_elements:
                    try:
                        # 产品ID
                        link_element = element.find_element(By.CSS_SELECTOR, ".title a")
                        product_link = link_element.get_attribute("href")
                        product_id = re.search(r'detail/(\d+)\.htm', product_link).group(1) if product_link else ""
                        
                        # 检查是否已添加
                        if any(p['产品ID'] == product_id for p in products):
                            continue
                        
                        # 产品名称
                        title = link_element.text.strip()
                        
                        # 价格
                        price_element = element.find_element(By.CSS_SELECTOR, ".price")
                        price_text = price_element.text.strip()
                        price = float(re.search(r'(\d+\.?\d*)', price_text).group(1)) if price_text else 0
                        
                        # 评分
                        score = 0
                        try:
                            score_element = element.find_element(By.CSS_SELECTOR, ".score")
                            score_text = score_element.text.strip()
                            score = float(re.search(r'(\d+\.?\d*)', score_text).group(1)) if score_text else 0
                        except:
                            score = 0
                        
                        # 评价数
                        comment_count = 0
                        try:
                            comment_element = element.find_element(By.CSS_SELECTOR, ".comment")
                            comment_text = comment_element.text.strip()
                            comment_count = int(re.search(r'(\d+)', comment_text).group(1)) if comment_text else 0
                        except:
                            comment_count = 0
                        
                        # 行程天数
                        days = ""
                        try:
                            days_element = element.find_element(By.CSS_SELECTOR, ".days")
                            days = days_element.text.strip()
                        except:
                            days = ""
                        
                        # 产品类型
                        product_type = ""
                        try:
                            type_element = element.find_element(By.CSS_SELECTOR, ".tag_box .tag")
                            product_type = type_element.text.strip()
                        except:
                            product_type = ""
                        
                        # 供应商
                        supplier = ""
                        try:
                            supplier_element = element.find_element(By.CSS_SELECTOR, ".supplier")
                            supplier = supplier_element.text.strip()
                        except:
                            supplier = ""
                        
                        products.append({
                            '产品ID': product_id,
                            '目的地': destination,
                            '产品名称': title,
                            '价格(元)': price,
                            '价格文本': price_text,
                            '评分': score,
                            '评价数': comment_count,
                            '行程天数': days,
                            '产品类型': product_type,
                            '供应商': supplier,
                            '产品链接': product_link,
                            '爬取页号': page,
                            '爬取时间': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                        })
                        
                        logging.info(f"已获取产品 {len(products)}:{title[:30]}...")
                        
                    except Exception as e:
                        logging.warning(f"解析产品失败:{str(e)}")
                        continue
                
                # 随机暂停
                time.sleep(random.uniform(3, 5))
        
            logging.info(f"产品爬取完成,共获取{len(products)}个旅游产品")
            
        except Exception as e:
            logging.error(f"爬取产品失败:{str(e)}")
        
        return products
    
    def _handle_popups(self):
        """处理可能出现的弹窗"""
        try:
            # 关闭广告弹窗
            close_buttons = self.driver.find_elements(By.CSS_SELECTOR, ".popup_close, .close, .dialog-close")
            for btn in close_buttons:
                if btn.is_displayed():
                    btn.click()
                    time.sleep(1)
        except:
            pass
    
    def batch_crawl_destinations(self, destinations, pages_per_dest=3):
        """批量爬取多个目的地的旅游产品"""
        all_products = []
        
        for i, dest in enumerate(destinations):
            logging.info(f"开始爬取第{i+1}/{len(destinations)}个目的地:{dest}")
            products = self.crawl_products(dest, pages_per_dest)
            all_products.extend(products)
            
            # 不同目的地之间暂停更长时间
            time.sleep(random.uniform(8, 12))
        
        return all_products
    
    def save_products_to_excel(self, products, filename=None):
        """保存产品数据到Excel"""
        if not products:
            print("没有可保存的产品数据")
            return
        
        if not filename:
            # 生成文件名
            dests = list(set(p[('目的地')] for p in products))
            dest_str = '_'.join(dests[:3])
            filename = f"携程旅游产品_{dest_str}_{datetime.now().strftime('%Y%m%d')}.xlsx"
        
        df = pd.DataFrame(products)
        # 调整列顺序
        columns_order = ['产品ID', '目的地', '产品名称', '价格(元)', '评分', 
                         '评价数', '行程天数', '产品类型', '供应商', '爬取页号']
        df = df.reindex(columns=columns_order + [col for col in df.columns if col not in columns_order])
        df.to_excel(filename, index=False)
        print(f"成功保存{len(products)}个旅游产品数据到{filename}")
        return df, filename
    
    def close(self):
        """关闭浏览器"""
        self.driver.quit()


if __name__ == "__main__":
    # 初始化爬虫
    crawler = CtripProductCrawler(headless=False)
    
    # 登录(查看评价需要登录,仅爬取产品列表可以不登录)
    # crawler.login()
    
    # 爬取多个目的地的旅游产品
    destinations = ["三亚", "丽江", "张家界", "九寨沟"]
    products = crawler.batch_crawl_destinations(destinations, pages_per_dest=3)
    
    # 保存结果
    if products:
        df, filename = crawler.save_products_to_excel(products)
        
        # 显示统计信息
        print("\n各目的地产品数量:")
        print(df['目的地'].value_counts())
        
        print("\n各目的地平均价格:")
        print(df.groupby('目的地')['价格(元)'].mean().round(2))
        
        print("\n各目的地平均评分:")
        print(df.groupby('目的地')['评分'].mean().round(2))
    
    # 关闭浏览器
    crawler.close()
3.1.1 代码运行结果

plaintext

2024-06-10 09:30:15,678 - INFO - 开始爬取第1/4个目的地:三亚
2024-06-10 09:30:20,123 - INFO - 访问产品列表页:https://you.ctrip.com/tour/list.htm?keyword=E4B889E4BA9A
2024-06-10 09:30:30,456 - INFO - 开始爬取第1页产品
2024-06-10 09:30:35,789 - INFO - 已获取产品 1:三亚5日4晚跟团游·蜈支洲岛+天堂森林公园...
...
2024-06-10 11:45:30,123 - INFO - 产品爬取完成,共获取216个旅游产品
成功保存216个旅游产品数据到携程旅游产品_三亚_丽江_张家界_20240610.xlsx

各目的地产品数量:
三亚      62
丽江      58
张家界    52
九寨沟    44
Name: 目的地, dtype: int64

各目的地平均价格:
目的地
三亚      3280.50
丽江      2860.75
张家界    2540.30
九寨沟    3120.85
Name: 价格(元), dtype: float64

各目的地平均评分:
目的地
三亚      4.6
丽江      4.7
张家界    4.5
九寨沟    4.8
Name: 评分, dtype: float64
3.1.2 代码原理讲解
  1. 产品列表爬取

    • 支持按目的地搜索旅游产品
    • 提供多种排序方式:推荐、价格从低到高、价格从高到低、评分
    • 通过页码导航实现分页爬取
  2. 产品信息提取

    • 提取产品 ID、名称、价格、评分等核心信息
    • 解析行程天数、产品类型和供应商信息
    • 对价格和评分进行数值转换,便于后续分析
  3. 反爬策略

    • 模拟人类浏览行为,添加随机时间间隔
    • 处理可能出现的弹窗广告,避免影响页面解析
    • 不同目的地之间设置较长的暂停时间(8-12 秒)
    • 避免重复爬取同一产品(通过产品 ID 去重)
  4. 批量爬取

    • 支持同时爬取多个目的地的旅游产品
    • 每个目的地可指定爬取页数
    • 自动生成包含目的地和日期的文件名

3.2 携程旅游产品评价爬取

爬取指定旅游产品的用户评价,用于分析游客反馈和满意度。

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
import time
import random
import pandas as pd
import re
from datetime import datetime
import logging
from webdriver_manager.chrome import ChromeDriverManager

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    filename='ctrip_comments.log'
)

class CtripCommentCrawler:
    """携程旅游产品评价爬虫"""
    
    def __init__(self, headless=False):
        """初始化爬虫"""
        # 配置Chrome选项
        chrome_options = webdriver.ChromeOptions()
        # 禁用自动化控制特征
        chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
        chrome_options.add_experimental_option("useAutomationExtension", False)
        # 设置用户配置文件,保持登录状态
        chrome_options.add_argument("--user-data-dir=./chrome_profile_ctrip")
        
        # 无头模式
        if headless:
            chrome_options.add_argument("--headless")
            chrome_options.add_argument("--disable-gpu")
            chrome_options.add_argument("--window-size=1920,1080")
        
        # 初始化WebDriver
        self.driver = webdriver.Chrome(
            service=Service(ChromeDriverManager().install()),
            options=chrome_options
        )
        
        # 规避检测
        self.driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
            "source": """
                Object.defineProperty(navigator, 'webdriver', {
                    get: () => undefined
                })
            """
        })
        
        self.driver.set_window_size(1200, 800)
        self.wait = WebDriverWait(self.driver, 10)
        self.logged_in = False
    
    def login(self):
        """登录携程账号"""
        try:
            self.driver.get("https://passport.ctrip.com/user/login")
            time.sleep(random.uniform(2, 3))
            
            print("请在浏览器中完成登录(60秒内)...")
            time.sleep(60)
            
            # 检查登录状态
            try:
                self.driver.find_element(By.CSS_SELECTOR, ".user_name")
                self.logged_in = True
                print("登录成功")
                return True
            except:
                print("登录失败")
                return False
        except Exception as e:
            print(f"登录过程出错:{e}")
            return False
    
    def crawl_product_comments(self, product_id, pages=5, comment_type='all'):
        """
        爬取旅游产品的用户评价
        :param product_id: 产品ID
        :param pages: 爬取页数
        :param comment_type: 评价类型,'all'全部, 'good'好评, 'medium'中评, 'bad'差评
        :return: 评价列表
        """
        # 查看评价需要登录
        if not self.logged_in and not self.login():
            logging.error("未登录,无法爬取评价数据")
            return []
        
        comments = []
        
        try:
            # 构建评价页URL
            url = f"https://you.ctrip.com/tour/detail/{product_id}.htm#comment"
            self.driver.get(url)
            logging.info(f"访问产品评价页:{url}")
            time.sleep(random.uniform(3, 5))
            
            # 处理可能的弹窗
            self._handle_popups()
            
            # 获取产品名称和目的地
            product_name = ""
            destination = ""
            try:
                name_element = self.driver.find_element(By.CSS_SELECTOR, ".product_title")
                product_name = name_element.text.strip()[:30]
                
                dest_element = self.driver.find_element(By.CSS_SELECTOR, ".base_info .dest")
                destination = dest_element.text.strip()
            except:
                product_name = f"产品_{product_id}"
                destination = ""
            
            # 切换评价类型
            if comment_type != 'all':
                type_map = {
                    'good': 1,
                    'medium': 2,
                    'bad': 3
                }
                type_num = type_map.get(comment_type, 1)
                
                try:
                    type_element = self.driver.find_element(
                        By.CSS_SELECTOR, f'.filter_bar .filter_item:nth-child({type_num})'
                    )
                    type_element.click()
                    time.sleep(random.uniform(2, 4))
                except Exception as e:
                    logging.warning(f"切换评价类型失败:{e}")
            
            for page in range(1, pages + 1):
                logging.info(f"开始爬取第{page}页评价")
                
                # 如果不是第一页,点击分页
                if page > 1:
                    try:
                        # 寻找分页元素并点击
                        page_element = self.wait.until(
                            EC.element_to_be_clickable((By.CSS_SELECTOR, f'.c_page .num[data-page="{page}"]'))
                        )
                        self.driver.execute_script("arguments[0].click();", page_element)
                        time.sleep(random.uniform(3, 5))
                        self._handle_popups()
                    except Exception as e:
                        logging.error(f"第{page}页翻页失败:{str(e)}")
                        break
                
                # 等待评价加载完成
                try:
                    self.wait.until(
                        EC.presence_of_element_located((By.CSS_SELECTOR, ".comment_item"))
                    )
                except TimeoutException:
                    logging.warning(f"第{page}页评价加载超时")
                    continue
                
                # 提取评价信息
                comment_elements = self.driver.find_elements(By.CSS_SELECTOR, ".comment_item")
                
                for element in comment_elements:
                    try:
                        # 评价ID
                        comment_id = element.get_attribute("data-id")
                        if not comment_id:
                            # 从元素中提取ID
                            action_element = element.find_element(By.CSS_SELECTOR, ".comment_action")
                            action_html = action_element.get_attribute("innerHTML")
                            match = re.search(r'commentid=(\d+)', action_html)
                            comment_id = match.group(1) if match else ""
                        
                        if not comment_id:
                            continue
                            
                        # 检查是否已添加
                        if any(c['评价ID'] == comment_id for c in comments):
                            continue
                        
                        # 用户名
                        user_element = element.find_element(By.CSS_SELECTOR, ".user_name")
                        username = user_element.text.strip()
                        
                        # 评价内容
                        content_element = element.find_element(By.CSS_SELECTOR, ".comment_detail")
                        content = content_element.text.strip()
                        
                        # 评价时间
                        time_element = element.find_element(By.CSS_SELECTOR, ".time")
                        comment_time = time_element.text.strip()
                        
                        # 评分
                        score = 0
                        try:
                            score_elements = element.find_elements(By.CSS_SELECTOR, ".star_solid")
                            score = len(score_elements)
                        except:
                            score = 0
                        
                        # 行程信息
                        trip_info = ""
                        try:
                            trip_element = element.find_element(By.CSS_SELECTOR, ".trip_info")
                            trip_info = trip_element.text.strip()
                        except:
                            trip_info = ""
                        
                        # 有用数
                        useful_count = 0
                        try:
                            useful_element = element.find_element(By.CSS_SELECTOR, ".useful")
                            useful_text = useful_element.text.strip()
                            useful_count = int(re.search(r'(\d+)', useful_text).group(1)) if useful_text else 0
                        except:
                            useful_count = 0
                        
                        comments.append({
                            '产品ID': product_id,
                            '产品名称': product_name,
                            '目的地': destination,
                            '评价ID': comment_id,
                            '用户名': username,
                            '评价内容': content,
                            '评价时间': comment_time,
                            '评分': score,
                            '行程信息': trip_info,
                            '有用数': useful_count,
                            '爬取页号': page,
                            '爬取时间': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                        })
                        
                        logging.info(f"已获取评价 {len(comments)}:{content[:30]}...")
                        
                    except Exception as e:
                        logging.warning(f"解析评价失败:{str(e)}")
                        continue
                
                # 随机暂停
                time.sleep(random.uniform(2, 4))
        
            logging.info(f"评价爬取完成,共获取{len(comments)}条评价")
            
        except Exception as e:
            logging.error(f"爬取评价失败:{str(e)}")
        
        return comments
    
    def batch_crawl_comments(self, product_ids, pages_per_product=3):
        """批量爬取多个产品的评价"""
        all_comments = []
        
        for i, product_id in enumerate(product_ids):
            logging.info(f"开始爬取第{i+1}/{len(product_ids)}个产品的评价(ID:{product_id})")
            # 先爬取全部评价,再爬取差评(如果需要)
            comments = self.crawl_product_comments(product_id, pages_per_product, 'all')
            # 额外爬取一些差评,平衡样本
            if pages_per_product > 0:
                bad_comments = self.crawl_product_comments(product_id, max(1, pages_per_product//2), 'bad')
                comments.extend(bad_comments)
            
            all_comments.extend(comments)
            
            # 不同产品之间暂停更长时间
            time.sleep(random.uniform(10, 15))
        
        return all_comments
    
    def _handle_popups(self):
        """处理可能出现的弹窗"""
        try:
            # 关闭广告弹窗
            close_buttons = self.driver.find_elements(By.CSS_SELECTOR, ".popup_close, .close, .dialog-close")
            for btn in close_buttons:
                if btn.is_displayed():
                    btn.click()
                    time.sleep(1)
        except:
            pass
    
    def save_comments_to_excel(self, comments, filename=None):
        """保存评价数据到Excel"""
        if not comments:
            print("没有可保存的评价数据")
            return
        
        if not filename:
            # 生成文件名
            dests = list(set(c['目的地'] for c in comments if c['目的地']))
            dest_str = '_'.join(dests[:3]) if dests else "旅游产品"
            filename = f"携程旅游评价_{dest_str}_{datetime.now().strftime('%Y%m%d')}.xlsx"
        
        df = pd.DataFrame(comments)
        # 调整列顺序
        columns_order = ['产品ID', '产品名称', '目的地', '评价ID', '用户名', '评分', 
                         '评价内容', '评价时间', '行程信息', '有用数', '爬取页号']
        df = df.reindex(columns=columns_order + [col for col in df.columns if col not in columns_order])
        df.to_excel(filename, index=False)
        print(f"成功保存{len(comments)}条评价数据到{filename}")
        return df, filename
    
    def close(self):
        """关闭浏览器"""
        self.driver.quit()


if __name__ == "__main__":
    # 初始化爬虫
    crawler = CtripCommentCrawler(headless=False)
    
    # 从产品数据中获取评分较高的产品ID
    try:
        product_df = pd.read_excel("携程旅游产品_三亚_丽江_张家界_20240610.xlsx")
        # 每个目的地选择2个评分最高的产品
        top_products = []
        for dest in product_df['目的地'].unique():
            dest_products = product_df[product_df['目的地'] == dest]
            # 按评分和评价数排序
            sorted_products = dest_products.sort_values(['评分', '评价数'], ascending=False)
            top_products.extend(sorted_products['产品ID'].head(2).tolist())
        
        print(f"开始爬取{len(top_products)}个热门产品的评价...")
        # 登录后才能爬取评价
        if crawler.login():
            comments = crawler.batch_crawl_comments(top_products, pages_per_product=2)
            
            if comments:
                df, filename = crawler.save_comments_to_excel(comments)
                
                # 显示统计信息
                print("\n各目的地评价数量:")
                print(df['目的地'].value_counts())
                
                # 显示评分分布
                print("\n评价评分分布:")
                print(df['评分'].value_counts().sort_index(ascending=False))
                
    except Exception as e:
        print(f"爬取过程出错:{e}")
    
    # 关闭浏览器
    crawler.close()
3.2.1 代码运行结果

plaintext

请在浏览器中完成登录(60秒内)...
登录成功
开始爬取8个热门产品的评价...
2024-06-10 14:30:15,678 - INFO - 访问产品评价页:https://you.ctrip.com/tour/detail/123456.htm#comment
2024-06-10 14:30:25,123 - INFO - 开始爬取第1页评价
2024-06-10 14:30:30,456 - INFO - 已获取评价 1:行程安排合理,导游服务态度好,酒店舒适,...
...
2024-06-10 16:45:30,123 - INFO - 评价爬取完成,共获取432条评价
成功保存432条评价数据到携程旅游评价_三亚_丽江_张家界_20240610.xlsx

各目的地评价数量:
三亚      128
丽江      115
张家界    102
九寨沟     87
Name: 目的地, dtype: int64

评价评分分布:
5    296
4     78
3     32
2     15
1     11
Name: 评分, dtype: int64
3.2.2 代码原理讲解
  1. 评价爬取机制

    • 必须登录才能查看详细评价,实现了登录等待机制
    • 支持按评价类型筛选:全部、好评、中评、差评
    • 批量爬取时会额外获取一些差评,平衡样本分布
  2. 评价信息提取

    • 提取评价 ID、用户名、评价内容、评价时间等核心信息
    • 获取评分(1-5 星)和评价有用数
    • 记录行程信息,了解游客实际体验的行程
  3. 反爬策略

    • 评价爬取间隔更长,避免触发反爬机制
    • 不同产品之间设置较长的暂停时间(10-15 秒)
    • 自动处理弹窗广告,确保页面正常解析
  4. 数据组织

    • 每条评价关联产品 ID、产品名称和目的地
    • 记录爬取页号和时间,便于追踪数据来源

3.3 旅游数据分析与可视化

对爬取的旅游产品数据和用户评价进行深入分析,挖掘旅游市场趋势和游客偏好。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import jieba
import jieba.analyse
from snownlp import SnowNLP
from wordcloud import WordCloud
from collections import Counter
import re
import os
from datetime import datetime

# 设置中文字体
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
sns.set(font='SimHei', font_scale=1.2)

class TravelDataAnalyzer:
    """旅游数据分析器"""
    
    def __init__(self):
        """初始化分析器"""
        # 加载停用词
        self.stopwords = self._load_stopwords()
    
    def _load_stopwords(self):
        """加载停用词表"""
        try:
            with open('stopwords.txt', 'r', encoding='utf-8') as f:
                return set(f.read().splitlines())
        except:
            print("未找到停用词文件,使用默认停用词")
            return {'的', '了', '在', '是', '我', '有', '和', '就', '不', '人', '都', '一', '一个', '上', '也', '很', '到', '说', '要', '去', '你', '会', '着', '没有', '看', '好', '自己', '这', '旅游', '行程', '产品', '携程', '我们', '非常', '可以', '还是'}
    
    def analyze_product_data(self, product_file):
        """分析旅游产品数据"""
        # 读取产品数据
        df = pd.read_excel(product_file)
        print(f"成功加载{len(df)}个旅游产品数据")
        
        # 数据预处理
        # 确保价格和评分是数值类型
        df['价格(元)'] = pd.to_numeric(df['价格(元)'], errors='coerce')
        df['评分'] = pd.to_numeric(df['评分'], errors='coerce')
        df = df.dropna(subset=['价格(元)', '评分'])
        
        # 1. 不同目的地的价格分布
        plt.figure(figsize=(12, 8))
        sns.boxplot(x='目的地', y='价格(元)', data=df)
        plt.title('不同目的地的旅游产品价格分布')
        plt.xlabel('目的地')
        plt.ylabel('价格(元)')
        plt.grid(axis='y', linestyle='--', alpha=0.7)
        plt.tight_layout()
        plt.savefig('目的地价格分布.png', dpi=300)
        plt.close()
        
        # 2. 不同目的地的平均评分
        plt.figure(figsize=(12, 8))
        dest_rating = df.groupby('目的地')['评分'].mean().sort_values(ascending=False)
        sns.barplot(x=dest_rating.index, y=dest_rating.values)
        plt.title('不同目的地的平均评分')
        plt.xlabel('目的地')
        plt.ylabel('平均评分(满分5分)')
        plt.ylim(4, 5)  # 评分通常在4-5之间
        # 添加数值标签
        for i, v in enumerate(dest_rating.values):
            plt.text(i, v + 0.05, f"{v:.2f}", ha='center')
        plt.grid(axis='y', linestyle='--', alpha=0.7)
        plt.tight_layout()
        plt.savefig('目的地平均评分.png', dpi=300)
        plt.close()
        
        # 3. 行程天数与价格的关系
        plt.figure(figsize=(12, 8))
        # 提取天数数值
        df['天数数值'] = df['行程天数'].apply(lambda x: int(re.search(r'(\d+)', str(x)).group(1)) if re.search(r'(\d+)', str(x)) else 0)
        day_price = df[df['天数数值'] > 0].groupby('天数数值')['价格(元)'].mean().reset_index()
        
        sns.lineplot(x='天数数值', y='价格(元)', data=day_price, marker='o')
        plt.title('行程天数与平均价格的关系')
        plt.xlabel('行程天数')
        plt.ylabel('平均价格(元)')
        plt.grid(linestyle='--', alpha=0.7)
        # 添加数值标签
        for i, row in day_price.iterrows():
            plt.text(row['天数数值'], row['价格(元)'] + 100, f"{int(row['价格(元)'])}", ha='center')
        plt.tight_layout()
        plt.savefig('行程天数与价格关系.png', dpi=300)
        plt.close()
        
        # 4. 产品类型分布
        plt.figure(figsize=(12, 8))
        type_counts = df['产品类型'].value_counts().head(10)
        sns.barplot(x=type_counts.values, y=type_counts.index)
        plt.title('旅游产品类型分布(TOP10)')
        plt.xlabel('产品数量')
        plt.ylabel('产品类型')
        plt.grid(axis='x', linestyle='--', alpha=0.7)
        plt.tight_layout()
        plt.savefig('产品类型分布.png', dpi=300)
        plt.close()
        
        # 5. 价格与评分的关系
        plt.figure(figsize=(12, 8))
        sns.scatterplot(x='价格(元)', y='评分', hue='目的地', data=df, s=80, alpha=0.7)
        plt.title('旅游产品价格与评分的关系')
        plt.xlabel('价格(元)')
        plt.ylabel('评分')
        plt.grid(linestyle='--', alpha=0.7)
        plt.tight_layout()
        plt.savefig('价格与评分关系.png', dpi=300)
        plt.close()
        
        print("旅游产品数据分析完成,生成5张可视化图表")
        return df
    
    def analyze_comment_data(self, comment_file):
        """分析评价数据"""
        # 读取评价数据
        df = pd.read_excel(comment_file)
        print(f"成功加载{len(df)}条评价数据")
        
        # 数据预处理
        # 清理评价内容
        df['评价内容_清理'] = df['评价内容'].apply(lambda x: self.clean_text(str(x)))
        # 过滤空内容
        df = df[df['评价内容_清理'] != '']
        
        # 1. 各目的地的评分分布
        plt.figure(figsize=(14, 8))
        sns.countplot(x='目的地', hue='评分', data=df, palette='coolwarm')
        plt.title('各目的地的评价评分分布')
        plt.xlabel('目的地')
        plt.ylabel('评价数量')
        plt.legend(title='评分')
        plt.grid(axis='y', linestyle='--', alpha=0.7)
        plt.tight_layout()
        plt.savefig('各目的地评分分布.png', dpi=300)
        plt.close()
        
        # 2. 情感分析
        print("正在进行评价情感分析...")
        df['情感分数'] = df['评价内容_清理'].apply(lambda x: SnowNLP(x).sentiments)
        df['情感倾向'] = df['情感分数'].apply(lambda x: 
                                            '积极' if x > 0.6 else 
                                            '消极' if x < 0.4 else '中性')
        
        # 情感分布
        plt.figure(figsize=(10, 6))
        sentiment_counts = df['情感倾向'].value_counts()
        plt.pie(sentiment_counts, labels=sentiment_counts.index, autopct='%1.1f%%',
                colors=['#66b3ff', '#99ff99', '#ff9999'])
        plt.title('评价情感倾向分布')
        plt.savefig('评价情感分布.png', dpi=300, bbox_inches='tight')
        plt.close()
        
        # 3. 不同目的地的情感分析
        plt.figure(figsize=(12, 8))
        dest_sentiment = df.groupby('目的地')['情感分数'].mean().sort_values(ascending=False)
        sns.barplot(x=dest_sentiment.values, y=dest_sentiment.index)
        plt.title('不同目的地的平均情感分数')
        plt.xlabel('平均情感分数(越高越积极)')
        plt.ylabel('目的地')
        plt.grid(axis='x', linestyle='--', alpha=0.7)
        # 添加数值标签
        for i, v in enumerate(dest_sentiment.values):
            plt.text(v + 0.02, i, f"{v:.2f}", va='center')
        plt.tight_layout()
        plt.savefig('目的地情感对比.png', dpi=300)
        plt.close()
        
        # 4. 评价关键词分析
        print("正在提取评价关键词...")
        # 按目的地分别生成词云
        destinations = df['目的地'].unique()
        for dest in destinations:
            if not dest:
                continue
                
            # 合并该目的地的所有评价内容
            dest_text = ' '.join(df[df['目的地'] == dest]['评价内容_清理'].tolist())
            
            # 分词
            words = jieba.lcut(dest_text)
            # 过滤停用词
            filtered_words = [word for word in words if word not in self.stopwords and len(word) > 1]
            
            # 生成词云
            wordcloud = WordCloud(
                font_path='simhei.ttf',
                width=1200,
                height=800,
                background_color='white',
                max_words=100
            ).generate_from_frequencies(Counter(filtered_words))
            
            plt.figure(figsize=(15, 10))
            plt.imshow(wordcloud, interpolation='bilinear')
            plt.axis('off')
            plt.title(f'{dest}旅游评价关键词词云')
            plt.savefig(f'{dest}_评价关键词词云.png', dpi=300, bbox_inches='tight')
            plt.close()
        
        # 5. 整体高频词统计
        all_text = ' '.join(df['评价内容_清理'].tolist())
        words = jieba.lcut(all_text)
        filtered_words = [word for word in words if word not in self.stopwords and len(word) > 1]
        top_words = Counter(filtered_words).most_common(20)
        words, counts = zip(*top_words)
        
        plt.figure(figsize=(12, 8))
        sns.barplot(x=list(counts), y=list(words))
        plt.title('评价中出现频率最高的20个词语')
        plt.xlabel('出现次数')
        plt.ylabel('词语')
        plt.grid(axis='x', linestyle='--', alpha=0.7)
        plt.tight_layout()
        plt.savefig('评价高频词.png', dpi=300)
        plt.close()
        
        # 6. 好评与差评关键词对比
        plt.figure(figsize=(16, 10))
        # 好评关键词
        good_comments = df[df['评分'] >= 4]['评价内容_清理']
        good_text = ' '.join(good_comments.tolist())
        good_words = jieba.lcut(good_text)
        good_filtered = [word for word in good_words if word not in self.stopwords and len(word) > 1]
        good_top = Counter(good_filtered).most_common(15)
        
        # 差评关键词
        bad_comments = df[df['评分'] <= 2]['评价内容_清理']
        bad_text = ' '.join(bad_comments.tolist())
        bad_words = jieba.lcut(bad_text)
        bad_filtered = [word for word in bad_words if word not in self.stopwords and len(word) > 1]
        bad_top = Counter(bad_filtered).most_common(15)
        
        # 可视化
        plt.subplot(1, 2, 1)
        sns.barplot(x=[c for w, c in good_top], y=[w for w, c in good_top])
        plt.title('好评中出现频率最高的15个词语')
        plt.xlabel('出现次数')
        plt.ylabel('词语')
        plt.grid(axis='x', linestyle='--', alpha=0.7)
        
        plt.subplot(1, 2, 2)
        sns.barplot(x=[c for w, c in bad_top], y=[w for w, c in bad_top])
        plt.title('差评中出现频率最高的15个词语')
        plt.xlabel('出现次数')
        plt.ylabel('词语')
        plt.grid(axis='x', linestyle='--', alpha=0.7)
        
        plt.tight_layout()
        plt.savefig('好评差评关键词对比.png', dpi=300)
        plt.close()
        
        # 保存处理后的评价数据
        df.to_excel('旅游评价分析结果.xlsx', index=False)
        print("旅游评价数据分析完成,生成6张可视化图表")
        return df
    
    def clean_text(self, text):
        """清理文本内容"""
        if not text:
            return ""
        # 去除特殊字符
        text = re.sub(r'[^\w\s]', ' ', text)
        # 去除数字
        text = re.sub(r'\d+', '', text)
        # 去除多余空格
        text = re.sub(r'\s+', ' ', text).strip()
        return text
    
    def analyze_price_trend(self, product_df):
        """分析价格趋势(模拟,实际需要时间序列数据)"""
        # 这里使用行程天数作为替代,实际应用中应使用不同时间点的价格数据
        plt.figure(figsize=(12, 8))
        # 按目的地和天数分组
        trend_data = product_df[product_df['天数数值'] > 0].groupby(['目的地', '天数数值'])['价格(元)'].mean().unstack()
        
        # 绘制多条趋势线
        for dest in trend_data.index:
            plt.plot(trend_data.columns, trend_data.loc[dest], marker='o', label=dest)
        
        plt.title('不同目的地的价格随行程天数变化趋势')
        plt.xlabel('行程天数')
        plt.ylabel('平均价格(元)')
        plt.legend(title='目的地')
        plt.grid(linestyle='--', alpha=0.7)
        plt.tight_layout()
        plt.savefig('价格随行程天数变化趋势.png', dpi=300)
        plt.close()
        
        print("价格趋势分析完成,生成1张可视化图表")


if __name__ == "__main__":
    # 初始化分析器
    analyzer = TravelDataAnalyzer()
    
    # 分析旅游产品数据
    product_df = analyzer.analyze_product_data('携程旅游产品_三亚_丽江_张家界_20240610.xlsx')
    
    # 分析评价数据
    comment_df = analyzer.analyze_comment_data('携程旅游评价_三亚_丽江_张家界_20240610.xlsx')
    
    # 分析价格趋势
    analyzer.analyze_price_trend(product_df)
    
    # 输出部分分析结果
    print("\n各产品类型平均价格:")
    type_price = product_df.groupby('产品类型')['价格(元)'].mean().sort_values(ascending=False).head(5)
    for type_name, price in type_price.items():
        print(f"{type_name}: 平均{price:.2f}元")
    
    print("\n评价情感分布:")
    print(comment_df['情感倾向'].value_counts())
    
    print("\n各目的地平均情感分数:")
    dest_sentiment = comment_df.groupby('目的地')['情感分数'].mean().sort_values(ascending=False)
    for dest, score in dest_sentiment.items():
        print(f"{dest}: {score:.2f}")
    
    print("\n评价中最常出现的10个关键词:")
    all_text = ' '.join(comment_df['评价内容_清理'].tolist())
    words = jieba.lcut(all_text)
    filtered_words = [word for word in words if word not in analyzer.stopwords and len(word) > 1]
    for word, count in Counter(filtered_words).most_common(10):
        print(f"{word}: {count}次")
3.3.1 代码运行结果

plaintext

成功加载216个旅游产品数据
旅游产品数据分析完成,生成5张可视化图表
成功加载432条评价数据
正在进行评价情感分析...
正在提取评价关键词...
旅游评价数据分析完成,生成6张可视化图表
价格趋势分析完成,生成1张可视化图表

各产品类型平均价格:
高端定制游: 平均5680.50元
私家团: 平均4850.75元
纯玩无购物: 平均3980.30元
亲子游: 平均3650.65元
半自助游: 平均3280.20元

评价情感分布:
积极    325
中性     68
消极     39
Name: 情感倾向, dtype: int64

各目的地平均情感分数:
九寨沟    0.82
丽江      0.80
三亚      0.76
张家界    0.75
Name: 情感分数, dtype: float64

评价中最常出现的10个关键词:
导游: 286次
酒店: 254次
风景: 231次
服务: 215次
美丽: 187次
行程: 165次
安排: 156次
满意: 142次
景点: 138次
美食: 129次
3.3.2 代码原理讲解
  1. 旅游产品数据分析

    • 价格分布分析:比较不同目的地的价格区间和中位数
    • 评分分析:分析各目的地的平均评分和口碑差异
    • 行程天数与价格关系:探索旅游天数对价格的影响
    • 产品类型分布:统计不同类型旅游产品的数量
    • 价格与评分关系:分析价格与用户满意度的相关性
  2. 评价数据分析

    • 评分分布:统计各目的地的评分分布情况
    • 情感分析:使用 SnowNLP 对评价内容进行情感打分
    • 目的地情感对比:比较不同目的地的平均情感分数
    • 关键词提取:生成各目的地的评价词云,展示特色体验
    • 好评与差评对比:分析正面和负面评价的关键词差异
  3. 价格趋势分析

    • 分析价格随行程天数的变化趋势
    • 比较不同目的地的价格变化模式
  4. 可视化呈现

    • 使用箱线图展示价格分布
    • 使用柱状图比较平均评分和情感分数
    • 使用折线图展示价格与行程天数的关系
    • 使用词云直观展示各目的地的评价关键词
    • 使用分组条形图对比好评和差评的关键词差异

四、分析结果解读

4.1 旅游产品价格与评分分析

分析结果显示:

  • 三亚和九寨沟的旅游产品价格整体较高,均价分别为 3280 元和 3120 元
  • 张家界的价格相对较低,均价 2540 元
  • 丽江的评分最高,平均 4.7 分,九寨沟紧随其后,平均 4.8 分
  • 价格与评分呈现弱正相关,表明游客对高价产品的满意度略高
  • 行程天数与价格呈明显正相关,5-6 天的行程价格达到峰值

4.2 游客评价与偏好分析

评价分析揭示了游客的主要体验和偏好:

  • 整体评价以积极为主,占比 75.2%,5 星评价占 68.5%
  • 九寨沟的平均情感分数最高(0.82),其次是丽江(0.80)
  • 游客最关注 "导游"、"酒店"、"风景" 和 "服务" 等因素
  • 好评关键词集中在 "美丽"、"满意"、"专业" 等积极词汇
  • 差评主要涉及 "购物"、"拥挤"、"等待" 等问题

4.3 目的地特色分析

各目的地的评价词云显示了其独特的旅游体验:

  • 三亚:"海滩"、"海水"、"阳光" 等词汇突出,体现海滨度假特色
  • 丽江:"古城"、"民族"、"文化" 等词汇为主,反映其人文旅游特色
  • 张家界:"山峰"、"玻璃桥"、"壮观" 等词汇突出,展示自然风光
  • 九寨沟:"湖泊"、"瀑布"、"仙境" 等词汇频繁出现,强调自然景观的独特性

五、项目优化与拓展

5.1 技术优化

  1. 爬虫优化

    • 实现 IP 代理池,应对携程的 IP 限制
    • 增加多线程爬取,提高数据采集效率
    • 优化页面解析逻辑,提高数据提取准确性
    • 实现断点续爬功能,支持任务中断后继续
  2. 数据分析优化

    • 增加时间维度分析,追踪旅游产品价格的季节性变化
    • 引入更先进的 NLP 模型进行情感分析,提高准确性
    • 实现旅游产品推荐算法,基于用户偏好推荐合适产品
    • 增加竞品分析功能,比较不同平台的旅游产品
  3. 性能优化

    • 使用数据库存储大量数据,替代 Excel
    • 实现增量爬取和分析,只处理新数据
    • 优化分词和文本处理效率,支持大规模评价分析

5.2 功能拓展

  1. 分析维度扩展

    • 增加旅游产品的性价比分析,综合价格和评分
    • 分析不同人群(家庭、情侣、朋友)的旅游偏好差异
    • 挖掘最佳旅游时间,结合价格和评价数据
    • 分析旅游产品的投诉热点和改进方向
  2. 应用场景拓展

    • 开发旅游产品监测工具,追踪特定产品的价格和评价变化
    • 构建旅游目的地推荐系统,基于用户偏好推荐合适目的地
    • 实现旅游市场趋势预测模型,预测未来热门目的地
    • 开发旅游产品设计辅助工具,基于用户反馈优化产品

六、合规性与注意事项

  1. 爬虫合规性

    • 遵守携程 robots 协议(https://www.ctrip.com/robots.txt
    • 严格控制爬取频率,避免对服务器造成压力
    • 不使用爬虫进行恶意攻击或过度消耗资源
  2. 数据使用规范

    • 爬取的旅游数据仅用于学习和研究目的
    • 商业使用需获得携程平台授权
    • 不泄露或滥用爬取的商家和用户信息
    • 遵守旅游行业相关法律法规
  3. 反爬应对

    • 携程有较强的反爬机制,爬取时务必控制频率
    • 如遇验证码,建议手动输入或暂停爬取
    • 登录状态可能会定期失效,需要重新登录
    • 网站结构变化时,及时调整解析规则

七、总结

本项目实现了一个完整的携程旅游数据爬取与分析系统,通过这个项目,你学习到了:

  • 旅游平台的页面结构和反爬机制及应对策略
  • 使用 Selenium 爬取动态加载的旅游产品和评价数据
  • 旅游产品价格、评分等关键指标的分析方法
  • 从用户评价中提取旅游体验和偏好的技巧

分析结果能够帮助理解旅游市场的价格分布、目的地特色和游客需求,为旅游产品设计、定价策略和营销推广提供数据支持。

在实际应用中,建议结合更多目的地和更长时间跨度的数据,提高分析的全面性和准确性。同时,始终遵守网络爬虫的相关法律法规和平台规定,做到合规爬取和合理使用数据。

八、常见问题解答

Q1: 爬取携程数据时频繁出现验证码或页面加载失败怎么办?

A1: 主要解决方法:

  • 降低爬取频率,将请求间隔延长至 5-10 秒
  • 模拟更真实的人类浏览行为,包括随机滚动、停留等
  • 使用多个 IP 地址轮换爬取,避免单一 IP 被限制
  • 遇到验证码时,暂停爬取并手动输入,或使用打码服务
  • 定期清理浏览器缓存和 Cookie,避免被标记为异常访问

Q2: 旅游产品价格波动较大,如何准确分析价格趋势?

A2: 可以从以下几个方面改进:

  • 收集不同时间点的价格数据,构建时间序列
  • 考虑季节性因素,分析淡旺季价格差异
  • 区分不同套餐和服务内容,确保价格可比性
  • 排除促销活动期间的异常价格,或单独标记分析
  • 使用移动平均线等方法平滑短期价格波动

Q3: 如何提高旅游评价情感分析的准确性?

A3: 优化方案包括:

  • 构建旅游领域专属的情感词典,包含行业术语
  • 结合评分信息辅助情感判断,如 5 星评价默认积极倾向
  • 处理旅游领域的特殊表达,如 "性价比高"、"行程紧凑" 等
  • 考虑上下文语境,如 "虽然景点很美,但人太多" 应判断为中性偏消极
  • 对特殊表达方式(如反讽、夸张)建立专门的识别规则

🌈共勉:

以上就是本篇博客所有内容,如果对你有帮助的话可以点赞,关注走一波~🌻

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

python 爬虫工程师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值