【2022.05.23】对无验证码的整个网页公告的内容进行自适应爬取(2)

学习内容

Selenium是一个浏览器自动化操作框架。可以模拟用户操作。这样我们就可以用selenium做很多事情了,测试自动化,爬虫等等

但是我要写的脚本要在很多台电脑上运行,所以我要使用一个通用的脚本来支持在win7/win10上都可以运行,我要找到同时支持两个系统的浏览器内核

因为我打算写个自适应所有网页的脚本,而不单单是只对一个网页做适配

(好像除了Selenium以外,没有可以适配所有电脑的方案

未完成难点

Selenium可以根据xpath获取元素的位置,但是却无法反向,根据其他条件获得的元素,无法得到其绝对的xpath路径(为了解决这个我头都大了

目前的想法是通过

driver.execute_script("return document.documentElement.innerHTML")

从中获取到整个html文件,再从html文件中获取绝对路径

前期准备

Chrome内核

这个是驱动下载页面CNPM Binaries Mirror (npmmirror.com)

这个是浏览器下载页面Google Chrome 64bit Windows版_chrome浏览器,chrome插件,谷歌浏览器下载,谈笑有鸿儒 (chromedownloads.net)

两者最好是在同一个开发环境

因此我选的是99.0.4844.51版本

浏览器chrome_win64_stable_99.0.4844.51离线安装包下载 (chromedownloads.net)

驱动CNPM Binaries Mirror (npmmirror.com)

Edge内核

Microsoft Edge WebDriver - Microsoft Edge Developer

代码

将驱动和代码文件放在同一个目录下

image-20220523102901967

这时候有两种调用驱动的方式

具体可以参照selenium 安装与 chromedriver安装 - Rogn - 博客园 (cnblogs.com)

因为我要在不同环境下使用该脚本,因此我采用不配置环境变量的

先试下范例,看看能不能成功打开

from selenium import webdriver
import time

def main():
    chrome_driver = r'.\chromedriver.exe'  #chromedriver的文件位置
    b = webdriver.Chrome(executable_path = chrome_driver)
    b.get('https://www.google.com')
    time.sleep(5)
    b.quit()

if __name__ == '__main__':
    main()

代码

import requests
from lxml import etree
from lxml import html
import json
import time
import string
from urllib.parse import urljoin
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# 全局变量
with open('config.json', 'r', encoding='utf-8') as f:
    JsonFile = json.load(f)
# 全局变量可修改
base_url = JsonFile['url']
notice_title_href_xpath = JsonFile['notice_title_href_xpath']
notice_title_xpath = JsonFile['notice_title_xpath']
notice_content_xpath = JsonFile['notice_content_xpath']
notice_pages = JsonFile['notice_pages']
search = JsonFile['search']
notice_content_xpath = notice_content_xpath.replace("替换", search)
# 全局变量不修改,调试用的
chrome_driver = r'.\chromedriver.exe'  # chromedriver的文件位置
next_page_keywords = JsonFile['next_page_keywords']
delay = JsonFile['delay']
notice_list_number = JsonFile['notice_list_number']

# 返回下一页的链接,因为是动态js加载的,所以我要使用selenium
def get_next_page_url(current_url, notice_pages):
    s = Service(executable_path=chrome_driver)
    driver = webdriver.Chrome(service=s)
    driver.get(current_url)
    time.sleep(3)
    result = []
    result.append(driver.current_url)
    i = 1
    print(driver.current_url)
    print("开始寻找所有的公告页面")
    while True:
        try:
            pre_url = driver.current_url
            driver.find_element(by=By.XPATH, value=next_page_keywords).click()
            if driver.current_url==pre_url:
                break
            i = i + 1
            result.append(driver.current_url)
            if i>=notice_pages:
                break
            time.sleep(delay)
        except:
            print("到达页数上限")
            break
    print("已找到所有页面,一共有", i, "页")
    time.sleep(3)
    driver.quit()
    print(result)
    return result

# 自适应寻找公告列表的xpath
def get_notice_page_title(base_url):
    s = Service(executable_path=chrome_driver)
    driver = webdriver.Chrome(service=s)
    driver.get(base_url)
    time.sleep(1)
    # print(driver.execute_script("return document.documentElement.innerHTML"))
    ul_list = driver.find_elements(By.XPATH, value="//ul")
    # 找出最长ul
    temp = 0

    for ul in ul_list:
        # 没有字数的列表块跳过不输出
        if len(ul.text)==0:
            continue
        print("————————————————————————————————————————————")
        print("【当前最大列表块】\n字数:", len(ul.text), "\n内容:\n", ul.text)
        print("————————————————————————————————————————————")
        if len(ul.text)>temp:
            temp = len(ul.text)
            print(temp)
            # li_list记录的是最长板块的列表集合
            li_list = ul.find_elements(By.XPATH, value="li")
            # 将列表中的第二个公告赋予xpath,防止置顶贴

        print("\n")
    print("最后得到的公告标题为:\n", li_list[notice_list_number].find_element(By.XPATH, value="a").text)
    return li_list[notice_list_number].find_element(By.XPATH, value="a").text
    driver.quit()

# 将标题去获取页面的标题xpath
def get_notice_page_xpath(title):
    page = requests.get(base_url)
    page.encoding = 'utf-8'
    # print(page.text)
    root = html.fromstring(page.text)
    tree = root.getroottree()
    temp_xpath = """//a[contains(text(),\"""" + title + """\")]"""
    result = root.xpath(temp_xpath)
    result_xpath = tree.getpath(result[0])
    print("返回的xpath是:\n", result_xpath)
    # 如果有多项才使用for循环
    # for r in result:
    #     print(tree.getpath(r))
    return result_xpath

# 将取得的单项xpath处理成多项,将最后的li后面中括号去掉
def handle_suffix_of_xpath(pre_xpath):
    # print(pre_xpath.rfind('[', beg=0, end=len(title_xpath)))
    print(str.rfind(pre_xpath, '[', 0, len(pre_xpath)))
    x = str.rfind(pre_xpath, '[', 0, len(pre_xpath))
    # print(pre_xpath)
    string_list = list(pre_xpath)
    # print(len(string_list))
    # print(string_list)
    del string_list[x:x+3]
    # print(len(string_list))
    # print(string_list)
    after_xpath = ''.join(string_list)
    # print(len(pre_xpath))
    # print(pre_xpath)
    # print(len(after_xpath))
    print("处理后的xpath为", after_xpath)
    return after_xpath

if __name__ == '__main__':
    current_url = base_url

    # 自适应获取当前后续的所有页面List
    notice_pages_list = get_next_page_url(current_url, notice_pages)
    # 自适应获取通知列表的标题
    title = get_notice_page_title(base_url)
    # 将上面函数获取的标题去获取页面的标题xpath
    title_xpath = get_notice_page_xpath(title)
    # 将取得的单项xpath处理成多项
    after_xpath = handle_suffix_of_xpath(title_xpath)
    # current_html = requests.get(current_url)
    # current_html.encoding = "utf-8"
    # selector = etree.HTML(current_html.text)
    # notice = selector.xpath(notice_title_href_xpath)
    # print(notice)
    # 获取当前页面的所有公告
    # for result in notice:
    #     # 获得网址
    #     result_url = urljoin(current_url, result)
    #     print("网址: ", result_url)
    #     result_html = requests.get(result_url)
    #     result_html.encoding = "utf-8"
    #     result_detail = etree.HTML(result_html.text)
    #     result_title = result_detail.xpath(notice_title_xpath)
    #     print("标题: ", result_title)
    #
    #     result_content = result_detail.xpath(notice_content_xpath)
    #     print("内容: ")
    #     for result_print in result_content:
    #         print(result_print)
    #     print("\n")
    #     time.sleep(delay)

参考链接

selenium 安装与 chromedriver安装 - Rogn - 博客园 (cnblogs.com)

不使用selenium插件如何抓取网页的动态加载数据 - 大数据 - 亿速云 (yisu.com)

python学习selenium笔记 | 个人知识清单 (luzhengxiang.com)

selenium grid 的使用,实现在另一台电脑运行自动化测试脚本_米尔大哥的博客-CSDN博客

Python使用selenium+chrome配置指南-YES开发框架网 (yesdotnet.com)

爬虫

5. 等待页面加载完成(Waits) — Selenium-Python中文文档 2 documentation (selenium-python-zh.readthedocs.io)

innerhtml与outerhtml的区别_高明懿大可爱的博客-CSDN博客_innerhtml和outerhtml的区别

如何在当前页面的selenium中获取html - 我爱学习网 (5axxw.com)

Xpath教程_待到寒蝉鸣泣的博客-CSDN博客_root.xpath

python3 替换字符串中指定位置字符_小Aer的博客-CSDN博客_python更改字符串特定位置

打包

解决利用pyinstaller打包selenium脚本时集成chromedriver.exe的问题_sinat_36797467的博客-程序员ITS401 - 程序员ITS401

Pyinstaller 打包selenium项目 生成包含chromedriver 的exe文件_风一样的男子&的博客-CSDN博客_pyinstaller selenium

基于python+selenium的每日健康上报的程序打包问题 - 哔哩哔哩 (bilibili.com)

Selenium 常用函数总结_从零开始的数据猿的博客-CSDN博客_selenium函数

posted @ 2022-05-23 21:53  Mokou  阅读(53)  评论(0编辑  收藏  举报