jackwmj12 发表于 2017-4-23 09:16:29

用python进行外网对自己服务器访问(不需要花生壳)

本帖最后由 jackwmj12 于 2017-4-23 09:22 编辑

之前一直想用树莓派来做自己的数据库,但是苦于没法外网访问,然后利用免费的花生壳,又对流量有所限制。所以自己想了个别的办法来获取自己家里的树莓派的公网ip,获取IP我这里有两个阶段,第一个阶段是我树莓派通过花生壳建立一个网页,这个网页什么都不干,就返回一个IP地址,该返回的IP地址取值于树莓派服务器的数据库。第二阶段是树莓派还会讲IP发送到你指定的邮箱和地址。所以我们要做的就是让树莓派跑一个脚本,这个脚本的功能就是运行一个爬虫,访问访问 http://www.ip.cn/ ,或者http://members.3322.org/dyndns/getip,爬去该网页上显示的你的机器的公网IP,如果IP变更,那么就将你树莓派上数据库里IP值变更掉,并且将IP发送到你的邮箱内。
这样,我们的树莓派公网IP我们就获得了,如果你在外网需要访问树莓派的服务器或者是ssh远程控制你的树莓派,那么只需要检查邮箱或者查看你的网页下显示的IP地址就可以了。当然这里有一个前提,那就是你需要用端口映射来设置路由器,将你路由器上的公网端口映射到你内网的树莓派上的IP地址
比如你需要使用3306号端口做mysql,那你就在路由器里设置本地ip:3306号端口映射到外网ip:3306号端口就可以直接用 外网:3306 进行访问。
#!/usr/bin/python3
#-*- coding:utf-8 -*-
import urllib.request
from smtplib import SMTP_SSL
from email.header import Header
from email.mime.text import MIMEText
import time
from email.parser import Parser
from email.header import decode_header
import poplib
import info_init
import pymongo

