• selenium自动化测试+OCR-获取图片页面小说


    随着爬虫技术的发展,反爬虫技术也越来越高。

    目前有些网站通过自定义字体库的方式实现反爬,主要表现在页面数据显示正常,但是页面获取到的实际数据是别的字符或者是一个编码。
    这种反爬需要解析网站自己的字体库,对加密字符使用字体库对应字符替换。需要制作字体和基本字体间映射关系。
    还有些网站通过图片加载内容的方式实现反爬,想要获取网页内容,可以结合使用OCR技术获取图片文字内容。

    第一步:先获取网页内容截图

    结合之前《selenium自动化测试-获取动态页面小说》相关的文章代码,改造下,封装成一个新方法,只获取小说网页内容截图,按章节ID分目录保存每页截图文件。

    依旧采用拆分步骤细化功能模块封装方法编写代码,便于后续扩展功能模块,代码中缺少的封装方法代码,详情参考之前的《selenium自动化测试》文章。

    1. def spider_novel_content_save_image(req_dict):
    2. '''
    3. @方法名称: 爬取小说章节明细内容,保存内容截图文件
    4. @中文注释: 爬取小说章节明细内容,保存内容截图文件
    5. @入参:
    6. @param req_dict dict 请求容器
    7. @出参:
    8. @返回状态:
    9. @return 0 失败或异常
    10. @return 1 成功
    11. @返回错误码
    12. @返回错误信息
    13. @param rsp_dict dict 响应容器
    14. @作 者: PandaCode辉
    15. @weixin公众号: PandaCode辉
    16. @创建时间: 2023-09-26
    17. @使用范例: spider_novel_content_save_image(req_dict)
    18. '''
    19. try:
    20. if (not type(req_dict) is dict):
    21. return [0, "111111", "请求容器参数类型错误,不为字典", [None]]
    22. # 章节目录文件名
    23. json_name = req_dict['novel_name'] + '.json'
    24. # 检查文件是否存在
    25. if os.path.isfile(json_name):
    26. print('json文件存在,不用重新爬取小说目录.')
    27. else:
    28. print('json文件不存在')
    29. # 爬取小说目录
    30. spider_novel_mulu(req_dict)
    31. # 读取json文件
    32. with open(json_name, 'r') as f:
    33. data_str = f.read()
    34. # 转换为字典容器
    35. mulu_dict = json.loads(data_str)
    36. # 在列表中查找指定元素的下标,未完成标志下标
    37. flag_index = mulu_dict['flag'].index('0')
    38. print(flag_index)
    39. # 章节总数
    40. chap_len = len(mulu_dict['chap_url'])
    41. # 在列表中查找指定元素的下标
    42. print('章节总数:', chap_len)
    43. # 截图目录
    44. screenshot_dir = os.path.join(os.path.dirname(__file__), 'screenshot')
    45. if not os.path.exists(screenshot_dir):
    46. os.makedirs(screenshot_dir)
    47. print('打开浏览器驱动')
    48. open_driver()
    49. # 循环读取章节
    50. for chap_id in range(flag_index, chap_len):
    51. print('chap_id : ', chap_id)
    52. # 章节url
    53. chap_url = mulu_dict['chap_url'][chap_id]
    54. # 截图目录,根据章节ID分类保存
    55. chap_id_dir = os.path.join(screenshot_dir, str(chap_id))
    56. if not os.path.exists(chap_id_dir):
    57. os.makedirs(chap_id_dir)
    58. # 打开网址网页
    59. print('打开网址网页')
    60. driver.get(chap_url)
    61. # 等待6秒启动完成
    62. driver.implicitly_wait(6)
    63. print('随机休眠')
    64. # 随机休眠 暂停0-2秒的整数秒
    65. time.sleep(random.randint(0, 2))
    66. # 章节分页url列表初始化
    67. page_href_list = []
    68. # 根据url地址获取网页信息
    69. chap_rst = get_html_by_webdriver(chap_url)
    70. time.sleep(3)
    71. if chap_rst[0] != 1:
    72. # 跳出循环爬取
    73. break
    74. chap_html_str = chap_rst[3][0]
    75. # 使用BeautifulSoup解析网页数据
    76. chap_soup = BeautifulSoup(chap_html_str, "html.parser")
    77. # 章节内容分页数和分页url
    78. # 获取分页页码标签下的href元素取出
    79. page_href_list_tmp = chap_soup.select("div#PageSet > a")
    80. all_page_cnt = len(page_href_list_tmp)
    81. print("分页页码链接数量:" + str(all_page_cnt))
    82. # 去除最后/后面数字+.html
    83. tmp_chap_url = re.sub(r'(\d+\.html)', '', chap_url)
    84. for each in page_href_list_tmp:
    85. if len(each) > 0:
    86. chap_url = tmp_chap_url + str(each.get('href'))
    87. print("拼接小说章节分页url链接:" + chap_url)
    88. # 判断是否已经存在列表中
    89. if not chap_url in page_href_list:
    90. page_href_list.append(chap_url)
    91. print("分页url链接列表:" + str(page_href_list))
    92. # 网页长宽最大化,保证截图是完整的,不会出现滚动条
    93. S = lambda X: driver.execute_script('return document.body.parentNode.scroll' + X)
    94. driver.set_window_size(S('Width'), S('Height'))
    95. # 章节页码,首页
    96. page_num = 1
    97. # 章节内容截图
    98. image_file = os.path.join(chap_id_dir, str(page_num) + '.png')
    99. # 元素定位
    100. chap_content_element = driver.find_element(By.ID, 'content')
    101. print(chap_content_element)
    102. # 元素截图
    103. chap_content_element.screenshot(image_file)
    104. # 分页列表大于0
    105. if len(page_href_list) > 0:
    106. for chap_url_page in page_href_list:
    107. print("chap_url_page:" + chap_url_page)
    108. time.sleep(3)
    109. # 打开网址网页
    110. print('打开网址网页')
    111. driver.get(chap_url_page)
    112. # 等待6秒启动完成
    113. driver.implicitly_wait(6)
    114. print('随机休眠')
    115. # 随机休眠 暂停0-2秒的整数秒
    116. time.sleep(random.randint(0, 2))
    117. # 章节页码
    118. page_num += 1
    119. # 章节内容截图
    120. image_file = os.path.join(chap_id_dir, str(page_num) + '.png')
    121. # 元素定位
    122. chap_content_element = driver.find_element(By.ID, 'content')
    123. print(chap_content_element)
    124. # 元素截图
    125. chap_content_element.screenshot(image_file)
    126. # 爬取明细章节内容截图成功后,更新对应标志为-2-截图已完成
    127. mulu_dict['flag'][chap_id] = '2'
    128. print('关闭浏览器驱动')
    129. close_driver()
    130. # 转换为json字符串
    131. json_str = json.dumps(mulu_dict)
    132. # 再次写入json文件,保存更新处理完标志
    133. with open(json_name, 'w', encoding="utf-8") as json_file:
    134. json_file.write(json_str)
    135. print("再次写入json文件,保存更新处理完标志")
    136. # 返回容器
    137. return [1, '000000', '爬取小说内容截图成功', [None]]
    138. except Exception as e:
    139. print('关闭浏览器驱动')
    140. close_driver()
    141. # 转换为json字符串
    142. json_str = json.dumps(mulu_dict)
    143. # 再次写入json文件,保存更新处理完标志
    144. with open(json_name, 'w', encoding="utf-8") as json_file:
    145. json_file.write(json_str)
    146. print("再次写入json文件,保存更新处理完标志")
    147. print("爬取小说内容异常," + str(e))
    148. return [0, '999999', "爬取小说内容截图异常," + str(e), [None]]

    第二步:通过OCR接口识别截图

    结合之前《PaddleOCR学习笔记3-通用识别服务》和《selenium自动化测试-获取动态页面小说》相关的文章代码,改造下,封装成一个新方法,通过OCR接口识别小说网页内容截图,然后写入文件保存。

    依旧采用拆分步骤细化功能模块封装方法编写代码,便于后续扩展功能模块,代码中缺少的封装方法代码,详情参考之前的《selenium自动化测试》文章。

    1. # 模拟http请求
    2. def requests_http(file_path, file_name, url):
    3. full_file_path = os.path.join(file_path, file_name)
    4. # 请求参数,文件名
    5. req_data = {'upload_file': open(full_file_path, 'rb')}
    6. # 模拟http请求
    7. rsp_data = requests.post(url, files=req_data)
    8. # print(rsp_data.text)
    9. result_dict = json.loads(rsp_data.text)
    10. # print(result_dict)
    11. return result_dict
    12. def spider_novel_content_by_ocr(req_dict):
    13. '''
    14. @方法名称: 通过OCR接口获取小说章节明细内容文字
    15. @中文注释: 读取章节列表json文件,通过OCR接口获取小说章节明细内容文字,保存到文本文件
    16. @入参:
    17. @param req_dict dict 请求容器
    18. @出参:
    19. @返回状态:
    20. @return 0 失败或异常
    21. @return 1 成功
    22. @返回错误码
    23. @返回错误信息
    24. @param rsp_dict dict 响应容器
    25. @作 者: PandaCode辉
    26. @weixin公众号: PandaCode辉
    27. @创建时间: 2023-09-26
    28. @使用范例: spider_novel_content_by_ocr(req_dict)
    29. '''
    30. try:
    31. if (not type(req_dict) is dict):
    32. return [0, "111111", "请求容器参数类型错误,不为字典", [None]]
    33. # 章节目录文件名
    34. json_name = req_dict['novel_name'] + '.json'
    35. # 检查文件是否存在
    36. if os.path.isfile(json_name):
    37. print('json文件存在,不用重新爬取小说目录.')
    38. else:
    39. print('json文件不存在')
    40. # 爬取小说目录
    41. spider_novel_mulu(req_dict)
    42. # 读取json文件
    43. with open(json_name, 'r') as f:
    44. data_str = f.read()
    45. # 转换为字典容器
    46. mulu_dict = json.loads(data_str)
    47. """
    48. 关于open()的mode参数:
    49. 'r':读
    50. 'w':写
    51. 'a':追加
    52. 'r+' == r+w(可读可写,文件若不存在就报错(IOError))
    53. 'w+' == w+r(可读可写,文件若不存在就创建)
    54. 'a+' ==a+r(可追加可写,文件若不存在就创建)
    55. 对应的,如果是二进制文件,就都加一个b就好啦:
    56. 'rb'  'wb'  'ab'  'rb+'  'wb+'  'ab+'
    57. """
    58. file_name = req_dict['novel_name'] + '.txt'
    59. # 在列表中查找指定元素的下标,2-截图完成,标志下标
    60. flag_index = mulu_dict['flag'].index('2')
    61. print(flag_index)
    62. # 2-截图完成,标志下标为0,则为第一次爬取章节内容,否则已经写入部分,只能追加内容写入文件
    63. # 因为章节明细内容很多,防止爬取过程中间中断,重新爬取,不用重复再爬取之前成功写入的数据
    64. if flag_index == 0:
    65. # 打开文件,首次创建写入
    66. fo = open(file_name, "w+", encoding="utf-8")
    67. else:
    68. # 打开文件,再次追加写入
    69. fo = open(file_name, "a+", encoding="utf-8")
    70. # 章节总数
    71. chap_len = len(mulu_dict['chap_url'])
    72. # 在列表中查找指定元素的下标
    73. print('章节总数:', chap_len)
    74. # 截图目录
    75. screenshot_dir = os.path.join(os.path.dirname(__file__), 'screenshot')
    76. # 循环读取章节
    77. for chap_id in range(flag_index, chap_len):
    78. # 识别成功标志
    79. succ_flag = False
    80. # 章节标题
    81. chap_title = mulu_dict['chap_title'][chap_id]
    82. print('chap_id : ', chap_id)
    83. # # 写入文件,章节标题
    84. fo.write("\n" + chap_title + "\r\n")
    85. # 截图目录,根据章节ID分类保存
    86. chap_id_dir = os.path.join(screenshot_dir, str(chap_id))
    87. # 列出目录下的所有文件和文件夹
    88. file_list = os.listdir(chap_id_dir)
    89. # 章节目录下文件列表大于0
    90. if len(file_list) > 0:
    91. for file_name in file_list:
    92. print("file_name:" + file_name)
    93. time.sleep(3)
    94. url = "http://127.0.0.1:5000/upload/"
    95. # 模拟http请求
    96. result_dict = requests_http(chap_id_dir, file_name, url)
    97. print(result_dict)
    98. # 识别成功
    99. if result_dict['error_code'] == '000000':
    100. succ_flag = True
    101. result_list = result_dict['result']
    102. for data in result_list:
    103. print(data['text'])
    104. # 将识别结果,逐行写入文件,章节内容
    105. fo.write(data['text'] + "\n")
    106. else:
    107. succ_flag = False
    108. print('识别失败异常.')
    109. # 识别成功则更新
    110. if succ_flag:
    111. # 爬取明细章节内容成功后,更新对应标志为-1-已完成
    112. mulu_dict['flag'][chap_id] = '1'
    113. print('关闭浏览器驱动')
    114. close_driver()
    115. # 关闭文件
    116. fo.close()
    117. print("循环爬取明细章节内容,写入文件完成")
    118. # 转换为json字符串
    119. json_str = json.dumps(mulu_dict)
    120. # 再次写入json文件,保存更新处理完标志
    121. with open(json_name, 'w', encoding="utf-8") as json_file:
    122. json_file.write(json_str)
    123. print("再次写入json文件,保存更新处理完标志")
    124. # 返回容器
    125. return [1, '000000', '爬取小说内容成功', [None]]
    126. except Exception as e:
    127. print('关闭浏览器驱动')
    128. close_driver()
    129. # 关闭文件
    130. fo.close()
    131. # 转换为json字符串
    132. json_str = json.dumps(mulu_dict)
    133. # 再次写入json文件,保存更新处理完标志
    134. with open(json_name, 'w', encoding="utf-8") as json_file:
    135. json_file.write(json_str)
    136. print("再次写入json文件,保存更新处理完标志")
    137. print("爬取小说内容异常," + str(e))
    138. return [0, '999999', "爬取小说内容异常," + str(e), [None]]

    第三步:运行效果

    第四步:总结

    目前很多网站都有基本的反爬策略,常见就是验证码、JS参数加密这两种。
    爬虫本身会对网站增加一定的压力,所以也应该合理设定爬取速率,尽量避免对目标网站造成麻烦,影响网站正常使用,一定注意自己爬虫的姿势。

    敬畏法律,遵纪守法,从我做起。

     最后说明:上述文章仅供学习参考,请勿用于商业用途,感谢阅读。

  • 相关阅读:
    java计算机毕业设计基于ssm的少儿编程管理系统(源代码+数据库+Lw文档)
    Vmware通过VMware tools设置共享文件夹
    uniapp:启动图 .9png 制作教程
    Python爬虫教程:从入门到实战
    go 递归 数据多层级横向展开 插入数据库
    html_语义化标签
    Java基础——Java语言与面向对象
    一文了解 Java 中的构造器
    从零手写实现 nginx-25-directive map 条件判断指令
    spring boot自动装配及自动装配条件判断
  • 原文地址:https://blog.csdn.net/xionghui2007/article/details/133313572