selenium自动化测试-获取动态页面小说

news2024/11/25 14:26:55

有的网站页面是动态加载的资源,使用bs4库只能获取静态页面内容,无法获取动态页面内容,通过selenium自动化测试工具可以获取动态页面内容。

参考之前的"bs4库爬取小说工具"文章代码,稍微修改下,就可以转成获取动态页面小说工具。

第一步:先确定目标网址

先找到小说目录页面。

网址首页:'https://www.bq0.net/'

目标小说目录页:'https://www.bq0.net/1bqg/898531870/'

第二步:确定章节目录和内容元素坐标

通过谷歌浏览器F12调试功能可以很快的定位页面元素位置.

第三步:编写代码

采用拆分步骤细化功能模块封装方法编写代码,便于后续扩展功能模块。

requests_webdriver.py:

# -*- coding: UTF-8 -*-
# selenium 自动化测试工具,爬取动态网页工具
import requests
import time
import re
from bs4 import BeautifulSoup
import random
import json
import os
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By


# 打开驱动
def open_driver():
    try:
        # 连接浏览器web驱动全局变量
        global driver
        # Linux系统下浏览器驱动无界面显示,需要设置参数
        # “–no-sandbox”参数是让Chrome在root权限下跑
        # “–headless”参数是不用打开图形界面
        '''
        chrome_options = Options()
        # 设为无头模式
        chrome_options.add_argument('--headless')
        chrome_options.add_argument('--no-sandbox')
        chrome_options.add_argument('--disable-gpu')
        chrome_options.add_argument('--disable-dev-shm-usage')
        # 连接Chrome浏览器驱动,获取驱动
        driver = webdriver.Chrome(chrome_options=chrome_options)
        '''

        # 此步骤很重要,设置chrome为开发者模式,防止被各大网站识别出来使用了Selenium
        options = Options()
        # 去掉提示:Chrome正收到自动测试软件的控制
        options.add_argument('disable-infobars')
        # 以键值对的形式加入参数,打开浏览器开发者模式
        options.add_experimental_option('excludeSwitches', ['enable-automation'])
        # 打开浏览器开发者模式
        # options.add_argument("--auto-open-devtools-for-tabs")
        driver = webdriver.Chrome(chrome_options=options)

        # driver = webdriver.Chrome()
        print('连接Chrome浏览器驱动')
        # 浏览器窗口最大化
        driver.maximize_window()
        '''
        1, 隐式等待方法
        driver.implicitly_wait(最大等待时间, 单位: 秒)
        2, 隐式等待作用
        在规定的时间内等待页面所有元素加载;
        3,使用场景:
        在有页面跳转的时候, 可以使用隐式等待。
        '''
        driver.implicitly_wait(3)
        # 强制等待,随机休眠 暂停0-3秒的整数秒,时间区间:[0,3]
        time.sleep(random.randint(0, 3))

    except Exception as e:
        driver = None
        print(str(e))


# 关闭驱动
def close_driver():
    driver.quit()
    print('关闭Chrome浏览器驱动')


def get_html_by_webdriver(url_str):
    '''
    @方法名称: 根据浏览器驱动获取动态网页内容
    @中文注释: 根据浏览器驱动获取动态网页内容
    @入参:
        @param url_str str url地址
    @出参:
        @返回状态:
            @return 0 失败或异常
            @return 1 成功
        @返回错误码
        @返回错误信息
    @作    者: PandaCode辉
    @创建时间: 2023-09-21
    @使用范例: get_html_by_webdriver('www.baidu.com')
    '''
    try:

        if (not type(url_str) is str):
            return [0, "111111", "url地址参数类型错误,不为字符串", [None]]
        print('浏览器驱动不存在,重新打开浏览器驱动.')
        # open_driver()

        # 打开网址网页
        driver.get(url_str)
        # 等待6秒启动完成
        driver.implicitly_wait(6)
        # 获取动态网页的html字符串信息
        html_str = driver.page_source
        print('开始关闭Chrome浏览器驱动')
        # close_driver()
        # print(html_str)
        return [1, '000000', "获取动态网页内容成功", [html_str]]
    except Exception as e:
        print("获取动态网页内容异常超时," + str(e))
        print('开始关闭Chrome浏览器驱动')
        close_driver()
        return [0, '999999', "获取动态网页内容异常超时," + str(e), [None]]


