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

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

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

信息的获取分为四个小操作,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):                              #通过正则匹配标签内的字符
        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>(.*)"
            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"<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文本的处理方法




你可能感兴趣的:(python,数据库,网络安全,python,爬虫,网页信息获取)