使用selenium + Chrome爬取某网站乌云公开漏洞文章并保存为pdf文件

目的:使用selenium + Chrome爬取某网站指定类型的乌云公开漏洞文章,即在win10终端输入漏洞类型(如未授权),则爬取所有该类型的漏洞文章,并把每个分页的数字作为文件夹名,来保存该分页下面的所有的漏洞文章。

总结:本例只是能简单的爬取某一类型漏洞的所有文章,但不能爬取多个类型漏洞的所有文章,有时可能会有一些小bug导致没爬取完就崩溃,需要手工修改进而重新爬。其它问题解决看注释。

  1. 关于python代码里面出现中文在windows里面的处理,还没完全掌握。可参考Python for Windows 中文编码问题汇总
  2. 遇到的超时问题TimeoutException: Message: timeout 还没解决
  3. 本例代码只是输入漏洞的指定类型,才可以爬取其所有文章。但如想一块爬取漏洞的类型如 未授权、sql等,导入paramunittest也没搞定
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import unittest
import time
from lxml import etree
import urllib2
import pdfkit
import random
import os
import shutil
import urllib
# import chardet
import paramunittest

# @paramunittest.parametrized(
#     {'user':'编辑器漏洞','result':'true'},
    # {'user':'未授权','result':'true'},
# )

