【python学习笔记】38:使用Selenium抓取去哪儿网动态页面

版权声明:本文为博主原创学习笔记,如需转载请注明来源。 https://blog.csdn.net/SHU15121856/article/details/83902626

学习《Python3爬虫、数据清洗与可视化实战》时自己的一些实践。


去哪儿网PC端自由行页面,用户需要输入出发地和目的地,点击开始定制,然后就可以看到一系列相关的旅游产品。在这个旅游产品页换页不会改变URL,而是重新加载,这时页码没有体现在URL中,这种动态页面用传统的爬虫实现不了。

安装配置

Selenium本身用Anaconda安装,作为模拟用户行为的自动化测试工具,它另外还要使用浏览器驱动。在这篇里讲述了Chrome和其驱动ChromeDriver的兼容关系,驱动可以直接在官网下载,解压后直接放在系统环境变量目录下就可以,我放在Anaconda目录下了。

XPath的选取和使用

XPath使用路径表达式来选取XML文档中的节点或节点集,在Chrome中通过检查元素可以很方便的定位并获取HTML中某个或某些元素的XPath:
在这里插入图片描述图中元素的XPath是//*[@id="list"]/div[2],这表示它是该列表中的第二个div,如果需要选取整个列表,在使用时XPath只要不指明下标就可以了,在这里也就是//*[@id="list"]/div

在使用时,注意driver.find_element_by_xpath()返回的是一个WebElement对象,driver.find_elements_by_xpath()才能返回可迭代的一系列对象,两个函数仅有一个字母s之差,很容易弄混。

爬虫代码

import requests
import urllib.request
import time
import random
from selenium import webdriver
from selenium.webdriver.common.by import By  # 用于指定HTML文件中的DOM元素
from selenium.webdriver.support.ui import WebDriverWait  # 用于等待网页加载完成
from selenium.webdriver.support import expected_conditions as EC  # 用于指定标志网页加载结束的条件

'''
去哪网PC端自由行 https://fh.dujia.qunar.com/?tf=package
ChromeDriver下载 https://npm.taobao.org/mirrors/chromedriver
'''

# 出发地城市列表
dep_citys = ['北京', '上海', '杭州', '南京', '深圳', '成都']


# 每次发送请求隔一会(模拟用户的输入和检查较慢)
def get_resp(url):
    time.sleep(5)
    return requests.get(url)


if __name__ == '__main__':
    # 控制循环次数
    j = k = 0
    # 用驱动打开Chrome浏览器
    driver = webdriver.Chrome()
    # 对每个出发地
    for dep in dep_citys:
        url = 'https://touch.dujia.qunar.com/golfz/sight/arriveRecommend?dep={}&exclude=&extensionImg=255,175'.format(
            urllib.request.quote(dep))
        response = get_resp(url)
        # 查询到的就是该出发地选定后供选择的若干目的地
        arrv_dict = response.json()
        for data_it in arrv_dict['data']:  # 这里得到的是列表中的一项项dict
            j += 1
            if j > 4:
                break
            for subMod_it in data_it['subModules']:  # 该dict里面subModules列表里的每一项
                k += 1
                if k > 6:
                    break
                for item_it in subMod_it['items']:  # 该项的item字段所示列表的每一项
                    # 通过浏览器打开网页
                    driver.get('https://fh.dujia.qunar.com/?tf=package')
                    # WebDriverWait(driver, 10)意思是使driver保持等待,最多10秒
                    # .until()里指定等待的是什么事件
                    # EC.presence_of_element_located()里面指定标志等待结束的DOM元素
                    # 里面传入元组(By.ID, "depCity")意思是等待id="depCity"的元素加载完成
                    WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "depCity")))
                    # 在Chrome检查元素后,直接右键Copy XPath即可选择相应的元素!
                    # 将出发地清空
                    driver.find_element_by_xpath("//*[@id='depCity']").clear()
                    # 将出发地写进去
                    driver.find_element_by_xpath("//*[@id='depCity']").send_keys(dep)
                    # 将目的地写进去
                    driver.find_element_by_xpath("//*[@id='arrCity']").send_keys(item_it['query'])
                    # 点击[开始定制]按钮
                    driver.find_element_by_xpath("/html/body/div[2]/div[1]/div[2]/div[3]/div/div[2]/div/a").click()
                    print("dep:%s arrv:%s" % (dep, item_it['query']))
                    # 最多抓3页
                    for i in range(3):
                        time.sleep(random.uniform(5, 6))  # 随机等待5~6秒,模拟用户每页看个五六秒
                        # 关于[下一页]按钮:在不同的页上,下一页按钮的XPath是不一样的,比如下面两个
                        # // *[ @ id = "pager"] / div / a[8]
                        # // *[ @ id = "pager"] / div / a[7]
                        # 因此不能通过这种方式来实现点击下一页
                        # 可以用XPath获得翻页的整块元素,然后在其中找'下一页'按钮
                        page_btn_a_s = driver.find_elements_by_xpath('//*[@id="pager"]/div/a')
                        # 如果获取不到页码按钮,说明从出发地到目的地没有产品,直接跳出
                        if not page_btn_a_s:
                            break
                        # 旅行方案产品列表
                        routes = driver.find_elements_by_xpath('//*[@id="list"]/div')
                        # 如果第一页就没有旅行产品(如北京到泰国),那么后面的页也不会有
                        if not routes:
                            break
                        for route in routes:
                            result = {
                                'date': time.strftime('%Y-%m-%d', time.localtime(time.time())),
                                'dep': dep,
                                'arrv': item_it['query'],
                                'result': route.text
                            }
                            print(result)  # 这里可以做存到数据库的操作
                        has = False  # 记录是否找得到'下一页'
                        for a in page_btn_a_s:
                            if a.text == u"下一页":
                                has = True
                                a.click()
                                break
                        if not has:  # 如果没找到下一页
                            break  # 说明已经是最后一页,结束这一系产品的循环

运行结果

在这里插入图片描述

善后处理

后台可能还存在chromedriver进程:
在这里插入图片描述
任务管理器里右键->结束进程树。

猜你喜欢

转载自blog.csdn.net/SHU15121856/article/details/83902626