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

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

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

第一步:先确定目标网址

先找到小说目录页面。

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

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

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

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

 

 

第三步:编写代码

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

requests_webdriver.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
# -*- 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---------------------------------------

 

posted @   PandaCode辉  阅读(129)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示