【旅游景点项目日记 | 第二篇】基于Selenium爬取携程网景点详细数据

文章目录

  • 3.基于Selenium爬取携程网景点详细数据
    • 3.1前提环境
    • 3.2思路
    • 3.3代码详讲
      • 3.3.1查询指定城市的所有景点
      • 3.3.2获取详细景点的访问路径
      • 3.3.3获取景点的详细信息
    • 3.4数据库设计
    • 3.5全部代码
    • 3.6效果图

3.基于Selenium爬取携程网景点详细数据

3.1前提环境

  1. 确保安装python3.x环境
  2. 以管理员身份打开cmd,安装selenium、pymysql、datetime,默认安装最新版即可
pip install selenium
pip install pymysql
pip install datetime
  1. 确保chrome安装对应版本的驱动(将该驱动放在chrome安装路径下),用于控制chrome浏览器,并将路径添加到环境变量的Path变量中,如图所示!

    #安装chrome驱动教程链接:
    https://blog.csdn.net/linglong_L/article/details/136283810
    

    image-20240324223035454

3.2思路

  1. 搜索指定城市景点,网站通过分页进行展示
  2. 使用selenium每个景点的详细访问路径,并点击该路径获取详细景点信息,再通过正则表达式获取需要的内容

image-20240324220832823

  1. 如下图,景点的详细信息有:景点名称、景点等级(1-5A)、景点地址、开放时间(有两种,我们采用下面的)、联系电话、景点介绍、景点图片等内容

    image-20240324221249083

3.3代码详讲

3.3.1查询指定城市的所有景点

  • 控制打开chrome,并访问指定查询所有景点路径
    def __init__(self):
        options = Options()
        options.add_argument('--headless')
        service = Service()
        self.chrome = Chrome(service=service)
        self.chrome.get(
            'https://huodong.ctrip.com/things-to-do/list?pagetype=city&citytype=dt&keyword=%E6%A2%85%E5%B7%9E&id=523&name=%E6%A2%85%E5%B7%9E&pshowcode=Ticket2&kwdfrom=srch&bookingtransactionid=1711160613361_6064')
        time.sleep(3)
        self.page = 1
        self.headers = {
            'cookie': 'suid=lh/P1+4RKuhAYg684ErS+g==; suid=lh/P1+4RKuhAYg684ErS+g==',
            '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',
        }

3.3.2获取详细景点的访问路径

  • 使用selenium的根据class定位元素方法,找到详细景点的href属性,即为该景点的访问路径
  • 并通过page属性控制访问的页数
	#获取景点请求路径
    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

image-20240324222130900