def spider_novel_mulu(req_dict):
    '''
    @方法名称: 爬取小说章节目录
    @中文注释: 根据参数爬取小说目录,保存到json文件
    @入参:
        @param req_dict dict 请求容器
    @出参:
        @返回状态:
            @return 0 失败或异常
            @return 1 成功
        @返回错误码
        @返回错误信息
        @param rsp_dict dict 响应容器
    @作    者: PandaCode辉
    @创建时间: 2023-09-21
    @使用范例: spider_novel_mulu(req_dict)
    '''

    try:
        if (not type(req_dict) is dict):
            return [0, "111111", "请求容器参数类型错误,不为字典", [None]]

        open_driver()
        # 根据url地址获取动态网页信息
        rst = get_html_by_webdriver(req_dict['mulu_url'])
        print('随机休眠')
        # 随机休眠 暂停0-2秒的整数秒
        time.sleep(random.randint(0, 2))
        close_driver()
        if rst[0] != 1:
            return rst
        html_str = rst[3][0]
        # 使用BeautifulSoup解析网页数据
        soup = BeautifulSoup(html_str, "html.parser")

        # 目录列表地址
        tar_dir_href = soup.select(req_dict['tar_dir_href'])
        # print(tar_dir_href)
        tar_len = len(tar_dir_href)
        print('初始爬取章节总数量:', tar_len)

        # 过滤章节下标初始化
        del_index = 0
        for dir_href in tar_dir_href:
            chap_title = dir_href.text
            # print(chap_title)
            if '第1章' in chap_title:
                break
            del_index += 1
        # 过滤章节下标
        print(del_index)
        # 过滤章节,从第一章开始
        tar_dir_href_list = tar_dir_href[del_index:]
        tar_len = len(tar_dir_href_list)
        print('过滤后章节总数量:', tar_len)

        # 目录容器
        mulu_dict = {}
        # 章节标题,列表
        mulu_dict['chap_title'] = []
        # 章节url,列表
        mulu_dict['chap_url'] = []
        # 是否完成标志: 0-未完成,1-已完成,列表
        mulu_dict['flag'] = []
        # 循环读取章节
        for dir_href in tar_dir_href_list:
            # 章节标题
            chap_title = dir_href.text
            print(chap_title)
            mulu_dict['chap_title'].append(chap_title)
            # 章节url
            chap_url = req_dict['novel_url'] + dir_href['href']
            print(chap_url)
            mulu_dict['chap_url'].append(chap_url)
            mulu_dict['flag'].append('0')
        # 转换为json字符串
        json_str = json.dumps(mulu_dict)
        json_name = req_dict['novel_name'] + '.json'
        # 写入json文件
        with open(json_name, 'w', encoding="utf-8") as json_file:
            json_file.write(json_str)
        # 返回容器
        return [1, '000000', '爬取小说目录成功', [None]]

    except Exception as e:
        print("爬取小说目录异常," + str(e))
        return [0, '999999', "爬取小说目录异常," + str(e), [None]]


