Python-爬取携程景点信息

本文分享了使用Python爬取携程网站上景点信息的方法。通过分析网页结构,采用Selenium和Requests结合的方式,实现了对景点详情页数据的有效抓取,包括门票价格、景点介绍等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

携程在手 说走就走。今天来记录一下爬取携程旅行的教程。
首先告诉大家,爬携程还是相对简单的,当然也有难得方法,这里就以实现为最终目标讲解最简单得方法。在这里插入图片描述
我要采集得是携程门票一栏得景点信息。还是照惯例用chrome抓包。首先需要获取每个景点详情页的url,我这边找到了直接贴图。
在这里插入图片描述
原来以为拿到这个页面问题就解决了,可实践发现事情并没有我想的那么简单,这是一个post请求。我把参数都带上进行访问,压根拿不到数据。返回的json文件是这样的。
在这里插入图片描述
研究了一下,发现参数里有一个变动的加密参数‘traceid’,很明显每次请求都会生成一个一次性的加密参数,一次之后就过期无法复用,如果要走这个方法只能去做js逆向解密破解它,然而我内心是拒绝的,这不符合我的人物设定。
也给看到文章的朋友讲讲,如果你不是专注只做爬虫的话,其实没必要去研究js解密,因为首先你得会js。爬虫这个方向说实话入门的下限很低,但是上限非常高。如果你一心只搞爬虫,那可以花大把时间去研究,像我这种只拿爬虫作为工具的就不想花太多时间在这上面了。所以这里我改用selenium去获取数据,简单实用太香了。
在这里插入图片描述

selenium功能还是挺强大的,使用这个方法就可以拿到class值为’right-content-list’标签下的所有内容。然后就是正则匹配出每个景点详情页的url以及标题。拿到详情页的标题之后就可以requests请求详情页数据了。详情页的数据可以直接拿到比较简单。
然后在这里解释一下为什么要用while True死循环,因为携程在换页的时候它的url是不会变化的,所以这里只能用selenium的自动点击功能,让它在采完第一页数据之后自动跳转下一页进行采集,设置好你要爬取的页码,也就是我代码里的self.page的值,达到爬取的页数就跳出循环即可。
到了爬取详情页这一步,就看你想采什么样的数据去写规则了。在这里我只提一点,不难但是麻烦,大家在往下看之前也可以先思考一下。看下图。
在这里插入图片描述
图中的内容,有景点介绍 开放时间 优待政策几栏,但不是每个景点都是这几个,有的是只有一个,有的还有一个服务设备栏,而且看它的前端代码,属性标签啥的都是一模一样的,遇到这种情况如果是你会怎么去获取,才能保证不会少拿而且又能正确对应上数据?
思考令人进步。。。
下面给出我的答案!
在这里插入图片描述
我这里是把这个栏目的标题以及内容分别使用xpath提取形成两个列表,因为不管到底有几个内容,它的标题跟内容都肯定是一一对应上的,而列表是有序的。列表出来之后就是使用dict(zip(desc_title_list, desc_list)),先转json再转字典,标题就是key值,内容就是value值。如果要单独展示就遍历字典就可以了。这一步我代码里没写,大家自己去写吧。
要注意的点大致就这些,下面贴我的完整代码!!!

