无界面Linux系统和Windows系统使用selenium爬取CNVD数据

因为CNVD官网采用了反爬机制,所以使用selenium能够更容易的爬取漏洞数据

1、在Windows中使用

注意根据chrome版本下载对应chromedriver

2、在无界面的Linux中使用

因为对于爬虫的识别,无头模式相对于有头模式特征多,所以尽量使用有头模式,在无界面Linux中使用有头模式需要安装Xvfb。对于cnvd,目前使用无头模式即可,可不需要安装xvfb。
yum install Xvfb -y
yum install xorg-x11-fonts* -y

有头模式不能添加add_argument('--headless'),如果使用无头模式则添加这一项。
安装Xvfb后可能会提示“DevToolsActivePort文件不存在”,那么添加options.add_argument('--no-sandbox')。

使用Xvfb启动:
xvfb-run python3 cnvd_download.py
3、对于各式各样的验证码最好的方法就是对接第三方打码平台,使用pytesseract需要pip安装pytesseract库,而且安装tesseract-ocr-w64-setup-v5.2.0.20220712.exe,并且别忘了添加环境变量。
使用ddddocr库识别库比较高,但只试了字母的简单验证码,其他验证码不清楚。

### selenium使用的版本为1.141.0
### Python版本为3.8.12
import os
import random
import sys
import time
from configparser import ConfigParser
from io import BytesIO
import ddddocr
from PIL import Image
# from pytesseract import pytesseract
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException

# 读取配置文件信息
cf = ConfigParser()
cf.read('config.ini')

sys_path = os.path.abspath(".")

def get_config_data(target):
    cnnvd_filed = {}
    for k, v in cf.items(target):
        cnnvd_filed[k] = v
    return cnnvd_filed