3.3.3获取景点的详细信息

  • 景点的详细信息有:景点名称、景点等级(1-5A)、景点地址、开放时间(有两种,我们采用下面的)、联系电话、景点介绍、景点图片等内容
  • 通过正则表达式获取,详细代码如下:
  • 并每次获取详细信息之后,将信息保存到mysql数据库中
    def get_detail(self):
        detail_con = requests.get(self.detail_url, verify=False, headers=self.headers).text
        # time.sleep(2)
        '''使用正则获取信息'''
        self.title = ''.join(re.findall(r'<div class="title"><h1>(.*?)<', detail_con, re.DOTALL))
        print('景点名称:'+self.title)

        #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))
        self.quality_grade= ''.join(re.findall(r'<div class="titleTips"><span>(.*?)<!--', detail_con, re.DOTALL))
        #self.openTime = ''.join(re.findall(r'开放时间</div><div class="moduleContent">(.*?)<', detail_con, re.DOTALL))

        first_three_characters = self.address[:3]
        print('所在省份城市:'+'广东省'+first_three_characters)

        print('详细地址:'+self.address)
        #print('开放时间:'+self.openTime)
        print('电话:'+self.mobile)
        print('等级:'+self.quality_grade)
        if self.quality_grade=='':
            self.quality_grade=0

        '''使用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)

        first_value = list(desc_dict.values())[:2]  # 获取前两个值
        if len(first_value) >= 1:
            introduction = first_value[0]
        else:
            introduction = ''

        if len(first_value) >= 2:
            opening_hours = first_value[1]
        else:
            opening_hours = ''

        print('介绍:'+introduction)
        print('开放时间:'+ opening_hours)

        '''获取图片链接'''
        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(",".join(img_list))

        conn = pymysql.connect(host='localhost', user='root', password='root',
                               database='travel_ams', charset='utf8mb4')
        cursor = conn.cursor()

        sql = "INSERT INTO ams_attraction (attraction_name, quality_grade, province_city, location, open_hour, phone, introduction, images, add_time) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)"
        values = (self.title, self.quality_grade, '广东省'+first_three_characters, self.address, opening_hours, self.mobile, introduction,",".join(img_list)  ,datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
        cursor.execute(sql, values)

        conn.commit()
        conn.close()

        #self.get_ticket()

3.4数据库设计

travel_ams数据库的ams_attraction表

字段名字段类型是否为主键是否有唯一约束是否有非空约束注释
attraction_idint景点id,自增
attraction_namevarchar(20)景点名称
resource_type_idint景点资源类型id
quality_gradeint景点等级
province_cityvarchar(20)景点所在省份城市
locationvarchar(1000)详细位置
open_hourvarchar(1000)开放时间
phonevarchar(1000)电话
introductionvarchar(10000)景点介绍
imagesvarchar(1000)景点图片列表
stausint状态【1为显示,0为不显示】
add_timedatetime添加时间
update_timedatetime修改时间

创建表语句如下:

CREATE TABLE ams_attraction (
    attraction_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '景点id,自增',
    attraction_name VARCHAR(20) COMMENT '景点名称',
    resource_type_id INT COMMENT '景点资源类型id',
    province_city VARCHAR(20) COMMENT '景点所在省份城市',
    location VARCHAR(1000) COMMENT '详细位置',
    open_hour VARCHAR(1000) COMMENT '开放时间',
    phone VARCHAR(1000) COMMENT '电话',
    introduction VARCHAR(10000) COMMENT '景点介绍',
    images VARCHAR(1000) COMMENT '景点图片列表',
    status INT COMMENT '状态【1为显示,0为不显示】',
    add_time DATETIME COMMENT '添加时间',
    update_time DATETIME COMMENT '修改时间'
);

3.5全部代码

  • 执行该main方法,即可完成导入指定访问路径的景点数据
  • 可以在控制台查询是否导入成功
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
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
import pymysql
import datetime



urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class Jy_jd(object):
    def __init__(self):
        options = Options()
        options.add_argument('--headless')
        service = Service()
        self.chrome = Chrome(service=service)
        self.chrome.get(
            'https://huodong.ctrip.com/things-to-do/list?pagetype=city&citytype=dt&keyword=%E6%A2%85%E5%B7%9E&id=523&name=%E6%A2%85%E5%B7%9E&pshowcode=Ticket2&kwdfrom=srch&bookingtransactionid=1711160613361_6064')
        time.sleep(3)
        self.page = 1
        self.headers = {
            'cookie': 'suid=lh/P1+4RKuhAYg684ErS+g==; suid=lh/P1+4RKuhAYg684ErS+g==',
            '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.title = ''.join(re.findall(r'<div class="title"><h1>(.*?)<', detail_con, re.DOTALL))
        print('景点名称:'+self.title)

        #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))
        self.quality_grade= ''.join(re.findall(r'<div class="titleTips"><span>(.*?)<!--', detail_con, re.DOTALL))
        #self.openTime = ''.join(re.findall(r'开放时间</div><div class="moduleContent">(.*?)<', detail_con, re.DOTALL))

        first_three_characters = self.address[:3]
        print('所在省份城市:'+'广东省'+first_three_characters)

        print('详细地址:'+self.address)
        #print('开放时间:'+self.openTime)
        print('电话:'+self.mobile)
        print('等级:'+self.quality_grade)
        if self.quality_grade=='':
            self.quality_grade=0

        '''使用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)

        first_value = list(desc_dict.values())[:2]  # 获取前两个值
        if len(first_value) >= 1:
            introduction = first_value[0]
        else:
            introduction = ''

        if len(first_value) >= 2:
            opening_hours = first_value[1]
        else:
            opening_hours = ''

        print('介绍:'+introduction)
        print('开放时间:'+ opening_hours)

        '''获取图片链接'''
        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(",".join(img_list))

        conn = pymysql.connect(host='localhost', user='root', password='root',
                               database='travel_ams', charset='utf8mb4')
        cursor = conn.cursor()

        sql = "INSERT INTO ams_attraction (attraction_name, quality_grade, province_city, location, open_hour, phone, introduction, images, add_time) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)"
        values = (self.title, self.quality_grade, '广东省'+first_three_characters, self.address, opening_hours, self.mobile, introduction,",".join(img_list)  ,datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
        cursor.execute(sql, values)

        conn.commit()
        conn.close()

        #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()


3.6效果图

image-20240324223358077

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/485410.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

生产力工具|安装更新R软件(R、studio)

内容介绍&#xff1a; 安装R软件&#xff1a; 下载 R X64 3.5.1: 访问官方R网站 https://cran.r-project.org/。选择适合Windows版本的安装包。将安装包下载到您的计算机。 本地安装: 运行下载的“R-3.5.1-win.exe”文件。按照安装向导&#xff0c;选择安装路径&#xff0c;取消…

Windows 进程权限浅谈 -- 提权 / 降权

在 Windows 上&#xff0c;用户对权限并不敏感&#xff0c;可能最为直观的是 UAC &#xff0c;但相信很多人已经关掉了它的提示。 但其实安全性早已深入了 Windows 的方方面面。Windows Vista 引入了一个称为强制完整性控制&#xff08;Mandatory Integrity Controls&#xff0…

BMS设计中的短路保护和MOSFET选型(上)

电池管理系统&#xff08;BMS&#xff09;是一种能够对电池进行监控和管理的电子装备&#xff0c;是电池与用户之间的纽带。通过对电压、电流、温度以及SOC等数据采集&#xff0c;计算进而控制电池的充放电过程&#xff0c;主要就是为了能够提高电池的利用率&#xff0c;防止电…

【Python从入门到进阶】51、电影天堂网站多页面下载实战

接上篇《50、当当网Scrapy项目实战&#xff08;三&#xff09;》 上一篇我们讲解了使用Scrapy框架在当当网抓取多页书籍数据的效果&#xff0c;本篇我们来抓取电影天堂网站的数据&#xff0c;同样采用Scrapy框架多页面下载的模式来实现。 一、抓取需求 打开电影天堂网站&…

Ubuntu Desktop 更改默认应用程序 (Videos -> SMPlayer)

Ubuntu Desktop 更改默认应用程序 [Videos -> SMPlayer] References System Settings -> Details -> Default Applications 概况、默认应用程序、可移动介质、法律声明 默认应用程序&#xff0c;窗口右侧列出了网络、邮件、日历、音乐、视频、照片操作的默认应用程序…

OCR如何解决字体多样性难题?

OCR&#xff08;Optical Character Recognition&#xff0c;光学字符识别&#xff09;技术的确是一项非常实用的技术&#xff0c;能够将图像中的文字转化为可编辑的文本&#xff0c;大大提高了工作效率。然而&#xff0c;你所遇到的问题——字体多样性导致的模型泛化能力不足&a…

AtCoder Regular Contest 175(A~B)

补题&#xff1a;A - Spoon Taking Problem 阅读理解就能劝退好多人&#xff0c;先看B题可能收益会更高。 N个人都是这么坐的&#xff0c;勺子标号也给你标好了。 如果 s[1]L&#xff0c;那么1这个人就要拿左边的勺子&#xff0c;如果左边没有就拿右边的&#xff0c;右边也没…

[AutoSar]BSW_ OS CORE, Physical core,EcuC core,EcuC partition,OSApplication的关系

目录 关键词平台说明一、总体依赖关系二、相关概念三、在配置中的实现3.1 EcucPartition3.2 OsApplication3.3 Ecu core 关键词 嵌入式、C语言、autosar、OS、BSW 平台说明 项目ValueOSautosar OSautosar厂商vector &#xff0c; EB芯片厂商TI 英飞凌编程语言C&#xff0c;C…

Centos7 防火墙iptables?

Centos7 防火墙iptables&#xff1f; 文章目录 Centos7 防火墙iptables&#xff1f;1. 介绍2. firewalld 和 iptables区别3. 区域管理概念区域管理有如下几种不同的初始化区域&#xff1a; 4.iptables的配置1.简述2.基本原理3.iptables传输数据包的过程4. iptables规则表和链5.…

C++初阶:STL容器list的使用与初版自实现

目录 1. list的接口与使用1.1 默认成员函数1.2 迭代器与容量相关成员函数1.3 存储数据操作相关成员函数1.4 其他list操作成员函数 2. list的自实现2.1 list的自实现功能2.2 list的结点结构2.3 list的迭代器2.3 list的结构2.4 list迭代器的运算符重载2.5 list的成员函数 3. cons…

python绘图matplotlib——使用记录2

本博文来自于网络收集&#xff0c;如有侵权请联系删除 三维图绘制 1 三维散点图2 三维柱状图三维曲面 1 三维散点图 import matplotlib.pyplot as plt import numpy as npfrom mpl_toolkits.mplot3d import Axes3Dfig plt.figure() # ax fig.gca(projection"3d")…

javase day11笔记

第十一天课堂笔记 构造代码块 { } 给 所有对象 共性特点 进行初始化操作 创建对象时在堆区对象中存放实例变量,同时执行构造代码块 执行顺序:静态代码块—>非静态代码块—>构造方法 继承★★★ 将多个类中相同的实例变量和实例方法 , 单独存放到一个类中,成为父类…

【Linux】写个日志和再谈线程池

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;折纸花满衣 &#x1f3e0;个人专栏&#xff1a;信号量和线程池 目录 &#x1f449;&#x1f3fb;日志代码Log.cppMain.cc &#x1f449;&#x1f3fb;线程池代码LockGuard.hpp(自定义互斥锁&#xff0c;进…

网易web安全工程师进阶版课程

课程介绍 《Web安全工程师&#xff08;进阶&#xff09;》是由“ i春秋学院联合网易安全部”出品&#xff0c;资深讲师团队通过精炼的教学内容、丰富的实际场景及综合项目实战&#xff0c;帮助学员纵向提升技能&#xff0c;横向拓宽视野&#xff0c;牢靠掌握Web安全工程师核心…

Python6:Socket编程初步学习笔记

Socket协议概要 创建socket的时候&#xff0c;需要一些选项来说明本次使用协议具体是什么&#xff0c;常用的两个&#xff1a; 由此产生的不同组合&#xff1a; 但目前TCP(IPV4)是主流&#xff0c;SOCK_STREAMAF_INET 创建和使用Socket socket模块中有socket类&#xff1a…

51单片机学习笔记——LED闪烁和流水灯

任务分析 首先要知道LED闪烁主要是怎么工作的&#xff0c;闪烁亮灭自然是一下为高一下为低&#xff0c;亮灭的频率则需要延时来进行控制。 上节已经知道了如何点亮那延时如何做呢首先先编写主框架 这样是否可以通过循环将LED灯一直循环闪烁。 以为while一直在循环所以其实是可…

向开发板上移植ip工具:交叉编译 ip工具

一. 简介 前面几篇文章学习了 CAN设备节点的创建&#xff0c;以及如何使能 CAN驱动。 本文学习向开发板上移植ip工具。 二. 向开发板上移植ip工具&#xff1a;交叉编译 ip工具 注意&#xff1a;在移植 ip 命令的时候必须先对根文件系统做个备份&#xff01;防止操作失误导…

力扣74---搜索二维矩阵

目录 题目描述&#xff1a; 思路&#xff1a; 代码&#xff1a; 题目描述&#xff1a; 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。 每行的第一个整数大于前一行的最后一个整数。 给你一个整数 targ…

c#绘制图形

窗体工具控件 如果选纹理 ,需要在ImageList中选择图像(点击添加选择图片路径) using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Drawing2D; using System.Linq; using System.…

【Redis教程0x03】详解Redis的基本数据类型

引言 根据【Redis教程0x02】中介绍的&#xff0c;Redis的数据类型可分为5种基本数据类型&#xff08;String、Hash、List、Set、Zset&#xff09;和4种高级数据类型&#xff08;BitMap、HyperLogLog、GEO、Stream&#xff09;。在本篇博客中&#xff0c;我们将详解这9种数据类…