一份爬虫文件的分析
最近实验室学长布置下来了任务,是有关将各种CVE的信息整合后形成一张攻击图的。下面的代码是最开始的步骤,目的是通过python爬虫将CVE的四个目标信息从两个网站上爬取下来,并存入mongodb数据库。
源代码
# Script to web scrape vulnerability information into mongodb
from bs4 import BeautifulSoup
from pymongo import MongoClient
from urllib.request import Request, urlopen
import csv
import sys
import re
client = MongoClient('mongodb://localhost:27017/')
db = client.project #project是一个自定义的数据库。
vulns = db.vulnerabilities #vulnerabilities是db中的一个数据表
vulns.drop() #删除数据表中的集合【删除之前,重新爬取最新的数据】
mapping1 = { 'High': 2, 'Low': 1, 'None': 0 } #数据结构——字典
mapping2 = { 'Admin': 2, 'User': 1, 'None': 0 }
filename=sys.argv[1] #在命令行中输入的第二个文件,第一个文件就是这个py文件本身
print("Current file:\t", sys.argv[1])
try:
with open(filename) as csv_file: #with语句打开文件不用手动关闭文件,提高了代码的优雅性
csv_reader = csv.reader(csv_file) #调用csv库中的reader方法得到一个遍历csv文件各行的读取器对象
for row in csv_reader: #利用for循环把csv文件每行的内容读取下来[每行返回的是一个列表]
CVE = row[0] #行列表的第一个,因为每行只有一个文件,所以返回的值就是那个CSV的名称
print(CVE)
url = "https://www.cvedetails.com/cve/" + CVE
req = Request(url, headers={ 'User-Agent': 'Mozilla/5.0' })
html_doc = urlopen(req).read()
soup = BeautifulSoup(html_doc, 'lxml') #BeautifulSoup使得按照标签、id、class来寻找内容成为可能
table = soup.find("table", { 'id': 'cvssscorestable', 'class': 'details' })
field_row = table.findAll("tr")[6]
field_value = field_row.find("span").string
gained_access = mapping2[field_value]
url = "https://nvd.nist.gov/vuln/detail/" + CVE
html_doc = urlopen(url).read()
soup = BeautifulSoup(html_doc, 'lxml')
required_priv_string = re.findall("vuln-cvssv3-pr\&\#39\;\>\;\s(.*?)\s\<.*",html_doc.decode()) #re是正则模块,引号内的内容就是正则语句,注意html_doc.decode()的内容和soup的内容略有不同
if len(required_priv_string) != 0:
field_value = required_priv_string[0]
else:
field_value = 'None'
required_priv = mapping1[field_value]
attack_vectors = re.findall("vuln-cvssv2-av\&\#39\;\>\;(.*?)\<.*",html_doc.decode())
attack_vector = attack_vectors[0]
# Add entry
document = {}
document['cveName'] = CVE
document['gained_access'] = gained_access
document['required_priv'] = required_priv
document['access_vector'] = attack_vector
#print(document)
vulns.insert_one(document)
print("Successfully imported CVE details")
except IOError:
print("File {} does not exist".format(filename))
exit()
pymongo模块
这个python模块是用来连接本地的mongodb的。
from pymongo import MongoClient
client = MongoClient('mongodb://localhost:27017/')
db = client.project
vulns = db.vulnerabilities
vulns.drop()
我们初始化一个MongoClient之后就可以用.
来选择数据库、数据库再.
一下就是数据表。数据表的各种操作也是通过.
;非常直观与易用。这里要注意的是,我们使用client.database
来选择一个数据库的时候,如果这个数据库不存在,我们这样调用后这个database不会立刻被创建,只有这个数据库中有表了,并且往表中插入集合后,这个数据库才会被创建。
sys模块
sys.argv属性会以列表方式返回我们在python3
命令后输入的文件名称。其中sys.argv[0]
自然就是我们运行的python文件名。
➜ ~ cat test.py
import sys
print(sys.argv)%
➜ ~ python3 test.py fuck
['test.py', 'fuck']
python with语句
with 语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源,比如文件使用后自动关闭/线程中锁的自动获取和释放等。
我们可以利用with语句来打开一个文件,这样我们就不用手动来关闭文件,提高了代码的优雅度。
with open(filename) as f:
dosomething
同时我们可以用as
来给这个文件句柄取一个好听的别名。
csv库
之前只是听说过CSV这个词,今天更加深入地了解了一下。
逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)。纯文本意味着该文件是一个字符序列,不含必须像二进制数字)那样被解读的数据。
这个爬虫文件用到了这样一个csv文件。
我们在之后的程序中会爬取这些CVE的信息,那python是如何处理CSV文件的呢?
这里利用到了csv这个库。
csv_reader = csv.reader(csv_file) #调用csv库中的reader方法得到一个遍历csv文件各行的读取器对象
for row in csv_reader: #利用for循环把csv文件每行的内容读取下来[每行返回的是一个列表]
CVE = row[0] #行列表的第一个,因为每行只有一个文件,所以返回的值就是那个CSV的名称
print(CVE)
urllib.request库中Request和urlopen
from urllib.request import Request, urlopen
url = "https://www.cvedetails.com/cve/CVE-2021-31760"
req = Request(url, headers={ 'User-Agent': 'Mozilla/5.0' })
html_doc = urlopen(req).read()
我们来看一看html_doc会返回怎么样的内容。
经过vscode的格式化之后,我们发现它和普通的html文件差不多,但是有着许多\r\n\t
等换行符。
我们可以通过输出html_doc.decode()
来消除掉这些换行符,代码和结果如下
from urllib.request import Request, urlopen
url = "https://www.cvedetails.com/cve/CVE-2021-31760"
req = Request(url, headers={ 'User-Agent': 'Mozilla/5.0' })
html_doc = urlopen(req).read()
with open('3.html', "w") as f:
f.write(str(html_doc.decode()))
bs4库中的BeautifulSoup
我们看到利用urllib.request里面的Request和urlopen已经爬到了网页的源代码。那我们为什么还需要BeautifulSoup这个工具呢?原因在于我们用它可以方便得利用标签、id、class来进行信息的检索。
soup = BeautifulSoup(html_doc, 'lxml') #选择lxml解析器
table = soup.find("table", { 'id': 'cvssscorestable', 'class': 'details' })#找到id为cvs...;class为details的table标签
field_row = table.findAll("tr")[6] #在这个标签中找到第七个tr标签
field_value = field_row.find("span").string #得到这个tr标签中的span标签的值
re正则库
required_priv_string = re.findall("vuln-cvssv3-pr\&\#39\;\>\;\s(.*?)\s\<.*",html_doc.decode())
利用re可以实现正则匹配。
format格式函数
print("File {} does not exist".format(filename))
实现了类似C中%s
的操作。python中用{}
来代替。
➜ ~ cat test.py
print('{} yyds!'.format('wuuconix'))%
➜ ~ python3 test.py
wuuconix yyds!