class DownloadCnvd(object):
    def __init__(self):
        # 从配置文件获取下载文件存放目录
        self.download_path = get_config_data('CNVDPATH')['download_path']
        # 从配置文件获取url
        self.url = get_config_data('CNVDURL')['url']
        # 登录CNVD官网的账号邮箱
        self.login_email = get_config_data('CNVDLOGIN')['email']
        # 登录CNVD官网的账号密码
        self.login_password = get_config_data('CNVDLOGIN')['password']
        options = webdriver.ChromeOptions()
        # 配置下载文件存放目录
        prefs = {'profile.default_content_settings.popups': 0,
                 'download.default_directory': os.path.join(sys_path, self.download_path)}
        options.add_experimental_option('prefs', prefs)
        # 修改windows.navigator.webdriver,防机器人识别机制,selenium自动登陆判别机制
        options.add_experimental_option('excludeSwitches', ['enable-automation'])
        # 禁止策略化
        options.add_argument('disable-infobars')
        # 当在Linux中使用xvfb进行有头操作时,此行配置解决DevToolsActivePort文件不存在的报错
        options.add_argument('--no-sandbox')
        #无头模式
        options.add_argument('--headless)
        options.add_argument('--ignore-certificate-errors')
        options.add_argument("--disable-web-security")
        # Linux中运行的配置,浏览器配置文件夹路径
        options.add_argument('--user-data-dir=/home/ubuntu/.config/google-chrome')
        # Windows中运行的配置,浏览器配置文件夹路径
        #options.add_argument(r'--user-data-dir=C:\Users\Leeeee\AppData\Local\Google\Chrome\User Data')
        # Linux中运行的配置, chromedriver的位置
        self.driver = webdriver.Chrome(executable_path='/usr/bin/chromedriver', chrome_options=options)
        #self.driver = webdriver.Chrome(executable_path='chromedriver.exe', chrome_options=options)
        self.driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
            "source": """
                          Object.defineProperty(navigator, 'webdriver', {
                            get: () => undefined
                          })
                        """
        })

    # 使用pytesseract库进行识别图片验证码,不过识别率感人 
    def get_code(self):
        os.remove(code_path)
        element = self.driver.find_element_by_id('codeSpan')  # 定位验证码图片
        # 获取验证码图片在网页中的位置
        left = int(element.location['x'])  # 获取图片左上角坐标x
        top = int(element.location['y'])  # 获取图片左上角y
        right = int(element.location['x'] + element.size['width'])  # 获取图片右下角x
        bottom = int(element.location['y'] + element.size['height'])  # 获取图片右下角y
        # 通过Image处理图像
        code_path = os.path.join(sys_path, self.download_path) + 'img' + '.png' 
        self.driver.save_screenshot(code_path)  # 截取当前窗口并保存图片
        im = Image.open(code_path)  # 打开图片
        im = im.crop((left, top, right, bottom))  # 截图验证码
        im.save(code_path)  # 保存验证码图片
        verityCode = Image.open(code_path)
        code = pytesseract.image_to_string(verityCode)
        return code

    # 在Linux中获取验证码图片并识别验证码,使用ddddocr库进行图片验证码识别
    def get_code2(self):
        element = self.driver.find_element_by_id('codeSpan')  # 定位验证码图片
        # 获取图片验证码的请求地址
        img_path0 = element.get_attribute('src')
        url = img_path0
        print(url)
        time.sleep(1)
        # 打开新的页面,url为图片验证码的请求地址,应该是分辨率的问题,在登陆页直接截图获得验证码图片的是白灰方格,不是正常验证码图片,所以。。。
        newTab = f'window.open("{url}");'
        self.driver.execute_script(newTab)
        self.driver.switch_to.window(self.driver.window_handles[-1])
        element2 = self.driver.find_element_by_xpath('/html/body/img')
        left = int(element2.location['x'])  # 获取图片左上角坐标x
        top = int(element2.location['y'])  # 获取图片左上角y
        right = int(element2.location['x'] + element2.size['width'])  # 获取图片右下角x
        bottom = int(element2.location['y'] + element2.size['height'])  # 获取图片右下角y
        code_image = self.driver.get_screenshot_as_png()
        code_png = Image.open(BytesIO(code_image))  # 打开图片
        code_png = code_png.crop((left, top, right, bottom))  # 截图验证码
        code_png.save('code_png.png')
        my_ocr = ddddocr.DdddOcr()
        with open('code_png.png', 'rb') as f:
            code_img_bytes = f.read()
        code = my_ocr.classification(code_img_bytes)
        # 切换标签页为登录页面
        self.driver.switch_to.window(self.driver.window_handles[0])
        print(code)
        return code

    def get_code1(self):
        element = self.driver.find_element_by_id('codeSpan')  # 定位验证码图片
        # 获取验证码图片在网页中的位置
        left = int(element.location['x'])  # 获取图片左上角坐标x
        top = int(element.location['y'])  # 获取图片左上角y
        right = int(element.location['x'] + element.size['width'])  # 获取图片右下角x
        bottom = int(element.location['y'] + element.size['height'])  # 获取图片右下角y
        # img = conn.get(img_url)
        # 通过Image处理图像
        code_path = os.path.join(sys_path, self.download_path)+ 'img' + '.png'  # 生成随机文件名
        # self.driver.save_screenshot(code_path)  # 截取当前窗口并保存图片
        # im = Image.open(code_path)  # 打开图片
        # im = im.crop((left, top, right, bottom))  # 截图验证码
        # im.save(code_path)  # 保存验证码图片
        code_image = self.driver.get_screenshot_as_png()
        code_png = Image.open(BytesIO(code_image))  # 将图片读入内存中
        code_png = code_png.crop((left, top, right, bottom))  # 截图验证码
        code_png.save(code_path)
        my_ocr = ddddocr.DdddOcr()
        with open(code_path, 'rb') as f:
            code_img_bytes = f.read()
        code = my_ocr.classification(code_img_bytes)
        os.remove(code_path)
        print(code)
        return code

    # 点击文件进行下载,一页下载十个文件
    def get_file(self, flag):
        file_count = 10
        if flag == 1:
            file_count = 1
        num = 1
        while num <= file_count:
            file = self.driver.find_element_by_xpath(
                '/html/body/div[4]/div[1]/div/div[2]/table/tbody/tr[{}]/td[1]/a'.format(num))
            file.click()
            num += 1
            time1 = random.random()
            time2 = random.randint(1, 3)
            time.sleep(time1 + time2)

    # 获取“下一页”按钮并点击进行翻页
    def get_next_page(self):
        try:
            page = self.driver.find_element_by_class_name('nextLink')
            page.click()
        except Exception as e:
            return False
        return True

    # 在登录页进行登录
    def login(self):
        # 莫名其妙有时根据ID获取不到邮箱填写框,出错时根据xpath再找一次
        try:
            email = self.driver.find_element_by_id('email')
        except NoSuchElementException as e:
            email = self.driver.find_element_by_class_name('ipt')
        # email = self.driver.find_element_by_xpath('/html/body/div[4]/div/form/div/p[1]/input')
        passwd = self.driver.find_element_by_id('password')
        code = self.driver.find_element_by_id('myCode')
        code_password = self.get_code1()
        email.clear()
        # 怕填写太快,添加延时
        for i in self.login_email:
            email.send_keys(i)
            time.sleep(random.random())
        for i in self.login_password:
            passwd.send_keys(i)
            time.sleep(random.random())
        for i in code_password:
            code.send_keys(i)
            time.sleep(random.random())
        time.sleep(5)
        # 找到登录按钮点击登录
        login = self.driver.find_element_by_xpath('/html/body/div[4]/div/form/div/p[5]/a/span')
        login.click()
        time.sleep(2)
        # 如果捕捉到“验证码错误”,则重新进行填写登录
        try:
            code_err = self.driver.find_element_by_id('myCode_error')
            #重新填写
            self.login()
        except:
            time.sleep(2)
            self.driver.get(self.url)
            return True

    # 如果flag为0则全量下载,如果flag为1则只下载最新的一个文件
    def run(self, flag=0):
        self.driver.get(self.url)
        if self.login():
            while True:
                self.get_file(flag=flag)
                time.sleep(2)
                if flag == 1:
                    break
                result = self.get_next_page()

                if not result:
                    break
            self.driver.close()
            self.driver.quit()



if __name__ == '__main__':
    if len(sys.argv) != 2:
        print('请添加参数 init:初始化规则库 update:更新规则库')
        sys.exit()
    get_cnvd_rule = DownloadCnvd()    
    if sys.argv[1] == 'init':
        get_cnvd_rule.run()
    elif sys.argv[1] == 'update':
        get_cnvd_rule.run(1)
    else:
        print('请添加参数 init:初始化规则库 update:更新规则库')
    #get_cnvd_rule = DownloadCnvd()
    #get_cnvd_rule.run()




posted @ 2022-09-28 15:09  kopok  阅读(707)  评论(1编辑  收藏  举报