class wooyunSelenium(unittest.TestCase):
# class TestDemo(unittest.TestCase):
    '''
    利用unittest模块的测试类来解决js分页,模拟点击下一页。
    
    '''
    
    def setUp(self):
    # def setParameters(self, user, result):
        ''' 
        初始化方法(固定写法),为测试准备环境。

        '''
        # self.user = user
        # self.result = result

        # 创建谷歌浏览器对象。
        self.driver = webdriver.Chrome()

        # 创建无头浏览器对象,实测速度比谷歌浏览器的要快点,但不是很多,没有浏览器窗口界面。
        # self.driver = webdriver.PhantomJS()

        # input输入的提示语如果是字符串,要加双引号,如果是数字,直接输入(python2),如果是raw_input函数,则不管输入的是什么,全部返回的类型为字符串类型。
        # vname = input('请输入要查询的漏洞类型:')  "编辑器漏洞"

        # raw_input输入的字符不用加双引号,始终返回字符串类型
        self.vname = raw_input('请输入要查询的漏洞类型:')

        # 输出类型为str类型,检测该字符串的编码格式
        # print chardet.detect(vname)

        # 对字符串中文进行url编码使用urllib.quote函数,对字典里面的中文进行url编码使用urlencode函数
        # print type(urllib.quote('编辑器漏洞'))
        # print urllib.quote('编辑器漏洞')

        # 实测发现windows系统的代码里面出现中文,默认编码格式为gbk,要进行解码处理,才不会出现中文乱码。
        # print self.vname
        os.mkdir(self.vname.decode('gbk'))
        os.chdir(self.vname.decode('gbk'))

        # 该处发现先解码后编码为utf-8,才可以解决下面成功获取响应内容。
        self.vname = urllib.quote(self.vname.decode('gbk').encode('utf-8'))

        self.page = 1

        # 对self.vname进行url编码,由于是字符串,所以采用urllib.quote()函数来编码字符串成为url编码格式,如果为字典,则使用urlencode函数来处理。
        # urllib.uquote(self.vname)
        
       
        # 请求网页,如果该行代码放在testMooc方法,会导致抓取的数据有重复,而且漏抓。注意vname要转换为urlcode编码格式,否则会报错 UnicodeDecodeError: 'utf8' codec can't decode byte 0xb1 in position 41: invalid start byte
        self.driver.get("http://wooyun.jozxing.cc/search?keywords=" + self.vname + "&&content_search_by=by_bugs&&search_by_html=False&&page=" + str(self.page))
        # self.driver.get('http://wooyun.jozxing.cc/search?keywords=%E7%BC%96%E8%BE%91%E5%99%A8%E6%BC%8F%E6%B4%9E&content_search_by=by_bugs')

    def testWooyun(self):
    # def testcase(self):
        '''
        具体的测试用例方法(方法名开头固定以test开头)

        '''

        # 加上u用来处理中文乱码,但发现不能使用PhantomJS,老是爆错,而谷歌浏览器则不会。
        # os.mkdir(u'代码执行')
        # os.chdir(u'命令执行')

        # 外循环控制页数,因为经过观察,某类型漏洞文章的最多分页是没有达到1000的,所以用该数以保证爬取到该类型漏洞的所有分页
        for i in range(self.page,1000):

            # 创建一个文件夹来存放该页所有文章,文件夹名字为分页数字
            os.mkdir(str(i))

            # 让网页完全加载,避免网页没加载完导致获取的数据丢失
            time.sleep(3)
            # 获取网页源码
            html = self.driver.page_source
            # 把源码解析为html dom文档
            content = etree.HTML(html)
            # 使用xpath去匹配所有的课程链接

            links = content.xpath('//td[2]/a/@href')

            n = 0
            # 遍历该分页里面的文章链接
            for each in links:
                each = 'http://wooyun.jozxing.cc/' + each
                print each
                try:
                    # self.driver2 = webdriver.Chrome()报错,发现页面空白。至于是漏洞文章页面还是分页页面,还没确定,
                    # 处代码表明是漏洞文章页面请求为空。大约请求第234个,会出现一个空白页面,一直等待好像死机一样。解决方法把谷歌浏览器驱动程序放入python安装目录下的script目录
                    self.driver2 = webdriver.Chrome()
                    # self.driver2 = webdriver.PhantomJS()
                    # 加上下面2行代码,即设置超时时间,实测发现有效去除 不能读取vr下面的某个文件 报错。Unable to read VR Path Registry from C:\Users\hp\AppData\Local\openvr\openvrpaths.vrpath
                    self.driver2.set_page_load_timeout(10)
                    self.driver2.set_script_timeout(10)
                    # self.driver2.implicitly_wait(10)
                    self.driver2.get(each)
                    html2 = self.driver2.page_source
                    content2 = etree.HTML(html2)
                    # 获取文章章标题
                    # title = content2.xpath("//h3[@class='wybug_title']/text()")[0]
                    # 处理获取不到页面时的异常(页面空白),再请求访问一次(可设置多次甚至循环),获取不到页面的原因可能是网站的反爬机制造成的。
                    title = content2.xpath("//h3[@class='wybug_title']/text()")[0]
                except:
                    # 关闭当前打开漏洞文章的浏览器窗口,发现使用close有时没作用,则使用quit直接退出当前漏洞文章浏览器窗口
                    self.driver2.quit()
                    # 重新获取该漏洞页面
                    self.driver2 = webdriver.Chrome()
                    # self.driver2.implicitly_wait(10)
                    self.driver2.set_page_load_timeout(10)
                    self.driver2.set_script_timeout(10)
                    # self.driver2 = webdriver.PhantomJS()

                    # 怎么老是报超时错误:TimeoutException: Message: timeout
                    self.driver2.get(each)
                    html2 = self.driver2.page_source
                    content2 = etree.HTML(html2)
                    title = content2.xpath("//h3[@class='wybug_title']/text()")[0]

                # 设置保存的文件名,由于windows环境对文件名命名有'/'、'\'、'?'、'|'、'<'、'>'、'"'、'*'有限制,所以要有如下过滤
                filename = title[6:].strip().replace('"','').replace('/','_').replace('\\','_').replace('<','').replace('>','').replace('(','').replace(')','').replace('[','').replace(']','').replace('\\','').replace('%','').replace(';','').replace('*','').replace('?','').replace(':','').replace('|','').replace(u'®','').replace('.','').replace('&','')
                # file = filename + ".pdf"
                n += 1
                # 初始文件名
                file1 = str(n) + '.pdf'
                # 保存文件名
                file2 = filename + '.pdf'
                try:
                    path_wk = r'C:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe'
                    config = pdfkit.configuration(wkhtmltopdf = path_wk)
                    pdfkit.from_url(each, file1, configuration=config)
                except:
                    path_wk = r'C:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe'
                    config = pdfkit.configuration(wkhtmltopdf = path_wk)
                    pdfkit.from_url(each, file1, configuration=config)
                
                
                self.driver2.quit()
                # m变量值用来区分同名文件
                m = 1
                # 由于文件名有重复,所以使用递归函数来处理,文件重名的,文件名末尾加递增数字保存。如a.pdf,a2.pdf
                self.modify_filename(file1,file2,filename,m)
                # time.sleep(random.randint(1,3))
            # 把当前分页里面的所有的文章文件移动到对应的分页文件里面
            for d in os.listdir('.'):
                if d.split('.')[-1] == 'pdf':
                    shutil.move(d,str(i))
            

            # 退出循环条件,从网页源码里面没找到某个类名,则返回为-1,进而作为当点击下一页时,如果没法点击时,则此时的返回值不为-1
            if (html.find('next disabled')) != -1:
                break
            
            # 模拟浏览器手动点击下一页。  phantomjs好像不支持翻页,但有时候测试发现也支持。搞不懂。
            self.driver.find_element_by_xpath('//li[@class="next"]/a').click()
            # self.assertTrue(self.result == "true")

    def modify_filename(self,file1,file2,filename,m):
       '''
       更改文件名函数
       如有多个同名文件,自动在文件名末尾加上数字,从2开始。
       方法递归

       '''

       if os.path.exists(file2):
            m += 1
            file2 = filename + str(m) + '.pdf'
            self.modify_filename(file1,file2,filename,m)
       else:
            os.rename(file1,file2)
            return


    def tearDown(self):
        ''' 退出方法(固定写法),清理测试环境,以备下次测试使用'''
        
        self.driver.quit()

if __name__ == '__main__':
    unittest.main()
    # unittest.main(verbosity=2)


posted @ 2018-08-12 15:50  silence-cc  阅读(2435)  评论(0编辑  收藏  举报