使用selenium + Chrome爬取某网站乌云公开漏洞文章并保存为pdf文件
目的:使用selenium + Chrome爬取某网站指定类型的乌云公开漏洞文章,即在win10终端输入漏洞类型(如未授权),则爬取所有该类型的漏洞文章,并把每个分页的数字作为文件夹名,来保存该分页下面的所有的漏洞文章。
总结:本例只是能简单的爬取某一类型漏洞的所有文章,但不能爬取多个类型漏洞的所有文章,有时可能会有一些小bug导致没爬取完就崩溃,需要手工修改进而重新爬。其它问题解决看注释。
- 关于python代码里面出现中文在windows里面的处理,还没完全掌握。可参考Python for Windows 中文编码问题汇总
- 遇到的超时问题TimeoutException: Message: timeout 还没解决
- 本例代码只是输入漏洞的指定类型,才可以爬取其所有文章。但如想一块爬取漏洞的类型如 未授权、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)