import pandas
import re
import time
import requests
import urllib3
from lxml import etree
from selenium.webdriver import Chrome
from selenium.webdriver.chrome.options import Options

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class Jy_jd(object):
    def __init__(self):
        options = Options()
        options.add_argument('--headless')
        self.chrome = Chrome(options=options)
        self.chrome.get(
            'https://huodong.ctrip.com/things-to-do/list?pagetype=city&citytype=dt&keyword=%E6%8F%AD%E9%98%B3&pshowcode=Ticket2')
        time.sleep(3)
        self.page = 1
        self.headers = {
            'cookie': 'Session=SmartLinkCode=U155952&SmartLinkKeyWord=&SmartLinkQuary=&SmartLinkHost=&SmartLinkLanguage=zh; _RSG=KqK3qETfa143fOqQl4rFXB; _RDG=282f24100640c82731283334fcc3364464; _RGUID=4064a5d3-b40d-4d14-b84f-d44bdad18a43; Union=OUID=index&AllianceID=4897&SID=155952&SourceID=&createtime=1600831032&Expires=1601435831593; MKT_OrderClick=ASID=4897155952&AID=4897&CSID=155952&OUID=index&CT=1600831031597&CURL=https%3A%2F%2Fwww.ctrip.com%2F%3Fsid%3D155952%26allianceid%3D4897%26ouid%3Dindex&VAL={"pc_vid":"1600831028743.427gzc"}; MKT_CKID=1600831031634.5olt5.f6pj; MKT_CKID_LMT=1600831031635; _ga=GA1.2.248639397.1600831032; _gid=GA1.2.1954297618.1600831032; MKT_Pagesource=PC; GUID=09031031210931119554; nfes_isSupportWebP=1; appFloatCnt=1; nfes_isSupportWebP=1; ASP.NET_SessionSvc=MTAuNjAuMzUuMTQ2fDkwOTB8amlucWlhb3xkZWZhdWx0fDE1ODkwMDMyMjQ5NDI; U_TICKET_SELECTED_DISTRICT_CITY=%7B%22value%22%3A%7B%22districtid%22%3A%22835%22%2C%22districtname%22%3A%22%E6%8F%AD%E9%98%B3%22%2C%22isOversea%22%3Anull%7D%2C%22createTime%22%3A1600847243848%2C%22updateDate%22%3A1600847243848%7D; _RF1=113.118.204.141; _gat=1; _pd=%7B%22r%22%3A1%2C%22d%22%3A614%2C%22_d%22%3A613%2C%22p%22%3A634%2C%22_p%22%3A20%2C%22o%22%3A655%2C%22_o%22%3A21%2C%22s%22%3A668%2C%22_s%22%3A13%7D; _bfa=1.1600831028743.427gzc.1.1600843833503.1600847244099.5.49.10650038368; _bfs=1.30; _bfi=p1%3D290510%26p2%3D290510%26v1%3D49%26v2%3D48; _jzqco=%7C%7C%7C%7C1600831031803%7C1.1555887407.1600831031625.1600849509140.1600849530503.1600849509140.1600849530503.0.0.0.19.19; __zpspc=9.4.1600846262.1600849530.14%232%7Cwww.baidu.com%7C%7C%7C%25E6%2590%25BA%25E7%25A8%258B%7C%23',
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36',
        }

    def get_url(self):
        while True:
            content = self.chrome.find_element_by_class_name('right-content-list').get_attribute('innerHTML')
            cons = re.findall(r'href="(.*?)" title="(.*?)"', content)
            for con in cons:
                self.detail_url = 'https:' + con[0]
                self.title = con[1]
                print(self.detail_url, self.title)
                self.get_detail()
            self.chrome.find_element_by_class_name('u_icon_enArrowforward').click()
            time.sleep(1)

            self.page += 1
            if self.page == 120:
                break

    def get_detail(self):
        detail_con = requests.get(self.detail_url, verify=False, headers=self.headers).text
        # time.sleep(2)
        '''使用正则获取信息'''
        self.rank = ''.join(re.findall(r'rankText">(.*?)<', detail_con, re.DOTALL))
        self.address = ''.join(re.findall(r'景点地址</p><p class="baseInfoText">(.*?)<', detail_con, re.DOTALL))
        self.mobile = ''.join(re.findall(r'官方电话</p><p class="baseInfoText">(.*?)<', detail_con, re.DOTALL))
        print(self.rank, self.address, self.mobile)
        '''使用xpath获取信息'''
        ret = etree.HTML(detail_con)
        desc_cons = ret.xpath('//div[@class="detailModule normalModule"]//div[@class="moduleContent"]')
        desc_titles = ret.xpath('//div[@class="detailModule normalModule"]//div[@class="moduleTitle"]')
        desc_list = []
        desc_title_list = []
        for d in desc_cons:
            des = ''.join(d.xpath('.//text()'))
            desc_list.append(des)
        for d in desc_titles:
            des = ''.join(d.xpath('.//text()'))
            desc_title_list.append(des)
        desc_dict = dict(zip(desc_title_list, desc_list))
        print(desc_dict)
        '''获取图片链接'''
        img_list = []
        imgs = re.findall(r'background-image:url\((.*?)\)', detail_con, re.DOTALL)
        for img in imgs:
            '''匹配到的同一张图片会有两种尺寸,我们只要大图,所以把尺寸为521*391的匹配出来即可'''
            image = re.search(r'521_391', img)
            if image:
                img_list.append(img)
        print(img_list)
        self.get_ticket()

    def get_ticket(self):
        id = self.detail_url.split('/')[-1]
        print(id)
        ticket_url = f'https://piao.ctrip.com/ticket/dest/{id}?onlyContent=true&onlyShelf=true'
        ticket_res = requests.get(ticket_url, verify=False, headers=self.headers).text
        # time.sleep(1)
        ticket_ret = etree.HTML(ticket_res)
        ticket = ticket_ret.xpath('//table[@class="ticket-table"]//div[@class="ttd-fs-18"]/text()')
        price = ticket_ret.xpath(
            '//table[@class="ticket-table"]//td[@class="td-price"]//strong[@class="ttd-fs-24"]/text()')
        print(ticket)
        print(price)
        '''拿到的列表里可能存在不确定数量的空值,所以这里用while True把空值全部删除,这样才可以确保门票种类与价格正确对应上'''
        while True:
            try:
                ticket.remove(' ')
            except:
                break
        while True:
            try:
                price.remove(' ')
            except:
                break
        '''
            这里多一个if判断是因为我发现有些详情页即便拿到门票信息并剔除掉空值之后仍然存在无法对应的问题,原因是网页规则有变动,
            所以一旦出现这种情况需要使用新的匹配规则,否则会数据会出错(不会报错,但信息对应会错误)
        '''
        if len(ticket) != len(price):
            ticket = ticket_ret.xpath(
                '//table[@class="ticket-table"]/tbody[@class="tkt-bg-gray"]//a[@class="ticket-title "]/text()')
            price = ticket_ret.xpath('//table[@class="ticket-table"]//strong[@class="ttd-fs-24"]/text()')
            while True:
                try:
                    ticket.remove(' ')
                except:
                    break
            while True:
                try:
                    price.remove(' ')
                except:
                    break
            print(ticket)
            print(price)
        ticket_dict = dict(zip(ticket, price))
        print(ticket_dict)

