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---------------------------------------
分类:
Python
标签:
selenium自动化
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效