def spider_novel_content(req_dict):
    '''
    @方法名称: 爬取小说章节明细内容
    @中文注释: 读取章节列表json文件,爬取小说章节明细内容,保存到文本文件
    @入参:
        @param req_dict dict 请求容器
    @出参:
        @返回状态:
            @return 0 失败或异常
            @return 1 成功
        @返回错误码
        @返回错误信息
        @param rsp_dict dict 响应容器
    @作    者: PandaCode辉
    @创建时间: 2023-09-21
    @使用范例: spider_novel_content(req_dict)
    '''

    try:
        if (not type(req_dict) is dict):
            return [0, "111111", "请求容器参数类型错误,不为字典", [None]]
        # 章节目录文件名
        json_name = req_dict['novel_name'] + '.json'
        # 检查文件是否存在
        if os.path.isfile(json_name):
            print('json文件存在,不用重新爬取小说目录.')
        else:
            print('json文件不存在')
            # 爬取小说目录
            spider_novel_mulu(req_dict)
        # 读取json文件
        with open(json_name, 'r') as f:
            data_str = f.read()
        # 转换为字典容器
        mulu_dict = json.loads(data_str)
        """
            关于open()的mode参数:
            'r':读
            'w':写
            'a':追加
            'r+' == r+w(可读可写,文件若不存在就报错(IOError))
            'w+' == w+r(可读可写,文件若不存在就创建)
            'a+' ==a+r(可追加可写,文件若不存在就创建)
            对应的,如果是二进制文件,就都加一个b就好啦:
            'rb'  'wb'  'ab'  'rb+'  'wb+'  'ab+'
        """
        file_name = req_dict['novel_name'] + '.txt'
        # 在列表中查找指定元素的下标,未完成标志下标
        flag_index = mulu_dict['flag'].index('0')
        print(flag_index)
        # 未完成标志下标为0,则为第一次爬取章节内容,否则已经写入部分,只能追加内容写入文件
        # 因为章节明细内容很多,防止爬取过程中间中断,重新爬取,不用重复再爬取之前成功写入的数据
        if flag_index == 0:
            # 打开文件,首次创建写入
            fo = open(file_name, "w+", encoding="utf-8")
        else:
            # 打开文件,再次追加写入
            fo = open(file_name, "a+", encoding="utf-8")
        # 章节总数
        chap_len = len(mulu_dict['chap_url'])
        # 在列表中查找指定元素的下标
        print('章节总数:', chap_len)

        print('打开浏览器驱动')
        open_driver()
        # 循环读取章节
        for i in range(flag_index, chap_len):
            # 章节标题
            # chap_title = mulu_dict['chap_title'][i]
            print('i : ', i)
            # # 写入文件,章节标题
            # fo.write(chap_title + "\r\n")
            # 章节url
            chap_url = mulu_dict['chap_url'][i]
            # 章节分页url列表初始化
            page_href_list = []
            # 根据url地址获取网页信息
            chap_rst = get_html_by_webdriver(chap_url)
            time.sleep(3)
            if chap_rst[0] != 1:
                # 跳出循环爬取
                break
            chap_html_str = chap_rst[3][0]
            # 使用BeautifulSoup解析网页数据
            chap_soup = BeautifulSoup(chap_html_str, "html.parser")
            # 章节内容分页数和分页url
            # 获取分页页码标签下的href元素取出
            page_href_list_tmp = chap_soup.select("div#PageSet > a")
            all_page_cnt = len(page_href_list_tmp)
            print("分页页码链接数量:" + str(all_page_cnt))
            # 去除最后/后面数字+.html
            tmp_chap_url = re.sub(r'(\d+\.html)', '', chap_url)
            for each in page_href_list_tmp:
                if len(each) > 0:
                    chap_url = tmp_chap_url + str(each.get('href'))
                    print("拼接小说章节分页url链接:" + chap_url)
                    # 判断是否已经存在列表中
                    if not chap_url in page_href_list:
                        page_href_list.append(chap_url)
            print("分页url链接列表:" + str(page_href_list))

            # 章节标题
            chap_title = chap_soup.select(req_dict['chap_title'])[0].text
            print(chap_title)
            # 写入文件,章节标题
            fo.write("\n" + chap_title + "\n")
            # 章节内容
            chap_content = chap_soup.select(req_dict['chap_content'])[0].text
            chap_content = chap_content.replace('    ', '\n').replace('  ', '\n')
            # print(chap_content)
            # 写入文件,章节内容
            fo.write(chap_content + "\n")

            # 分页列表大于0
            if len(page_href_list) > 0:
                for chap_url_page in page_href_list:
                    print("chap_url_page:" + chap_url_page)
                    time.sleep(3)
                    # 等待3秒启动完成
                    driver.implicitly_wait(3)
                    print("等待3秒启动完成")
                    # 根据url地址获取网页信息
                    chap_rst = get_html_by_webdriver(chap_url_page)
                    if chap_rst[0] != 1:
                        # 跳出循环爬取
                        break
                    chap_html_str = chap_rst[3][0]

                    # 然后用BeautifulSoup解析html格式工具,简单的获取目标元素
                    chap_soup_page = BeautifulSoup(chap_html_str, "html.parser")
                    # 章节内容
                    chap_content_page = chap_soup_page.select(req_dict['chap_content'])[0].text
                    chap_content_page = chap_content_page.replace('    ', '\n').replace('  ', '\n')
                    # print(chap_content_page)
                    # 写入文件,章节内容
                    fo.write(chap_content_page + "\n")
            # 爬取明细章节内容成功后,更新对应标志为-1-已完成
            mulu_dict['flag'][i] = '1'
        print('关闭浏览器驱动')
        close_driver()
        # 关闭文件
        fo.close()
        print("循环爬取明细章节内容,写入文件完成")
        # 转换为json字符串
        json_str = json.dumps(mulu_dict)
        # 再次写入json文件,保存更新处理完标志
        with open(json_name, 'w', encoding="utf-8") as json_file:
            json_file.write(json_str)
        print("再次写入json文件,保存更新处理完标志")
        # 返回容器
        return [1, '000000', '爬取小说内容成功', [None]]

    except Exception as e:
        print('关闭浏览器驱动')
        close_driver()
        # 关闭文件
        fo.close()
        # 转换为json字符串
        json_str = json.dumps(mulu_dict)
        # 再次写入json文件,保存更新处理完标志
        with open(json_name, 'w', encoding="utf-8") as json_file:
            json_file.write(json_str)
        print("再次写入json文件,保存更新处理完标志")
        print("爬取小说内容异常," + str(e))
        return [0, '999999', "爬取小说内容异常," + str(e), [None]]