class Applaction:
    def __init__(self):
      info_app = info_init.Applaction()
      # info_data=info_app.load_decode(file_path=r"E:\data_init.jason") #这个jason文件保存着你的资料
      # self.url = info_data["url"]
      self.url = r"http://members.3322.org/dyndns/getip"
      self.email_head =info_data["email_head"]               #email头,用于脚本自动从邮箱读取IP时筛选邮件
      self.email_send =info_data["email_send"]               #发送的邮箱号
      self.email_sendpw=info_data["email_sendpw"]          #发送邮箱密码
      self.email_sendhost = info_data["email_sendhost"]    #发送邮箱host
      self.email_recv=info_data["email_recv"]                  #接收邮箱号
      self.email_recvpw = info_data["email_recvpw"]          #接受邮箱密码 #用于从邮箱读取IP信息
      self.email_recvhost = info_data["email_recvhost"]       #接受邮箱HOST
        self.ip_url = "你树莓派显示IP的网站名"      
      user = info_data["mongo_user"]                               #数据库账号
      pwd = info_data["mongo_pw"]                              #数据库密码
      uri = 'mongodb://' + user + ":" + pwd + "@" + "localhost" + ":" + "27017"
      client = pymongo.MongoClient(uri)
      metal = client['金属价格']
      coper = metal['coper']
      self.mongo_ip = coper['ip']

    def ip_check(self):
      content = None
      try:
            content = self.mongo_ip.find()["ip"]
      except Exception as e:
            print(e)
      if content is None:
            ip_address = 0
            message =[]
            # pop3服务器地址
            host = self.email_recvhost
            # 用户名
            username = self.email_recv
            # 密码
            password = self.email_recvpw
            # 创建一个pop3对象,这个时候实际上已经连接上服务器了
            pp = poplib.POP3_SSL(host)
            # 设置调试模式,可以看到与服务器的交互信息
            #pp.set_debuglevel(1)
            # 向服务器发送用户名
            pp.user(username)
            # 向服务器发送密码
            pp.pass_(password)
            # 获取服务器上信件信息,返回是一个列表,第一项是一共有多上封邮件,第二项是共有多少字节
            ret = pp.stat()
            num=ret#ret总共有几封邮件# 取第一封邮件完整信息,在返回值里,是按行存储在down的列表里的。down是返回的状态信息
            while (ip_address is 0):
                # ret = pp.list()
                hdr,lines,octet = pp.retr(num)
                try:
                  for line in lines:
                        message.append(line.decode('utf-8'))
                  msg_content = '\r\n'.join(message)
                  msg = Parser().parsestr(msg_content)
                except Exception as e:
                  print(e)
                num = num -1
                if (num is 0):
                  pp.quit()#退出pop
                  return None
                try:
                  ip_flag = (self.decode_str(msg.get('Subject')))#获取主题
                  if self.email_head in ip_flag:#获取树莓派IP
                        ip = ip_flag.split(':')#字符分割
                        ip_address = ip   #获取ip地址
                        return ip_address
                  else :
                        del message[:]
                except Exception as e:
                  print(e)
            pp.quit()
      return content

    def ip_crawler(self):
      try:# 伪装头,用于防止网站服务器屏蔽你的ip
            req = urllib.request.Request(self.url, headers={
                'Connection': 'Keep-Alive',
                'Accept': 'text/html, application/xhtml+xml, */*',
                'Accept-Language': 'en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3',
                'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko'
            })
            wp = urllib.request.urlopen(req)
            content = wp.read()
            content = content.decode('utf-8', 'ignore')# utf-8解码
            # content = content# 抓取ip所在位置
            # content = content# 抓取ip
            return content
      except Exception as e:
            print(e)
            return None

    def send_email(self,content):
      mail_info = {
            "from": self.email_send,
            "to": self.email_recv,
            "hostname": self.email_sendhost,
            "username": self.email_send,
            "password": self.email_sendpw,
            "mail_subject": '%s:%s' % (self.email_head, content),
            "mail_text": "%s" % content,
            "mail_encoding": "utf-8"
      }
      # 这里使用SMTP_SSL就是默认使用465端口
      smtp = SMTP_SSL(mail_info["hostname"])
      smtp.set_debuglevel(1)
      try:
            smtp.ehlo(mail_info["hostname"])
            smtp.login(mail_info["username"], mail_info["password"])
            msg = MIMEText(mail_info["mail_text"], "plain", mail_info["mail_encoding"])
            msg["Subject"] = Header(mail_info["mail_subject"], mail_info["mail_encoding"])
            msg["from"] = mail_info["from"]
            msg["to"] = mail_info["to"]
            smtp.sendmail(mail_info["from"], mail_info["to"], msg.as_string())
            smtp.quit()
            return True
      except Exception as e:
            print(e)
            return False

    def decode_str(self,s):#邮件格式转换
      value, charset = decode_header(s)
      if charset:
            value = value.decode(charset)
      return value
    def ip_update(self,data):
      print("IP更新为{}".format(data))
      self.mongo_ip.update({'num': '0'}, {'$set': {'ip':data}})
    def ip_get(self):
       content = None
       try:
         wp = urllib.request.urlopen(self.ip_url)
         content = wp.read()
         content = content.decode("UTF-8").split(":").strip()
       except Exception as e:
         print(e)
       if content is None:
         ip_address = 0
         message = []
         # pop3服务器地址
         host = self.email_recvhost
         # 用户名
         username = self.email_recv
         # 密码
         password = self.email_recvpw
         # 创建一个pop3对象,这个时候实际上已经连接上服务器了
         pp = poplib.POP3_SSL(host)
         # 设置调试模式,可以看到与服务器的交互信息
         # pp.set_debuglevel(1)
         # 向服务器发送用户名
         pp.user(username)
         # 向服务器发送密码
         pp.pass_(password)
         # 获取服务器上信件信息,返回是一个列表,第一项是一共有多上封邮件,第二项是共有多少字节
         ret = pp.stat()
         num = ret# ret总共有几封邮件# 取第一封邮件完整信息,在返回值里,是按行存储在down的列表里的。down是返回的状态信息
         while (ip_address is 0):
               # ret = pp.list()
               hdr, lines, octet = pp.retr(num)
               try:
                   for line in lines:
                     message.append(line.decode('utf-8'))
                   msg_content = '\r\n'.join(message)
                   msg = Parser().parsestr(msg_content)
               except Exception as e:
                   print(e)
               num = num - 1
               if (num is 0):
                   pp.quit()# 退出pop
                   return None
               try:
                   ip_flag = (self.decode_str(msg.get('Subject')))# 获取主题
                   if self.email_head in ip_flag:# 获取树莓派IP
                     ip = ip_flag.split(':')# 字符分割
                     ip_address = ip# 获取ip地址
                     return ip_address
                   else:
                     del message[:]
               except Exception as e:
                   print(e)
         pp.quit()
       return content


if __name__ == '__main__':
    content=None
    content_email=None
    ip_get =Applaction()
    while True:
      if content_email is None:
            content_email = ip_get.ip_check()
            print(content_email)
      content = ip_get.ip_crawler()
      print(content)
      if ((content_email != content) and content_email != None and content != None):#如果新ip与已存在的老ip不一样,则发送邮件,反之不发
            content_email =content
            ip_get.send_email(content)
            ip_get.ip_update(content)
      elif content ==None:
            time.sleep(60)
      time.sleep(3600)

fengyunyu 发表于 2017-4-23 10:59:11

