根據域名獲取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):                              #通過正則匹配<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文本的處理方法




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章