# 主方法
if __name__ == '__main__':
    req_dict = {}
    '''可变参数,小说名称和对应目录页面url地址'''
    # 小说名称
    req_dict['novel_name'] = '清末的法师'
    # 目录页面地址,第1页
    req_dict['mulu_url'] = 'https://www.bq0.net/1bqg/898531870/'

    '''下面参数不用变,一个官网一个特定参数配置'''
    # 官网首页
    req_dict['novel_url'] = 'https://www.bq0.net/'
    # 章节目录定位
    req_dict['tar_dir_href'] = "div.box_con>dl>dd>a"
    # 章节标题定位
    req_dict['chap_title'] = 'div.bookname > h1'
    # 章节明细内容定位
    req_dict['chap_content'] = 'div#content'
    # 爬取小说目录
    # spider_novel_mulu(req_dict)
    # 爬取小说内容
    spider_novel_content(req_dict)

第四步:运行测试效果

 -------------------------------------------end---------------------------------------

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

9.21号作业

实现把注册的信息导入数据库中 second.h #ifndef SECOND_H #define SECOND_H#include <QWidget> #include <QDebug> namespace Ui { class Second; }class Second : public QWidget {Q_OBJECT public:void newslot(); public:explicit Second(QWidget *parent n…

机器学习西瓜书+南瓜书吃瓜教程学习笔记第四章决策树

1、算法原理 从逻辑角度&#xff0c;一堆if else语句的组合 从集合角度&#xff0c;根据某种准则划分特征空间 最终目的&#xff1a;将样本越分越“纯” 决策树是基于树结构来进行决策的 例如&#xff0c;我们对“这是好瓜吗&#xff1f;”这样的问题进行决策时&#xff0c;通…

QTC++ day12

注册登录界面 widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QIcon> #include <QPushButton> #include <QLineEdit> #include <QLabel> #include <QDebug> #include <QMessageBox>//消息对话框类 #inc…

tracert 路由追踪原理

一、概念 就是利用ICMP(Internet Control Message Protocol)Internet控制报文协议 来追踪的计算机到目标计算机之间的所有路由器信息 二、不同平台下命令方式 windows下&#xff1a; tracert www.baidu.comlinux下 traceroute www.baidu.com 三、基于方式 UDP(user datagram pr…

【建议收藏】5个神级AI提示词网站,让AI工具效率拉满

HI&#xff0c;同学们&#xff0c;我是赤辰&#xff0c;本期是赤辰第9篇AI工具类教程&#xff0c;文章底部准备了粉丝福利&#xff0c;看完可以领取&#xff01; 今天给大家推荐五款非常好玩的AI提示词资源网站&#xff0c;完全免费&#xff0c;无需魔法&#xff0c;直接复制粘…

DIY 一个汽车方向盘游戏外设(MMOS OSW DIY)

OSW-MMOS直驱方向盘DIY过程记录 - 简书 (jianshu.com) DIY 一个汽车方向盘游戏外设&#xff08;MMOS OSW DIY&#xff09; 首先讲一下这个直驱系统大概的框架&#xff0c;首先是电脑&#xff0c;电脑里装MMOS的软件(这个软件国内高手把它汉化了的)&#xff0c;电脑通过USB线&a…

行业首款数字牙刷 F10 系列:笑容加带来的智能刷牙革命

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

为什么tomcat要自定义线程池实现?

背景 最近在研究tomcat调优的问题&#xff0c;开发人员做过的最多的tomcat调优想必就是线程池调优了&#xff0c;但是tomcat并没有使用jdk自己的线程池实现&#xff0c;而是自定了了线程池&#xff0c;自己实现了ThreadPoolExecutor类位于org.apache.tomcat.util.threads包下 …

数据湖存储在大模型中的应用

9月5日&#xff0c;浪潮信息新产品“互联网AIGC”行业巡展在深圳举行。本次巡展以“智算 开新局创新机”为主题&#xff0c;腾讯云存储受邀分享数据湖存储在大模型中的应用&#xff0c;并在展区对腾讯云存储解决方案进行了全面的展示&#xff0c;引来众多参会者围观。 ChatGPT…

