搜索
bottom↓
回复: 23

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

[复制链接]

出0入0汤圆

发表于 2017-4-23 09:16:29 | 显示全部楼层 |阅读模式
本帖最后由 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()[0]["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[0]#ret[0]总共有几封邮件# 取第一封邮件完整信息,在返回值里,是按行存储在down[1]的列表里的。down[0]是返回的状态信息
            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[1]   #获取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[content.find("IP:") + 1:]  # 抓取ip所在位置
            # content = content[content.find("<code>") + 6:content.find("</code>")]  # 抓取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)[0]
        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(":")[1].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[0]  # ret[0]总共有几封邮件# 取第一封邮件完整信息,在返回值里,是按行存储在down[1]的列表里的。down[0]是返回的状态信息
           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[1]  # 获取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)

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出100入101汤圆

发表于 2017-4-23 10:59:11 | 显示全部楼层
当然这里有一个前提,那就是你需要用端口映射来设置路由器,将你路由器上的公网端口映射到你内网的树莓派上的IP地址

出0入0汤圆

发表于 2017-4-23 11:05:50 | 显示全部楼层
现在公网IP很少了吧

出0入58汤圆

发表于 2017-4-23 11:10:56 来自手机 | 显示全部楼层
前提是得有个公网ip,不然一切都是扯淡。

出0入25汤圆

发表于 2017-4-23 11:26:32 来自手机 | 显示全部楼层
如何知道一个 adsl  ip是公网ip?

出0入0汤圆

发表于 2017-4-23 12:29:29 来自手机 | 显示全部楼层
小区网络NAT太深了就无效了吧

出0入0汤圆

发表于 2017-4-23 13:21:37 来自手机 | 显示全部楼层
需要要公网ip的,可以给运营商打电话试试,我就要到了一个

出0入0汤圆

 楼主| 发表于 2017-4-23 13:31:19 | 显示全部楼层
3050311118 发表于 2017-4-23 12:29
小区网络NAT太深了就无效了吧

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

出0入8汤圆

发表于 2017-4-23 15:42:26 | 显示全部楼层
开个qq,同意远程密码控制,远程过去,查下IP,这个方案可行吗

出0入0汤圆

发表于 2017-4-23 16:46:41 | 显示全部楼层
楼主,你需要ddclient

出0入0汤圆

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

出0入89汤圆

发表于 2017-4-23 16:56:04 来自手机 | 显示全部楼层
frp可以解决这个

出0入0汤圆

 楼主| 发表于 2017-4-23 17:28:34 | 显示全部楼层
SNOOKER 发表于 2017-4-23 16:47
每次都要去开邮箱查iP不觉得烦吗,搞个免费域名,每次访问轻松愉快

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

出0入0汤圆

 楼主| 发表于 2017-4-23 17:29:34 | 显示全部楼层
轻风 发表于 2017-4-23 15:42
开个qq,同意远程密码控制,远程过去,查下IP,这个方案可行吗

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

出0入0汤圆

 楼主| 发表于 2017-4-23 17:31:38 | 显示全部楼层
轻风 发表于 2017-4-23 15:42
开个qq,同意远程密码控制,远程过去,查下IP,这个方案可行吗

这样很不方便

出0入0汤圆

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

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

出0入0汤圆

发表于 2017-4-23 19:06:10 来自手机 | 显示全部楼层
把花生壳用错地方了,你需要的是oray的ddns 不是花生壳

出0入0汤圆

 楼主| 发表于 2017-4-24 10:28:04 | 显示全部楼层
panjun10 发表于 2017-4-23 19:06
把花生壳用错地方了,你需要的是oray的ddns 不是花生壳

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

出0入0汤圆

发表于 2017-5-20 14:19:44 | 显示全部楼层
楼主这个功能是对花生壳的拓展。这个想法不错。

出90入4汤圆

发表于 2017-5-20 16:49:46 来自手机 | 显示全部楼层
没有看明白,直接路由器中设花生壳,端口映射不可以?

出0入8汤圆

发表于 2017-5-20 17:31:20 | 显示全部楼层
楼主研究下 IPV6 咋搞。我这里运营商支持IPV6 ,但是 IPV6 不懂啊。不知道IPV6  是不是也有内网,外网的说法。

出0入0汤圆

发表于 2017-5-21 06:02:04 来自手机 | 显示全部楼层
花生棒,拿走不谢。当然也有免费的 ngrok

出0入0汤圆

发表于 2017-5-21 21:19:11 来自手机 | 显示全部楼层
还是ddns安逸

出0入0汤圆

发表于 2017-5-21 22:05:15 | 显示全部楼层
公网IP 有这个只需要用域名绑定IP,路由端口映射就可以了 哪来那么多复杂的东西
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-3-29 17:00

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表