根据域名获取ip地址、端口、服务器类型和标题

时间:2022-07-27 20:41:00

这个是我在一家网络安全公司面试时的操作题,回来后经过多次修改后才得到一个比较完整的程序。

整个模块可以分成两个大部分。一个是数据库的操作,一个是信息的获取(类似爬虫?)

信息的获取分为四个小操作,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获取失败"

ip的获取其实比较简单,就是用socket模块的方法根据域名访问获取ip。要注意的是,这个域名是不包括”http://“,所以代码中用url[7:]


端口的获取其实更简单:

    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"端口号获取失败"

用的也是socket模块的方法。


服务器类型的获取:

一开始我的想法是通过正则去匹配应答头,但是过了两天发现其实有更简单的方法,直接用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里面,而是在HTML文本里声明,其次在使用正则匹配时,我们首先要知道当前访问的页面的编码方式,这又需要去获取,最后不同的网页并不会固定title标签出现的位置,虽然大致位置一样。

所以我的想法是,先在回传数据的header里面找到‘content-type’(正常网页的编码方式都会在这里声明)。

然后获得这个字符串还不是编码方式,还要通过正则匹配匹配 charset:() 括号里面的才是真正的编码方式。

接着使用这个编码方式去对获取到的html文本解码,再编码成需要的类型(我的pycharm用utf-8)。

再就是对获取到的文体再正则匹配,这次匹配的是title标签里面的内容。

最后输出第一个匹配到的内容,输出的时候还要注意不同编码形式的字符串不能相连。

    def getTitle(self):                              #通过正则匹配<title>标签内的字符
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"<title>(.*)</title>"
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): #通过正则匹配<title>标签内的字符
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"<title>(.*)</title>"
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文本的处理方法