当然这里有一个前提,那就是你需要用端口映射来设置路由器,将你路由器上的公网端口映射到你内网的树莓派上的IP地址

duedue 发表于 2017-4-23 11:05:50

现在公网IP很少了吧

shawn_bu 发表于 2017-4-23 11:10:56

前提是得有个公网ip,不然一切都是扯淡。

rei1984 发表于 2017-4-23 11:26:32

如何知道一个 adslip是公网ip?

3050311118 发表于 2017-4-23 12:29:29

小区网络NAT太深了就无效了吧

runapp 发表于 2017-4-23 13:21:37

需要要公网ip的,可以给运营商打电话试试,我就要到了一个

jackwmj12 发表于 2017-4-23 13:31:19

3050311118 发表于 2017-4-23 12:29
小区网络NAT太深了就无效了吧

小区是没法用的,小区的IP实际上是一个局域网IP,直接用公网IP是没办法访问到的。

轻风 发表于 2017-4-23 15:42:26

开个qq,同意远程密码控制,远程过去,查下IP,这个方案可行吗

SNOOKER 发表于 2017-4-23 16:46:41

楼主,你需要ddclient

SNOOKER 发表于 2017-4-23 16:47:56

每次都要去开邮箱查iP不觉得烦吗,搞个免费域名,每次访问轻松愉快

youkebing 发表于 2017-4-23 16:56:04

frp可以解决这个

jackwmj12 发表于 2017-4-23 17:28:34

SNOOKER 发表于 2017-4-23 16:47
每次都要去开邮箱查iP不觉得烦吗,搞个免费域名,每次访问轻松愉快

第一点:你外部访问你树莓派的数据库和SSH是没法通过域名访问的
第二点:我上面提到我实际上是有域名的,并且用了花生壳,但是花生壳我用的是免费版,每个月是有一定的流量限制的,所以我在我的个人主页上也用了一个链接的办法,点进主页需要用到域名,点域名上的链接全部由django将域名转化成IP地址直接访问,这样有个好处就是通过IP直接访问是不需要用到花生壳流量的。
第三点:我查看我的ip地址不是只有邮箱查询一个办法的,还可以通过域名访问,我讲IP地址存在树莓派服务器的数据库里,然后把IP地址显示在我的一个域名链接上。当我其它软件需要访问到我的树莓派数据库的时候,我的软件会自动去这个域名地址爬去IP地址然后进行访问,这个是最最重要的,如果这个办法爬去到的IP不能访问,我的程序会退而进行邮箱里查询IP地址。因为第二者速度慢,所以优先第一种。

jackwmj12 发表于 2017-4-23 17:29:34

轻风 发表于 2017-4-23 15:42
开个qq,同意远程密码控制,远程过去,查下IP,这个方案可行吗

其实你稍微会点网站就行。把IP地址直接显示在自己域名网站上就行

jackwmj12 发表于 2017-4-23 17:31:38

轻风 发表于 2017-4-23 15:42
开个qq,同意远程密码控制,远程过去,查下IP,这个方案可行吗

这样很不方便

SNOOKER 发表于 2017-4-23 17:40:02

jackwmj12 发表于 2017-4-23 17:28
第一点:你外部访问你树莓派的数据库和SSH是没法通过域名访问的
第二点:我上面提到我实际上是有域名的, ...

数据库我不太清楚,sSH为何不能通过域名访问?你是指需要通过端口映射吗

panjun10 发表于 2017-4-23 19:06:10

把花生壳用错地方了,你需要的是oray的ddns 不是花生壳

jackwmj12 发表于 2017-4-24 10:28:04

panjun10 发表于 2017-4-23 19:06
把花生壳用错地方了,你需要的是oray的ddns 不是花生壳

花生壳也是需要的,我本身有个主页用到的,我是有了花生壳顺便扩展了这个功能。

yzz163 发表于 2017-5-20 14:19:44

楼主这个功能是对花生壳的拓展。这个想法不错。

john78 发表于 2017-5-20 16:49:46

没有看明白,直接路由器中设花生壳,端口映射不可以?

kebaojun305 发表于 2017-5-20 17:31:20

楼主研究下 IPV6 咋搞。我这里运营商支持IPV6 ,但是 IPV6 不懂啊。不知道IPV6是不是也有内网,外网的说法。

wxws_wxws 发表于 2017-5-21 06:02:04

花生棒,拿走不谢。当然也有免费的 ngrok

huangqi412 发表于 2017-5-21 21:19:11

还是ddns安逸

ywlzh 发表于 2017-5-21 22:05:15

公网IP 有这个只需要用域名绑定IP,路由端口映射就可以了 哪来那么多复杂的东西
页: [1]
查看完整版本: 用python进行外网对自己服务器访问(不需要花生壳)