A股20年数据回测结果mysql数据查询 phpadmin

编辑 数据库登录使用 1.登录mysql数据库管理台 phpadmin 访问地址&#xff1a; http://121.43.55.160:888/phpmyadmin_c77c64465f15a891/index.php 用户名&#xff1a;root 密码&#xff1a; root 2.切换到阿里云服务器 3 数据库密码 用户名&#xff1a;readonly 密码&am…

堆的OJ题

&#x1f525;&#x1f525; 欢迎来到小林的博客&#xff01;&#xff01;       &#x1f6f0;️博客主页&#xff1a;✈️林 子       &#x1f6f0;️博客专栏&#xff1a;✈️ 小林的算法笔记       &#x1f6f0;️社区 :✈️ 进步学堂       &am…

【数据结构】二叉树的·深度优先遍历(前中后序遍历)and·广度优先(层序遍历)

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

2023最新Nmap安装和使用详解,超详细教程

【点击文章末尾卡片&#xff0c;先领资料再阅读哦~】&#x1f447; 文章目录 【点击文章末尾卡片&#xff0c;先领资料再阅读哦~】&#x1f447; Nmap概述功能概述运行方式 Nmap安装Nmap参数详解目标说明主机发现端口扫描端口说明和扫描顺序服务与版本探测脚本扫描操作系统探测…

C++输入流和输出流介绍

C 又可以称为“带类的 C”&#xff0c;即可以理解为 C 是 C 语言的基础上增加了面向对象&#xff08;类和对象&#xff09;。在此基础上&#xff0c;学过 C 语言的读者应该知道&#xff0c;它有一整套完成数据读写&#xff08;I/O&#xff09;的解决方案&#xff1a; 使用 scan…

DAZ To UMA⭐二.设置DAZ导出的变形内容 和 获取模型纹理贴图位置

文章目录 🟩 设置DAZ导出的内容1️⃣ 找到要导出的参数名称2️⃣ 打开导出面板3️⃣ 设置导出规则举例 : 导出身体Assets🟦 获取模型纹理贴图🟩 设置DAZ导出的内容 设置参数有两个目的: DAZ可以进行模型的参数调整,例如胖瘦等等,那如何将这些调整后的数值一起导出到FBX中…

Window通过VMWare搭建Linux集群后,将虚拟机暴露到宿主机局域网中,实现个人服务器搭建

目录 一、目的二、实现的方法 2.1 保证VMnet8为NAT模式2.2 获取虚拟机IP后&#xff0c;与宿主机进行端口绑定2.3 查看宿主机端口配置是否生效2.4 宿主机的端口准入规则设置2.5 效果 一、目的 解决宿主机通过VMware WorkStation安装了虚拟机后&#xff0c;宿主机所在的局域网…

A股风格因子看板 (2023.09 第07期)

该因子看板跟踪A股风格因子&#xff0c;该因子主要解释沪深两市的市场收益、刻画市场风格趋势的系列风格因子&#xff0c;用以分析市场风格切换、组合风格景露等。 今日为该因子跟踪第7期&#xff0c;指数组合数据截止日2023-08-31&#xff0c;要点如下 近1年A股风格因子收益走…

开源日报 0821:帮你修复老旧照片

这篇文章总结了几个开源项目的特点和优势。其中包括了 Python 资源列表、金融研究工具、动画精灵程序、游戏和旧照片修复项目等。这些项目提供了丰富的功能和技术支持&#xff0c;用户可以根据自己的需求进行定制和改进。总的来说&#xff0c;这些开源项目为开发者和用户提供了…

在如今内卷严重的背景下,程序员该如何提高自己的收入,享受美好人生?

无论是当今程序员行业&#xff0c;还是各行各业&#xff0c;内卷都非常严重&#xff0c;好多人都处于了一种卷又卷不过&#xff0c;躺又躺不平的境地&#xff0c;十分的难受&#xff0c;那么作为一个程序员而言&#xff0c;该如何在这样的境地下提高自己的收入&#xff0c;享受…

动力节点老杜JavaWeb笔记(全)

Servlet 关于系统架构 系统架构包括什么形式&#xff1f; C/S架构B/S架构 C/S架构&#xff1f; Client / Server&#xff08;客户端 / 服务器&#xff09;C/S架构的软件或者说系统有哪些呢&#xff1f; QQ&#xff08;先去腾讯官网下载一个QQ软件&#xff0c;几十MB&#xff…