这个是我在一家网络安全公司面试时的操作题,回来后经过多次修改后才得到一个比较完整的程序。
整个模块可以分成两个大部分。一个是数据库的操作,一个是信息的获取(类似爬虫?)
信息的获取分为四个小操作,ip的获取、端口的获取、服务器类型的获取和标题的获取。
IP的获取:
ip的获取代码:
def getIp(self,url):
try:
str_ip = str(socket.gethostbyname(url[7:])) #通过socket方式以域名获取IP
self.print_msg(0, str_ip, "")
self.ip = str_ip
if str_ip:
self.correct += 1
except socket.error, e:
print u"ip获取失败"
端口的获取其实更简单:
def getPort(self): #通过socket返回端口号
try:
str_port = str(socket.getservbyname('http', 'tcp'))
self.print_msg(1, str_port, "")
self.port = str_port
if str_port:
self.correct += 1
except socket.error,e:
print u"端口号获取失败"
服务器类型的获取:
一开始我的想法是通过正则去匹配应答头,但是过了两天发现其实有更简单的方法,直接用urllib2模块的方法去访问,回传的数据中的header中直接找到"Server"这个键对应的键值就是服务器类型了。
def getServerType(self): #在header字典里寻找Server对应的键值
self.server = str(self.header.get("Server"))
self.print_msg(2,self.server, "")
if self.server:
self.correct +=1
所以我的想法是,先在回传数据的header里面找到‘content-type’(正常网页的编码方式都会在这里声明)。
然后获得这个字符串还不是编码方式,还要通过正则匹配匹配 charset:() 括号里面的才是真正的编码方式。
接着使用这个编码方式去对获取到的html文本解码,再编码成需要的类型(我的pycharm用utf-8)。
再就是对获取到的文体再正则匹配,这次匹配的是title标签里面的内容。
最后输出第一个匹配到的内容,输出的时候还要注意不同编码形式的字符串不能相连。
def getTitle(self): #通过正则匹配标签内的字符
data1 = self.header.get('content-type')
pattern = re.compile(u"charset=(.*)")
ress = pattern.findall(data1)
if not ress: #如果在header列表找不到该网页的编码形式,则在response里匹配
pattern = re.compile(r"charset=(.*)")
ress = pattern.findall(self.response)
if ress:
temp = self.response.decode(ress[0],'ignore').encode("utf-8")
xx = u"(.*) "
pattern = re.compile(xx)
results = pattern.findall(temp)
if results:
self.print_msg(3, "", results[0])
self.title = results[0]
if results[0]:
self.correct += 1
else:
print u"找不到标题"
这四个内容都获取到了,就存到mysql数据库里面。
总的代码如下:
这是信息获取部分
# *_*coding:utf-8 *_*
import Queue
import gevent
import urllib2
import socket
import sys
import re
import threading
import DbSave
class method1:
def __init__(self,strurl):
self.domain = ""
self.ip = ""
self.port = 0
self.server = ""
self.title = ""
self.correct = 0
self.problem_url = []
self.queue = Queue.Queue()
self.typeencode = sys.getfilesystemencoding()
if strurl:
for self.strurl in strurl:
if self.check_domain(self.strurl):
self.queue.put("http://"+self.strurl)
else:
print u"域名输入格式不正确"
exit(0)
else:
print u"没有输入域名"
exit(0)
if not self.queue.empty():
threads = [gevent.spawn(self.declare, i) for i in range(5)]
try:
gevent.joinall(threads)
except KeyboardInterrupt, e:
msg = '[WARNING] User aborted.'
print self.problem_url
def declare(self,i):
while not self.queue.empty():
url = self.queue.get()
try:
self.domain = url
print u"域名:"+url
self.urlget(url)
self.getIp(url)
self.getPort()
self.getServerType()
self.getTitle()
print "-------------------------"
DbSave.DbInsert(self.domain, self.ip,int(self.port), self.server, self.title)
self.domain = ""
self.ip = ""
self.port = 0
self.server = ""
self.title = ""
self.correct = 0
except:
print "数据库写入错误"
if self.correct != 4:
self.problem_url.append(url)
self.correct = 0
def urlget(self,url):
try:
res = urllib2.urlopen(url) #urllib2的get方法访问url
self.response = res.read() #获取正文
self.header = res.headers #获取应答头
except urllib2.URLError,e:
print u"urllib2访问失败,退出.."
exit(0)
def getIp(self,url):
try:
str_ip = str(socket.gethostbyname(url[7:])) #通过socket方式以域名获取IP
self.print_msg(0, str_ip, "")
self.ip = str_ip
if str_ip:
self.correct += 1
except socket.error, e:
print u"ip获取失败"
def getPort(self): #通过socket返回端口号
try:
str_port = str(socket.getservbyname('http', 'tcp'))
self.print_msg(1, str_port, "")
self.port = str_port
if str_port:
self.correct += 1
except socket.error,e:
print u"端口号获取失败"
def getServerType(self): #在header字典里寻找Server对应的键值
self.server = str(self.header.get("Server"))
self.print_msg(2,self.server, "")
if self.server:
self.correct +=1
def getTitle(self): #通过正则匹配标签内的字符
data1 = self.header.get('content-type')
pattern = re.compile(u"charset=(.*)")
ress = pattern.findall(data1)
if not ress: #如果在header列表找不到该网页的编码形式,则在response里匹配
pattern = re.compile(r"charset=(.*)")
ress = pattern.findall(self.response)
if ress:
temp = self.response.decode(ress[0],'ignore').encode("utf-8")
xx = u"(.*) "
pattern = re.compile(xx)
results = pattern.findall(temp)
if results:
self.print_msg(3, "", results[0])
self.title = results[0]
if results[0]:
self.correct += 1
else:
print u"找不到标题"
@staticmethod
def print_msg(signs, meg, title):
switcher = {0: u"ip地址:",
1: u"端口号:",
2: u"服务器信息:",
3: u"标题:",
4: u"协程开启错误"}
if meg:
print switcher.get(signs)+meg
else:
print switcher.get(signs).encode("utf-8")+title
@staticmethod
def check_domain(domain):
pattern = re.compile(r'(?i)^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$')
return pattern.match(domain)
if __name__ == '__main__':
L = ["www.51.com", "www.baidu.com", "www.1688.com", "www.taobao.com", "www.hao123.com",
"www.sohu.com", "www.youku.com", "www.taobao.com", "www.ifeng.com", "www.jd.com",
"www.4399.com", "www.126.com", "www.scnu.edu.cn", "www.163yun.com", "www.58.com",
"www.37.com", "www.tmall.com", "www.7k7k.com", "www.youxia.com", "sports.cctv.com",
"www.163.com", "v.qq.com", "www.bilibili.com", "www.hupu.com", "www.qidian.com",
"www.zol.com", "mail.qq.com", "www.51job.com", "www.liepin.com", "www.10086.cn",
"www.189.cn", "www.12306.com", "www.10010.com", "www.zol.com", "www.stockstar.com",
]
method1(L)
这是数据库部分:
# coding=utf-8
import MySQLdb
def Dbcreate():
try:
conn = MySQLdb.connect(host='localhost', user='root', passwd='', db='pydatabase', port=3306,charset='utf8')
cur = conn.cursor()
urlmanager = """CREATE TABLE URLMESSAGE(
DOMAIN CHAR(35) NOT NULL,
IP CHAR(15),
PORT INT,
SERVER VARCHAR(20),
TITLE TEXT
CHARACTER SET utf8 COLLATE utf8_general_ci
)
"""
cur.execute(urlmanager)
cur.close()
conn.close()
except MySQLdb.Error, e:
print "Mysql Error %d: %s" % (e.args[0], e.args[1])
def DbInsert(domain, ip, port, server, title):
try:
conn = MySQLdb.connect(host='localhost', user='root', passwd='', db='pydatabase', port=3306,charset="utf8")
cur = conn.cursor()
cur.execute("INSERT INTO urlmessage(DOMAIN,IP,PORT,SERVER,TITLE) VALUES('%s','%s','%d','%s','%s')"%(domain,ip,port,server,title))
cur.close()
conn.commit()
conn.close()
except MySQLdb.Error, e:
print "Mysql Error %d: %s" % (e.args[0], e.args[1])
尚未解决的问题:
1:在有些网页得到访问时间过长,会造成阻塞。
2:某些特殊的网页获取不到编码方式(只有www.qq.com出现这个问题)...
3:数据库的操作过于粗暴,正常来说不应该这么简单,导致一出现问题数据库就不工作了..
3:有些网页的标签无法获取。
解决方法:
1:应该使用多进程+协程的方法,我只是使用了协程(因为面试有道题目就是用协程,我只是想尝试一下..),通过多进程去设置访问超时时间。
2:这个问题尚未想到有什么解决的方案...因为只有这个域名出问题..
3:由于学数据库的时间太短,所以只会很简单的操作。这里我的想法是在信息获取和数据库操作之间加个中间层,设置一个缓存区,这样才可以一次打开数据库进行所有操作,而不是获取一个网页信息就操作一次。还有,数据库操作应该要很谨慎地处理数据,要有很好的异常处理机制(不是像我这样这么粗暴...),注意事务的提交和回滚。
4:这个问题的原因应该是正则匹配的不正确,因为不同网页HTML文本差的有点远...,暂时还没想到有什么方法..
还可以提升的部分:
在获取标题的部分,其实是把整个HTML都匹配一次,但其实我们只需要第一个获取到的title标签的内容.
输出内容时,不应该每获取到一个信息就调用一次print,可以统一输出。
好像在bs4的beautlfulsoup方法中有更好的对HTML文本的处理方法