if __name__ == '__main__':
    jy_jd = Jy_jd()
    jy_jd.get_url()
### 实现携程旅游景点信息的网络爬虫 为了实现对携程旅游景点信息的有效抓取,可以采用`Selenium`库配合浏览器自动化技术完成这一目标。对于Edge浏览器而言,需先安装对应版本的WebDriver,并通过Python脚本控制其访问指定URL,进而提取所需数据[^1]。 ```python from selenium import webdriver from selenium.webdriver.edge.options import Options as EdgeOptions import time # 设置Edge选项 edge_options = EdgeOptions() edge_options.add_argument('headless') # 启动无头模式 edge_options.add_argument('disable-gpu') edge_options.add_argument("window-size=1920,1080") # 创建Edge WebDriver实例 driver_path = 'path_to_edge_driver' # 替换为实际路径 driver = webdriver.Edge(executable_path=driver_path,options=edge_options) # 访问目标网站 target_url = "https://you.ctrip.com/sight/jingdezhen405/61145.html" driver.get(target_url) time.sleep(3) # 等待页面加载完毕 ``` 当成功打开网页后,通常还需要执行一些交互动作以触发动态内容加载,比如模拟滚动条向下拉动的操作来加载更多评论: ```python def scroll_down(driver): last_height = driver.execute_script("return document.body.scrollHeight") while True: # 下滑到底部 driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") # 等待新内容加载出来 time.sleep(2) new_height = driver.execute_script("return document.body.scrollHeight") if new_height == last_height: break last_height = new_height scroll_down(driver) ``` 最后一步是从HTML文档中解析出有用的信息片段,这可能涉及到XPath表达式的选择或是CSS选择器的应用。考虑到不同类型的元素定位方法各有优劣,在此提供一种基于BeautifulSoup库的方式处理已下载下来的静态HTML源码: ```python from bs4 import BeautifulSoup soup = BeautifulSoup(driver.page_source,'html.parser') comments_section = soup.find_all(attrs={"class": "comment"}) # 假设评论区有特定属性标识 for comment in comments_section: print(comment.text.strip()) ``` 以上代码展示了如何利用Python编写一个简单的Web Scraper用于收集来自携程旅行网上的景点评价资料。值得注意的是,由于反爬机制的存在以及法律风险考量,建议开发者遵循各平台的服务条款合理合法地开展此类活动[^2]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值