精华内容
参与话题
问答
  • 基于JavaMail的Java邮件发送:简单邮件发送

    万次阅读 多人点赞 2016-06-14 21:56:35
    电子邮件的应用非常广泛,例如在某网站注册了一个...但这些应用不可能和我们自己平时发邮件一样,先打开浏览器,登录邮箱,创建邮件再发送。本文将简单介绍如何通过 Java 代码来创建电子邮件,并连接邮件服务器发...

    本文链接: https://blog.csdn.net/xietansheng/article/details/51673073

    电子邮件的应用非常广泛,例如在某网站注册了一个账户,自动发送一封欢迎邮件,通过邮件找回密码,自动批量发送活动信息等。但这些应用不可能和我们自己平时发邮件一样,先打开浏览器,登录邮箱,创建邮件再发送。本文将简单介绍如何通过 Java 代码来创建电子邮件,并连接邮件服务器发送邮件。

    1. 电子邮件协议

    电子邮件的在网络中传输和网页一样需要遵从特定的协议,常用的电子邮件协议包括 SMTP,POP3,IMAP。其中邮件的创建和发送只需要用到 SMTP协议,所以本文也只会涉及到SMTP协议。SMTP 是 Simple Mail Transfer Protocol 的简称,即简单邮件传输协议。

    2. JavaMail

    我们平时通过 Java 代码打开一个 http 网页链接时,通常可以使用已经对 http 协议封装好的 HttpURLConnection 类来快速地实现。Java 官方也提供了对电子邮件协议封装的 Java 类库,就是JavaMail,但并没有包含到标准的 JDK 中,需要我们自己去官方下载,这里我从 JavaEE 官方的 Github 仓库下载。

    JavaMail 下载地址: https://github.com/javaee/javamail/releases

    这里我下载的版本是 1.6.0 版本,包含了 SMTP, IMAP, 和 POP3 协议的实现的 jar 包:

    javaxmail.png

    我把 JavaMail 1.6.0 的 jar 包上传到了 CSDN,如果无法从 Github 下载,可以从 CSDN 下载(建议到Github下载,CSDN上我设了积分): http://download.csdn.net/download/xietansheng/9928266

    特别注意:

    • 本测试用例用的 JavaMail 版本是 1.6.0,如果下载到其他版本的 JavaMail 运行时出现问题,请使用 JavaMail 1.6.0 版本再进行尝试。
    • 使用 JavaMail 1.6.0 要求的 JDK 版本必须是 JDK 1.7 以上(建议使用最新版 JDK)。
    • 不要直接就完完全全复制我的代码,需要 修改一下发送的标题、内容、用户昵称,要不然所有人都直接复制我的代码发送(自己也不要反复发送内容相同的邮件),内容一致的邮件多次发送,邮箱服务器就可能会检测到这些内容是垃圾广告内容,不让你发送,会返回错误码,查询错误码也能查询到失败原因。

    有些童鞋反应代码提示某些类找不到,或运行出现问题,往往都是 JDK 版本过低,或 JavaMail 版本过低,出现问题时 请使用推荐的版本

    再出现问题,只要能连接成功,都有错误码返回,有些童鞋发送失败在控制台已经给出了错误码,大部分还给出了查看错误码含义的链接,点开链接,查询对应的错误码,为什么失败,已经明明白白的告诉你了,针对失败原因修改即可!

    邮件发送,涉及多端(本地代码端、邮件发送服务器端、邮件接收服务器端),保证自己的代码没问题了,不一定就能成功,你把你的邮件提交到邮件发送服务器,发送的服务器可以给你拒绝服务(比如认为发送的内容是垃圾广告,或者你频繁请求发送),这不是代码端能管的事情,但都有错误码返回,明确告诉你为什么错误。就算你发送成功了,对方也有可能接收不到,成功发送到对方的邮件接收服务器后,对方的服务器可以根据你的内容拒绝收你的邮件(比如认为你的内容是广告诈骗等信息,或者发送过于频繁),对方的服务器可能直接把你的邮件扔垃圾箱,或者直接忽略。出现这些问题,请修改发送的标题、内容、昵称等,重复的内容不要反复发送,或多更换收件箱和发件箱试试!!!

    3. 创建一封简单的电子邮件

    首先创建一个 Java 工程,把下载好的 javax.mail.jar 作为类库加入工程,这里不多说。

    邮件创建步骤:

    1. 创建一个邮件对象(MimeMessage);
    2. 设置发件人,收件人,可选增加多个收件人,抄送人,密送人;
    3. 设置邮件的主题(标题);
    4. 设置邮件的正文(内容);
    5. 设置显示的发送时间;
    6. 保存到本地。

    代码实现:

    package com.xiets.javamaildemo;
    
    import javax.mail.Session;
    import javax.mail.internet.InternetAddress;
    import javax.mail.internet.MimeMessage;
    import java.io.FileOutputStream;
    import java.io.OutputStream;
    import java.util.Date;
    import java.util.Properties;
    
    /**
     * JavaMail 版本: 1.6.0
     * JDK 版本: JDK 1.7 以上(必须)
     */
    public class Main {
    
        public static void main(String[] args) throws Exception {
            // 1. 创建一封邮件
            Properties props = new Properties();
            // 用于连接邮件服务器的参数配置(发送邮件时才需要用到)
            Session session= Session.getInstance(props);        // 根据参数配置,创建会话对象(为了发送邮件准备的)
            MimeMessage message = new MimeMessage(session);     // 创建邮件对象
    
            /*
             * 也可以根据已有的eml邮件文件创建 MimeMessage 对象
             * MimeMessage message = new MimeMessage(session, new FileInputStream("MyEmail.eml"));
             */
    
            // 2. From: 发件人
            //    其中 InternetAddress 的三个参数分别为: 邮箱, 显示的昵称(只用于显示, 没有特别的要求), 昵称的字符集编码
            //    真正要发送时, 邮箱必须是真实有效的邮箱。
            message.setFrom(new InternetAddress("aa@send.com", "USER_AA", "UTF-8"));
    
            // 3. To: 收件人
            message.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress("cc@receive.com", "USER_CC", "UTF-8"));
            //    To: 增加收件人(可选)
            message.addRecipient(MimeMessage.RecipientType.TO, new InternetAddress("dd@receive.com", "USER_DD", "UTF-8"));
            //    Cc: 抄送(可选)
            message.setRecipient(MimeMessage.RecipientType.CC, new InternetAddress("ee@receive.com", "USER_EE", "UTF-8"));
            //    Bcc: 密送(可选)
            message.setRecipient(MimeMessage.RecipientType.BCC, new InternetAddress("ff@receive.com", "USER_FF", "UTF-8"));
    
            // 4. Subject: 邮件主题
            message.setSubject("TEST邮件主题", "UTF-8");
    
            // 5. Content: 邮件正文(可以使用html标签)
            message.setContent("TEST这是邮件正文。。。", "text/html;charset=UTF-8");
    
            // 6. 设置显示的发件时间
            message.setSentDate(new Date());
    
            // 7. 保存前面的设置
            message.saveChanges();
    
            // 8. 将该邮件保存到本地
            OutputStream out = new FileOutputStream("MyEmail.eml");
            message.writeTo(out);
            out.flush();
            out.close();
        }
    
    }
    

    保存的 MyEmail.eml 可以使用邮件客户端打开查看,实际上就是一堆符合SMTP协议格式的文本(内容使用base64进行了编码),也可用记事本打开,如下所示:

    my-email.png

    my-email-txt.png

    4. 发送电子邮件

    发送邮件首先需要有一个邮箱账号和密码,本文以网易163邮箱为例,邮箱账号必须要开启 SMTP 服务,在浏览器网页登录邮箱后一般在邮箱的“设置”选项中可以开启,并记下邮箱的 SMTP 服务器地址,如下所示(其他邮箱大同小异):

    smtp-163-setting.jpg

    代码实现:

    package com.xiets.javamaildemo;
    
    import javax.mail.Session;
    import javax.mail.Transport;
    import javax.mail.internet.InternetAddress;
    import javax.mail.internet.MimeMessage;
    import java.util.Date;
    import java.util.Properties;
    
    /**
     * JavaMail 版本: 1.6.0
     * JDK 版本: JDK 1.7 以上(必须)
     */
    public class Main {
    
        // 发件人的 邮箱 和 密码(替换为自己的邮箱和密码)
        // PS: 某些邮箱服务器为了增加邮箱本身密码的安全性,给 SMTP 客户端设置了独立密码(有的邮箱称为“授权码”), 
        //     对于开启了独立密码的邮箱, 这里的邮箱密码必需使用这个独立密码(授权码)。
        public static String myEmailAccount = "xxxxxxxxx@163.com";
        public static String myEmailPassword = "xxxxxxxxx";
    
        // 发件人邮箱的 SMTP 服务器地址, 必须准确, 不同邮件服务器地址不同, 一般(只是一般, 绝非绝对)格式为: smtp.xxx.com
        // 网易163邮箱的 SMTP 服务器地址为: smtp.163.com
        public static String myEmailSMTPHost = "smtp.163.com";
    
        // 收件人邮箱(替换为自己知道的有效邮箱)
        public static String receiveMailAccount = "xxxxxxxxx@qq.com";
    
        public static void main(String[] args) throws Exception {
            // 1. 创建参数配置, 用于连接邮件服务器的参数配置
            Properties props = new Properties();                    // 参数配置
            props.setProperty("mail.transport.protocol", "smtp");   // 使用的协议(JavaMail规范要求)
            props.setProperty("mail.smtp.host", myEmailSMTPHost);   // 发件人的邮箱的 SMTP 服务器地址
            props.setProperty("mail.smtp.auth", "true");            // 需要请求认证
    
            // PS: 某些邮箱服务器要求 SMTP 连接需要使用 SSL 安全认证 (为了提高安全性, 邮箱支持SSL连接, 也可以自己开启),
            //     如果无法连接邮件服务器, 仔细查看控制台打印的 log, 如果有有类似 “连接失败, 要求 SSL 安全连接” 等错误,
            //     打开下面 /* ... */ 之间的注释代码, 开启 SSL 安全连接。
            /*
            // SMTP 服务器的端口 (非 SSL 连接的端口一般默认为 25, 可以不添加, 如果开启了 SSL 连接,
            //                  需要改为对应邮箱的 SMTP 服务器的端口, 具体可查看对应邮箱服务的帮助,
            //                  QQ邮箱的SMTP(SLL)端口为465或587, 其他邮箱自行去查看)
            final String smtpPort = "465";
            props.setProperty("mail.smtp.port", smtpPort);
            props.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
            props.setProperty("mail.smtp.socketFactory.fallback", "false");
            props.setProperty("mail.smtp.socketFactory.port", smtpPort);
            */
    
            // 2. 根据配置创建会话对象, 用于和邮件服务器交互
            Session session = Session.getInstance(props);
            session.setDebug(true);                                 // 设置为debug模式, 可以查看详细的发送 log
    
            // 3. 创建一封邮件
            MimeMessage message = createMimeMessage(session, myEmailAccount, receiveMailAccount);
    
            // 4. 根据 Session 获取邮件传输对象
            Transport transport = session.getTransport();
    
            // 5. 使用 邮箱账号 和 密码 连接邮件服务器, 这里认证的邮箱必须与 message 中的发件人邮箱一致, 否则报错
            // 
            //    PS_01: 成败的判断关键在此一句, 如果连接服务器失败, 都会在控制台输出相应失败原因的 log,
            //           仔细查看失败原因, 有些邮箱服务器会返回错误码或查看错误类型的链接, 根据给出的错误
            //           类型到对应邮件服务器的帮助网站上查看具体失败原因。
            //
            //    PS_02: 连接失败的原因通常为以下几点, 仔细检查代码:
            //           (1) 邮箱没有开启 SMTP 服务;
            //           (2) 邮箱密码错误, 例如某些邮箱开启了独立密码;
            //           (3) 邮箱服务器要求必须要使用 SSL 安全连接;
            //           (4) 请求过于频繁或其他原因, 被邮件服务器拒绝服务;
            //           (5) 如果以上几点都确定无误, 到邮件服务器网站查找帮助。
            //
            //    PS_03: 仔细看log, 认真看log, 看懂log, 错误原因都在log已说明。
            transport.connect(myEmailAccount, myEmailPassword);
    
            // 6. 发送邮件, 发到所有的收件地址, message.getAllRecipients() 获取到的是在创建邮件对象时添加的所有收件人, 抄送人, 密送人
            transport.sendMessage(message, message.getAllRecipients());
    
            // 7. 关闭连接
            transport.close();
        }
    
        /**
         * 创建一封只包含文本的简单邮件
         *
         * @param session 和服务器交互的会话
         * @param sendMail 发件人邮箱
         * @param receiveMail 收件人邮箱
         * @return
         * @throws Exception
         */
        public static MimeMessage createMimeMessage(Session session, String sendMail, String receiveMail) throws Exception {
            // 1. 创建一封邮件
            MimeMessage message = new MimeMessage(session);
    
            // 2. From: 发件人(昵称有广告嫌疑,避免被邮件服务器误认为是滥发广告以至返回失败,请修改昵称)
            message.setFrom(new InternetAddress(sendMail, "某宝网", "UTF-8"));
    
            // 3. To: 收件人(可以增加多个收件人、抄送、密送)
            message.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress(receiveMail, "XX用户", "UTF-8"));
    
            // 4. Subject: 邮件主题(标题有广告嫌疑,避免被邮件服务器误认为是滥发广告以至返回失败,请修改标题)
            message.setSubject("打折钜惠", "UTF-8");
    
            // 5. Content: 邮件正文(可以使用html标签)(内容有广告嫌疑,避免被邮件服务器误认为是滥发广告以至返回失败,请修改发送内容)
            message.setContent("XX用户你好, 今天全场5折, 快来抢购, 错过今天再等一年。。。", "text/html;charset=UTF-8");
    
            // 6. 设置发件时间
            message.setSentDate(new Date());
    
            // 7. 保存设置
            message.saveChanges();
    
            return message;
        }
    
    }
    

    发送后查看收件人的收件箱:

    sended-email.jpg


    包含文本、图片、附件 的复杂邮件的创建请看下一篇:基于 JavaMail 的 Java 邮件发送:复杂邮件发送


    展开全文
  • 使用vbs代码实现多条消息快速发送

    万次阅读 多人点赞 2019-07-17 19:21:22
    带数字的提示的 set wshshell=wscript.createobject...wshshell.AppActivate"要发送的人的名字" for i=1 to 100 //循环次数 wscript.sleep 100 //间隔时间,单位毫秒 wshshell.sendKeys "^v" wshshell.sendKeys...

     

    带数字的提示的

    set wshshell=wscript.createobject("wscript.shell") 
    wshshell.AppActivate"要发送的人的名字" 
    for i=1 to 100     //循环次数
    wscript.sleep 100     //间隔时间,单位毫秒
    wshshell.sendKeys "^v" 
    wshshell.sendKeys i 
    wshshell.sendKeys "%s" 
    next
    

    不带数字的提示的

    set wshshell=wscript.createobject("wscript.shell") 
    wshshell.AppActivate"要发送的人的名字" 
    for i=1 to 100
    wscript.sleep 100 
    wshshell.sendKeys "^v" 
    wshshell.sendKeys "%s" 
    next
    

     

     创建一个text文本,把代码复制进去,后缀改为vbs,复制要发送的内容,然后点击聊天框,双击vbs文件即可,两个版本,一个带数字,一个不带

    展开全文
  • Python 简单发送邮件 / 发送带各种附件邮件

    万次阅读 多人点赞 2018-06-04 13:55:05
    写个自动发送邮件的python脚本。 用脚本发送邮件思路是:登陆邮箱-->写邮件-->发送邮件 发送邮箱设置: 得开通自己邮箱的 SMTP 功能,具体可百度。 以163网易邮箱为例: 设置 --> POP3/SMTP/IMAP --&...

    写个自动发送邮件的python脚本。

    用脚本发送邮件思路是:登陆邮箱-->写邮件-->发送邮件

    发送邮箱设置:

    得开通自己邮箱的 SMTP 功能,具体可百度。

    以163网易邮箱为例: 设置 -->  POP3/SMTP/IMAP  -->  设置POP3/SMTP/IMAP 勾选保存

    一、发送简单邮件:

    用STMP库

    import smtplib
    #发送字符串的邮件
    from email.mime.text import MIMEText
    #处理多种形态的邮件主体我们需要 MIMEMultipart 类
    from email.mime.multipart import MIMEMultipart
    #处理图片需要 MIMEImage 类
    from email.mime.image import MIMEImage
    
    #设置服务器所需信息
    fromaddr = '137xxx@163.com'#邮件发送方邮箱地址
    password = 'zzz'#密码(部分邮箱为授权码) 
    toaddrs = ['137xxx@163.com', '137xxxxx@qq.com']#邮件接受方邮箱地址,注意需要[]包裹,这意味着你可以写多个邮件地址群发
    
    
    #设置email信息
    #---------------------------发送字符串的邮件-----------------------------
    #邮件内容设置
    message = MIMEText('hello,ziqiiii','plain','utf-8')
    #邮件主题       
    message['Subject'] = 'ziqiiii test email' 
    #发送方信息
    message['From'] = sender 
    #接受方信息     
    message['To'] = receivers[0]  
    #---------------------------------------------------------------------
    
    
    #登录并发送邮件
    try:
        server = smtplib.SMTP('smtp.163.com')#163邮箱服务器地址,端口默认为25
        server.login(fromaddr,password)
        server.sendmail(fromaddr, toaddrs, message.as_string())
        print('success')
        server.quit()
    
    except smtplib.SMTPException as e:
        print('error',e) #打印错误

     

    二、发送各种带附件的邮件

    使用MIMEMultipart来标示这个邮件是多个部分组成的,然后attach各个部分。如果是附件,则add_header加入附件的声明。

    MIME有很多种类型,这个略麻烦,如果附件是图片格式,我要用MIMEImage,如果是音频,要用MIMEAudio,如果是word、excel,我都不知道该用哪种MIME类型了,得上google去查。

    最懒的方法就是,不管什么类型的附件,都用MIMEApplication,MIMEApplication默认子类型是application/octet-stream。

    import smtplib
    from email.mime.text import MIMEText
    from email.mime.image import MIMEImage
    from email.mime.multipart import MIMEMultipart
    from email.mime.application import MIMEApplication 
    
    if __name__ == '__main__':
            fromaddr = '137xxxx@163.com'
            password = 'password'
            toaddrs = ['137xxxx@163.com', '137xxxx@qq.com']
    
            content = 'hello, this is email content.'
            textApart = MIMEText(content)
    
            imageFile = '1.png'
            imageApart = MIMEImage(open(imageFile, 'rb').read(), imageFile.split('.')[-1])
            imageApart.add_header('Content-Disposition', 'attachment', filename=imageFile)
    
            pdfFile = '算法设计与分析基础第3版PDF.pdf'
            pdfApart = MIMEApplication(open(pdfFile, 'rb').read())
            pdfApart.add_header('Content-Disposition', 'attachment', filename=pdfFile)
        
    
            zipFile = '算法设计与分析基础第3版PDF.zip'
            zipApart = MIMEApplication(open(zipFile, 'rb').read())
            zipApart.add_header('Content-Disposition', 'attachment', filename=zipFile)
    
            m = MIMEMultipart()
            m.attach(textApart)
            m.attach(imageApart)
            m.attach(pdfApart)
            m.attach(zipApart)
            m['Subject'] = 'title'
    
            try:
                server = smtplib.SMTP('smtp.163.com')
                server.login(fromaddr,password)
                server.sendmail(fromaddr, toaddrs, m.as_string())
                print('success')
                server.quit()
            except smtplib.SMTPException as e:
                print('error:',e) #打印错误
    

     

     

     

     

     

    参考自:

    1.简单三步,用python发邮件

    2.飘逸的python - 发送带各种类型附件的邮件

    3.python收发邮件(demo)--smtplib--poplib

    展开全文
  • python3网络爬虫一《使用urllib.request发送请求》

    万次阅读 多人点赞 2017-07-25 16:16:05
    python 爱好者交流群:810306356...在Python2版本中,有urllib和urlib2两个库可以用来实现request的发送。而在Python3中,已经不存在urllib2这个库了,统一为urllib。Python3 urllib库官方链接 https://docs.pytho...

     

    使用urllib

    顺便提一下

    如果有任何问题,你可以在这里找到我 ,软件测试交流qq群,209092584

     

    在Python2版本中,有urllib和urlib2两个库可以用来实现request的发送。而在Python3中,已经不存在urllib2这个库了,统一为urllib。Python3 urllib库官方链接       

    https://docs.python.org/3/library/urllib.html

     

     

    urllib中包括了四个模块,包括

     

    urllib.request,urllib.error,urllib.parse,urllib.robotparser

     

     

    urllib.request可以用来发送request和获取request的结果
    urllib.error包含了urllib.request产生的异常
    urllib.parse用来解析和处理URL

     

    urllib.robotparse用来解析页面的robots.txt文件

     

     

    可见其中模拟请求使用的最主要的库便是urllib.request,异常处理用urllib.error库。

     

    下面会对它们一一进行详细的介绍。

    使用urllib.request发送请求

     

    urllib.request.urlopen()基本使用
    urllib.request 模块提供了最基本的构造 HTTP 请求的方法,利用它可以模拟浏览器的一个请求发起过程,同时它还带有处理 authenticaton (授权验证), redirections (重定向), cookies (浏览器Cookies)以及其它内容。好,那么首先我们来感受一下它的强大之处,我们百度为例,我们来把这个网页抓下来。

     

    import urllib.request
    
    response = urllib.request.urlopen("https://www.baidu.com")
    print(response.read().decode("utf-8"))

     

     

     

     

    真正的代码只有两行,我们便完成了百度的抓取,输出了网页的源代码,得到了源代码之后呢?你想要的链接、图片地址、文本信息不就都可以提取出来了吗?接下来我们看下它返回的到底是什么,利用 type 函数输出 response 的类型。

     

     

    >>> import urllib.request
    >>> response = urllib.request.urlopen("https://www.baidu.com")
    >>> print(type(response))
    <class 'http.client.HTTPResponse'>

     

     

     

    通过输出结果可以发现它是一个 HTTPResposne 类型的对象,它主要包含的方法有 read() 、 readinto() 、getheader(name) 、 getheaders() 、 fileno() 等函数和 msg 、 version 、 status 、 reason 、 debuglevel 、 closed 等属性。 得到这个对象之后,赋值为 response ,然后就可以用 response 调用这些方法和属性,得到返回结果的一系列信息。例如 response.read() 就可以得到返回的网页内容, response.status 就可以得到返回结果的状态码,如200代表请求成功,404代表网页未找到等。

     

    下面再来一个实例感受一下:

    >>> import urllib.request
    >>> response = urllib.request.urlopen("https://www.baidu.com")
    >>> print(response.status)
    200
    >>> print(response.getheaders())
    [('Accept-Ranges', 'bytes'), ('Cache-Control', 'no-cache'), ('Content-Length', '227'), ('Content-Type', 'text/html'), ('Date', 'Tue, 25 Jul 2017 06:36:40 GMT'), ('Last-Modified', 'Wed, 28 Jun 2017 02:16:00 GMT'), ('P3p', 'CP=" OTI DSP COR IVA OUR IND COM "'), ('Pragma', 'no-cache'), ('Server', 'BWS/1.1'), ('Set-Cookie', 'BD_NOT_HTTPS=1; path=/; Max-Age=300'), ('Set-Cookie', 'BIDUPSID=BEF13521D9F33BE4108EA36C07303743; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com'), ('Set-Cookie', 'PSTM=1500964600; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com'), ('Strict-Transport-Security', 'max-age=0'), ('X-Ua-Compatible', 'IE=Edge,chrome=1'), ('Connection', 'close')]
    >>> print(response.getheader("Server"))
    BWS/1.1


    可见,三个输出分别输出了响应的状态码,响应的头信息,以及通过传递一个参数获取了 Server 的类型。

     

     

     

    urllib.request.urlopen()详解

     

    利用以上最基本的 urlopen() 方法,我们可以完成最基本的简单网页的 GET 请求抓取。
    如果我们想给链接传递一些参数该怎么实现呢?我们首先看一下 urlopen() 函数的API。

    urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)


    可以发现除了第一个参数可以传递URL之外,我们还可以传递其它的内容,比如 data (附加参数), timeout (超时时间)等等。

    data 参数是可选的,如果要添加 data ,它要是字节流编码格式的内容,即 bytes 类型,通过 bytes() 函数可以进行转化,另外如果你传递了这个 data 参数,它的请求方式就不再是 GET 方式请求,而是 POST 。

    # coding=utf-8
    import urllib.parse
    import urllib.request
    data = bytes(urllib.parse.urlencode({'word': 'hello'}), encoding=
    'utf8')
    response = urllib.request.urlopen('http://httpbin.org/post', dat
    a=data)
    print(response.read())

     

    在这里我们传递了一个参数 word ,值是 hello 。它需要被转码成 bytes (字节流)类型。其中转字节流采用了 bytes() 方法,第一个参数需要是 str (字符串)类型,需要用 urllib.parse.urlencode() 方法来将参数字典转化为字符串。第二个参数指定编码格式,在这里指定为 utf8 。
    提交的网址是 httpbin.org ,它可以提供 HTTP 请求测试。 http://httpbin.org/post 这个地址可以用来测试 POST 请求,它可以输出请求和响应信息,其中就包含我们传递的 data 参数。
    运行结果如下:

     

    {
    "args": {},
    "data": "",
    "files": {},
    "form": {
    "word": "hello"
    },
    "headers": {
    "Accept-Encoding": "identity",
    "Content-Length": "10",
    "Content-Type": "application/x-www-form-urlencoded",
    "Host": "httpbin.org",
    "User-Agent": "Python-urllib/3.5"
    },
    "json": null,
    "origin": "123.124.23.253",
    "url": "http://httpbin.org/post"
    }

     


    我们传递的参数出现在了 form 中,这表明是模拟了表单提交的方式,以 POST 方式传输数据。

     

     

    timeout参数

    timeout 参数可以设置超时时间,单位为秒,意思就是如果请求超出了设置的这个时间还没有得到响应,就会抛出异常,如果不指定,就会使用全局默认时间。它支持 HTTP 、 HTTPS 、 FTP 请求。
    下面来用一个实例感受一下:

     

    >>> import urllib.request
    >>> response = urllib.request.urlopen("http://httpbin.org/get",timeout=1)
    >>> print(response.read())


    结果如下:

     

    During handling of the above exception, another exception occurr
    ed:
    Traceback (most recent call last): File "/var/py/python/urllibte
    st.py", line 4, in <module> response = urllib.request.urlopen('h
    ttp://httpbin.org/get', timeout=1)
    ...
    urllib.error.URLError: <urlopen error timed out>

     


    在这里我们设置了超时时间是1秒,程序1秒过后服务器依然没有响应,于是抛出了 urllib.error.URLError 异常,错误原因是 timed out 。

     

     

    因此我们可以通过设置这个超时时间来控制一个网页如果长时间未响应就跳过它的抓取,利用 try,except 语句就可以实现这样的操作。

     

     

    import urllib.request
    import  socket
    import urllib.error
    try:
        response = urllib.request.urlopen('http://httpbin.org/get',timeout=0.1)
    except urllib.error.URLError as e:
        if  isinstance(e.reason, socket.timeout):
                print("Time out!")

     


    在这里我们请求了 http://httpbin.org/get 这个测试链接,设置了超时时间是0.1秒,然后捕获了 urllib.error.URLError 这个异常,然后判断异常原因是超时异常,就得出它确实是因为超时而报错,打印输出了 TIME OUT ,当然你也可以在这里做其他的处理。

     

     

    运行结果如下:

     

    Time out!

    常理来说,0.1秒内基本不可能得到服务器响应,因此输出了 TIME OUT 的提示。这样,我们可以通过设置 timeout 这个参数来实现超时处理,有时还是很有用的。

     

     

     

    其他参数

    还有 context 参数,它必须是 ssl.SSLContext 类型,用来指定 SSL 设置。cafile 和 capath 两个参数是指定CA证书和它的路径,这个在请求 HTTPS 链接时会有用。

    cadefault 参数现在已经弃用了,默认为 False 。

    以上讲解了 url.request.urlopen() 方法的用法,通过这个最基本的函数可以完成简单的请求和网页抓取,如需详细了解,可以参见官方文档。
    https://docs.python.org/3/library/urllib.request.html

     

    urllib.request.Request的使用

    由上我们知道利用 urlopen() 方法可以实现最基本的请求发起,但这几个简单的参数并不足以构建一个完整的请求,如果请求中需要加入 headers 等信息,我们就可以利用更强大的 Request 类来构建一个请求。

    首先我们用一个实例来感受一下 Request 的用法:

     

    import urllib.request
    
    request =urllib.request.Request("https://www.baidu.com")
    response = urllib.request.urlopen(request)
    print(response.read().decode("utf-8"))

     


    可以发现,我们依然是用 urlopen() 方法来发送这个请求,只不过这次 urlopen() 方法的参数不再是一个URL,而是一个 Request ,通过构造这个这个数据结构,一方面我们可以将请求独立成一个对象,另一方面可配置参数更加
    丰富和灵活。
    下面我们看一下 Request 都可以通过怎样的参数来构造,它的构造方法如下。

     

     

     

     

    class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)


    第一个参数是请求链接,这个是必传参数,其他的都是可选参数。

     

    data 参数如果要传必须传 bytes (字节流)类型的,如果是一个字典,可以先用 urllib.parse.urlencode() 编码。

     

    headers 参数是一个字典,你可以在构造 Request 时通过 headers 参数传递,也可以通过调用 Request 对象的 add_header() 方法来添加请求头。请求头最常用的用法就是通过修改 User-Agent 来伪装浏览器,默认的 User-
    Agent 是 Python-urllib ,你可以通过修改它来伪装浏览器,比如要伪装火狐浏览器,你可以把它设置为 Mozilla/5.0 (X11; U; Linux i686)Gecko/20071127 Firefox/2.0.0.11

     

    origin_req_host 指的是请求方的 host 名称或者 IP 地址。

     

    unverifiable 指的是这个请求是否是无法验证的,默认是 False 。意思就是说用户没有足够权限来选择接收这个请求的结果。例如我们请求一个HTML文档中的图片,但是我们没有自动抓取图像的权限,这时 unverifiable 的值就是 True 。

    method 是一个字符串,它用来指示请求使用的方法,比如 GET , POST , PUT 等等。
    下面我们传入多个参数构建一个 Request 来感受一下:

     

    from urllib import request,parse
    url = "http://httpbin.org/post"
    headers = {
        #伪装一个火狐浏览器
        "User-Agent":'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)',
        "host":'httpbin.org'
    }
    dict = {
        "name":"Germey"
    }
    data = bytes(parse.urlencode(dict),encoding="utf8")
    req = request.Request(url=url,data=data,headers=headers,method="POST")
    response = request.urlopen(req)
    print(response.read().decode("utf-8"))

     


    在这里我们通过四个参数构造了一个 Request , url 即请求链接,在 headers 中指定了 User-Agent 和 Host ,传递的参数 data 用了 urlencode() 和 bytes() 方法来转成字节流,另外指定了请求方式为 POST 。

     

     

    运行结果如下:
     

     

    {
      "args": {}, 
      "data": "", 
      "files": {}, 
      "form": {
        "name": "Germey"
      }, 
      "headers": {
        "Accept-Encoding": "identity", 
        "Connection": "close", 
        "Content-Length": "11", 
        "Content-Type": "application/x-www-form-urlencoded", 
        "Host": "httpbin.org", 
        "User-Agent": "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)"
      }, 
      "json": null, 
      "origin": "1.85.221.5", 
      "url": "http://httpbin.org/post"
    }

     


    通过观察结果可以发现,我们成功设置了 data , headers 以及 method 。

     

     

    另外 headers 也可以用 add_header() 方法来添加。

     

     

    req = request.Request(url=url, data=data, method='POST')
    req.add_header('User-Agent', 'Mozilla/4.0 (compatible; MSIE 5.5;Windows NT)')

     

    如此一来,我们就可以更加方便地构造一个 Request ,实现请求的发送。

    urllib.request高级特性

     

    大家有没有发现,在上面的过程中,我们虽然可以构造 Request ,但是一些更高级的操作,比如 Cookies 处理,代理该怎样来设置?
    接下来就需要更强大的工具 Handler 登场了。

     

    简而言之你可以把它理解为各种处理器,有专门处理登录验证的,有处理 Cookies 的,有处理代理设置的,利用它们我们几乎可以做到任何 HTTP 请求中所有的事情。

     

    首先介绍下 urllib.request.BaseHandler ,它是所有其他 Handler 的父类,它提供了最基本的 Handler 的方法,例
    如 default_open() 、 protocol_request() 等。
    接下来就有各种 Handler 类继承这个 BaseHandler ,列举如下:

    • HTTPDefaultErrorHandler 用于处理HTTP响应错误,错误都会抛出 HTTPError 类型的异常。
    • HTTPRedirectHandler 用于处理重定向。
    • HTTPCookieProcessor 用于处理 Cookie 。
    • ProxyHandler 用于设置代理,默认代理为空。
    • HTTPPasswordMgr 用于管理密码,它维护了用户名密码的表。
    • HTTPBasicAuthHandler 用于管理认证,如果一个链接打开时需要认证,那么可以用它来解决认证问题。 另外还有其他的 Handler ,可以参考官方文档。

    https://docs.python.org/3/library/urllib.request.html#urllib.request.BaseHandler

     

    它们怎么来使用,不用着急,下面会有实例为你演示。
    另外一个比较重要的就是 OpenerDirector ,我们可以称之为 Opener ,我们之前用过 urllib.request.urlopen() 这个方法,实际上它就是一个 Opener 。
    那么为什么要引入 Opener 呢?因为我们需要实现更高级的功能,之前我们使用的 Request 、 urlopen() 相当于类库为你封装好了极其常用的请求方法,利用它们两个我们就可以完成基本的请求,但是现在不一样了,我们需要实现更高级的功能,所以我们需要深入一层,使用更上层的实例来完成我们的操作。所以,在这里我们就用到了比调用 urlopen() 的对象的更普遍的对象,也就是 Opener 。

    Opener 可以使用 open() 方法,返回的类型和 urlopen() 如出一辙。那么它和 Handler 有什么关系?简而言之,就是利用 Handler 来构建 Opener 。
    认证
    我们先用一个实例来感受一下:

     

     

    import urllib.request
    auth_handler = urllib.request.HTTPBasicAuthHandler()
    auth_handler.add_password(realm='PDQ Application',
                              uri='https://mahler:8092/site-updates.py',
                              user='klem',
                              passwd='kadidd!ehopper')
    opener = urllib.request.build_opener(auth_handler)
    urllib.request.install_opener(opener)
    urllib.request.urlopen('http://www.example.com/login.html')

     


    此处代码为实例代码,用于说明 Handler 和 Opener 的使用方法。在这里,首先实例化了一个 HTTPBasicAuthHandler 对象,然后利用 add_password() 添加进去用户名和密码,相当于建立了一个处理认证的处理器。
    接下来利用 urllib.request.build_opener() 方法来利用这个处理器构建一个 Opener ,那么这个 Opener 在发送请求的时候就具备了认证功能了。接下来利用 Opener 的 open() 方法打开链接,就可以完成认证了。
    代理

     

    如果添加代理,可以这样做:

     

    import urllib.request
    proxy_handler = urllib.request.ProxyHandler({
    'http': 'http://218.202.111.10:80',
    'https': 'https://180.250.163.34:8888'
    })
    opener = urllib.request.build_opener(proxy_handler)
    response = opener.open('https://www.baidu.com')
    print(response.read())

     


    此处代码为实例代码,用于说明代理的设置方法,代理可能已经失效。

     

     

     

     

    在这里使用了 ProxyHandler , ProxyHandler 的参数是一个字典,key是协议类型,比如 http 还是 https 等,value是代理链接,可以添加多个代理。
    然后利用 build_opener() 方法利用这个 Handler 构造一个 Opener ,然后发送请求即可。

     

    Cookie设置

    我们先用一个实例来感受一下怎样将网站的 Cookie 获取下来。

    import http.cookiejar, urllib.request
    cookie = http.cookiejar.CookieJar()
    handler = urllib.request.HTTPCookieProcessor(cookie)
    opener = urllib.request.build_opener(handler)
    response = opener.open('http://www.baidu.com')
    for item in cookie:
       print(item.name+"="+item.value)

    首先我们必须声明一个 CookieJar 对象,接下来我们就需要利用 HTTPCookieProcessor 来构建一个 handler ,最后利用 build_opener 方法构建出 opener ,执行 open() 即可。

    运行结果如下:

    BAIDUID=2E65A683F8A8BA3DF521469DF8EFF1E1:FG=1
    BIDUPSID=2E65A683F8A8BA3DF521469DF8EFF1E1
    H_PS_PSSID=20987_1421_18282_17949_21122_17001_21227_21189_21161_20927
    PSTM=1474900615
    BDSVRTM=0
    BD_HOME=0

     

    可以看到输出了每一条 Cookie 的名称还有值。
    不过既然能输出,那可不可以输出成文件格式呢?我们知道很多 Cookie 实际也是以文本形式保存的。
    答案当然是肯定的,我们用下面的实例来感受一下:

     

    filename = 'cookie.txt'
    cookie = http.cookiejar.MozillaCookieJar(filename)
    handler = urllib.request.HTTPCookieProcessor(cookie)
    opener = urllib.request.build_opener(handler)
    response = opener.open('http://www.baidu.com')
    cookie.save(ignore_discard=True, ignore_expires=True)

     

    这时的 CookieJar 就需要换成 MozillaCookieJar ,生成文件时需要用到它,它是 CookieJar 的子类,可以用来处理 Cookie 和文件相关的事件,读取和保存 Cookie ,它可以将 Cookie 保存成 Mozilla 型的格式。
    运行之后可以发现生成了一个 cookie.txt 文件。

     

    内容如下:

    # Netscape HTTP Cookie File
    # http://curl.haxx.se/rfc/cookie_spec.html
    # This is a generated file! Do not edit.
    .baidu.com TRUE / FALSE 3622386254 BAIDUID 05A
    E39B5F56C1DEC474325CDA522D44F:FG=1
    .baidu.com TRUE / FALSE 3622386254 BIDUPSID 05
    AE39B5F56C1DEC474325CDA522D44F
    .baidu.com TRUE / FALSE H_PS_PSSID 19638_1453
    _17710_18240_21091_18560_17001_21191_21161
    .baidu.com TRUE / FALSE 3622386254 PSTM 147490
    2606
    www.baidu.com FALSE / FALSE BDSVRTM 0
    www.baidu.com FALSE / FALSE BD_HOME 0


    另外还有一个 LWPCookieJar ,同样可以读取和保存 Cookie ,但是保存的格式和 MozillaCookieJar 的不一样,它会保存成与libwww-perl的Set-Cookie3文件格式的 Cookie 。
    那么在声明时就改为

     

    cookie = http.cookiejar.LWPCookieJar(filename)

     

     

    #LWP-Cookies-2.0
    Set-Cookie3: BAIDUID="0CE9C56F598E69DB375B7C294AE5C591:FG=1"; path="/"; domain=".baidu.com"; path_spec; domain_dot; expires="208
    4-10-14 18:25:19Z"; version=0
    Set-Cookie3: BIDUPSID=0CE9C56F598E69DB375B7C294AE5C591; path="/"; domain=".baidu.com"; path_spec; domain_dot; expires="2084-10-1
    4 18:25:19Z"; version=0
    Set-Cookie3: H_PS_PSSID=20048_1448_18240_17944_21089_21192_21161_20929; path="/"; domain=".baidu.com"; path_spec; domain_dot; di
    scard; version=0
    Set-Cookie3: PSTM=1474902671; path="/"; domain=".baidu.com"; path_spec; domain_dot; expires="2084-10-14 18:25:19Z"; version=0
    Set-Cookie3: BDSVRTM=0; path="/"; domain="www.baidu.com"; path_spec; discard; version=0
    Set-Cookie3: BD_HOME=0; path="/"; domain="www.baidu.com"; path_spec; discard; version=0
    
    
    

     


    生成的内容如下:由此看来生成的格式还是有比较大的差异的。
    那么生成了 Cookie 文件,怎样从文件读取并利用呢?
    下面我们以 LWPCookieJar 格式为例来感受一下:

     

     

     

     

    cookie = http.cookiejar.LWPCookieJar()
    cookie.load('cookie.txt', ignore_discard=True, ignore_expires=True)
    handler = urllib.request.HTTPCookieProcessor(cookie)
    opener = urllib.request.build_opener(handler)
    response = opener.open('http://www.baidu.com')
    print(response.read().decode('utf-8'))

     

     

    前提是我们首先利用上面的方式生成了 LWPCookieJar 格式的 Cookie ,然后利用 load() 方法,传入文件名称,后面同样的方法构建 handler 和 opener 即可。
    运行结果正常输出百度网页的源代码。

     

     

     

    好,通过如上用法,我们可以实现绝大多数请求功能的设置了。
    发出请求之后,如果遇到异常怎么办?在下一节,我们会讲解一下有关异常处理的
    流程。

     

     

    展开全文
  • postman 发送json请求

    万次阅读 多人点赞 2015-08-19 15:29:00
    简介: postman是一个很好的http模拟器,在开发rest服务时是很好用的工具,可以发送get、post、put等各种请求。 发送json的具体步骤: ...2、设置body的编码方式为raw,application/json, raw是发送
  • 最近使用vue框架开发项目的时候, 遇到了一个问题,其实这个问题在之前就已经遇到过,不过因为当时没有时间,所以采用了另外一...vue开发的时候,使用axios跨域发送请求,同时post发送的数据格式是json格式,发送出...
  • Java发送邮件

    万次阅读 2018-07-28 21:51:35
    通过查阅以及实验,很快的写了个发送邮件的功能;现在整理一下记录下来。 【发送邮件】 一、在pom中引入相关依赖 <dependency> <groupId>javax.mail</groupId> <artifa...
  • Python实现自动发送邮件 -- 自动发送资料到博客/网站中留言的邮箱背景脚本要解决的问题:实现方法代码及解析1. AutoSendEmailByBlogComments.py2. FileOperation.py3. SendEmail.py 背景 在各大论坛、博客上,大部分...
  • 使用Excel和OutLook实现自动发送邮件

    万次阅读 多人点赞 2018-01-25 00:33:26
    我们在平时工作中可能会遇到要批量发送邮件的工作,例如财务部根据每个人的工资情况发放工资条到个人的邮箱,因为邮件因人而异,所以需要完成大量而细致的工作,工作繁琐,重复劳动多,非常适合批量发送。...
  • 微信公众号开发之接收与发送消息

    万次阅读 2018-12-28 22:04:14
    说明:该篇博客是博主一字一码编写的,实属不易,请... 公众号接收与发送消息 验证URL有效性成功后即接入生效,成为开发者。如果公众号类型为服务号(订阅号只能使用普通消息接口),可以在公众平台网站中申请认证...
  • TCP数据发送发送窗口

    千次阅读 2019-02-26 00:07:12
    TCP的发送过程由滑动窗口控制,而滑动窗口的大小受限于发送窗口和拥塞窗口,拥塞窗口由拥塞控制算法的代表,而发送窗口是流量控制算法的代表,这篇笔记记录了发送窗口相关的内容,包括发送窗口的初始化、更新、以及...
  • Java实现邮件发送

    万次阅读 多人点赞 2019-07-20 16:03:19
    Java实现邮件发送 一、邮件服务器与传输协议 要在网络上实现邮件功能,必须要有专门的邮件服务器。这些邮件服务器类似于现实生活中的邮局,它主要负责接收用户投递过来的邮件,并把邮件投递到邮件接收者的电子邮箱...
  • 1、最近公司实现部分数据统计、分析的报表进行每天定时发送到相关人员的邮箱之中的配置代码被人为删除了,需要重新恢复该功能,由于原先是在linux上使用shell配置发送,实在是太繁琐,所以准备使用python来实现该...
  • 同一个文件,发送钉钉就完全没有问题,发送微信却总提示“文件占用,无法发送” 可能是微信尚不支持以文件方式发送, 解决方案 右击文件夹–发送到–压缩文件夹–将压缩文件发送到微信 (其实就是压缩一下,将压缩...
  • Python进阶(四十六)-Python3实现SMTP发送邮件详细教程

    万次阅读 多人点赞 2017-04-26 22:13:21
    Python进阶(四十六)-Python3实现SMTP发送邮件详细教程简介  Python发送邮件的教程本人在网站搜索的时候搜索出来了一大堆,但是都是说了一大堆原理然后就推出了实现代码,我测试用给出的代码进行发送邮件时都不成功...
  • 我们在用串口发送数据的时候首先将待发送的数据/符号转换为对应的ASCII码,然后将这些ASCII码按照二进制的方式一位一位地发送出去。 (注:以下图片来自https://blog.csdn.net/wityy/article/details/8234739) ...
  • Python发送邮件

    千次阅读 2018-11-03 15:40:13
    之前在《Java基础(八):发送邮件》博文中实践了如何利用Java程序去发送邮件,最近自己用Python写了几个小程序;结合自己在工作中经常有在服务器上发邮件的需求,Linux对Python的支持没得说,于是自己通过在网上...
  • Python 技术篇-邮件写入html代码,邮件发送表格,邮件发送超链接,邮件发送网络图片。 邮件可以发送html,那我们就能做个表格出来,做个超链接出来,网上找个图片发出来。 重点是:`message = MIMEText(html_text, '...
  • 最近写的一个通信框架中有两种最基本的消息发送方式:同步发送和异步发送。同步方式:消息的发送方发A送一条消息到接收端B,B收到消息之后需要对消息进行处理,然后发送ACK确认消息回A,A收到B的ACK之后就可以认为这...
  • 基于JavaMail的Java邮件发送:复杂邮件发送

    万次阅读 多人点赞 2016-06-20 23:11:52
    上一篇文章 基于 JavaMail 的 Java 邮件发送:简单邮件发送 讲解了邮件的基本协议,JavaMail组件,创建并发送一封简单邮件的详细步骤。本文将介绍如何创建并发送一封包含图片和附件的复杂邮件。 一封复杂的邮件内容...
  • RocketMQ一行代码造成大量消息发送失败

    万次阅读 多人点赞 2020-05-18 21:01:58
    1、问题现象 首先接到项目反馈使用 RocketMQ 会出现如下错误...由于项目组并没有对消息发送失败做任何补偿,导致丢失消息丢失,故需要对这个问题进行深层次的探讨,并加以解决。 2、问题分析 首先我们根据关键字:TIMEO
  • Java发送邮件的几种方式

    万次阅读 多人点赞 2017-05-31 16:21:55
    最近的一个项目中用到了邮件发送,所以研究了一下。将其总结下来。 要发送邮件就要用到java中的JavaMail,关于JavaMailAPI的详解呢在 (http://blog.csdn.net/imain/article/details/1453677“)中有非常详尽的介绍,...
  • 一般流程就是你输入你的邮箱和设置你的密码,点击发送后,就会发给你一个验证码,你输入正确的验证码,然后点击提交后才真正的完成了注册 比如: 最下面会附上源码,不懂的可以加超厂长微信:y958231955或微信...
  • 发送的数据是0x21。0b0010,0001 刚开始一个拉低,代表数据开始传输。 然后先发送低位,后发送高位。111101000,01001111111
  • python的requests发送/上传多个文件

    万次阅读 热门讨论 2017-07-10 13:45:32
    在requests中发送文件的接口只有一种,那就是使用requests.post的files参数, 请求形式如下: url = "http://httpbin.org/post" data = None files = { ... } r = requests.post(url, data, files=f...
  • Python3 - 实现邮件的发送

    万次阅读 2018-12-10 17:22:39
    一、 使用SMTP模块发送邮寄 二、 发送HTML格式右键 三、发送带附件的邮件 四、Django处理邮件的发送 4-1 settings配置 4-2 授权码的获取  4-3 视图函数 4-4 群发邮件(一次发送多份) 4-5 携带附件或发送...
  • 利用java实现发送邮件

    万次阅读 多人点赞 2017-11-14 15:01:31
    很显然这些应用不可能和我们自己平时发邮件一样,先打开浏览器,登录邮箱,创建邮件再发送。本文将简单介绍如何通过 Java 代码来创建电子邮件,并连接邮件服务器发送邮件。电子邮件协议 电子邮件在网络中传输和网页...
  • 现假定A开始发送一帧,并且在A发送结束之前B也发送一帧。如果A发送的是以太网所容许的最短的帧,那么A在检测到和B发生碰撞之前能否把自己的数据发送完毕?换言之,如果A在发送完毕之前并没有检测到碰撞,那么能否...
  • MirrorOpSend+MirrorOpReceiver(MirrorOp发送端+接收端)破解无时间限制版,包含了手机和电脑的发送端、接收端全部软件,超值! 无线传屏电脑发送端WinMirrorOpSender 无线传屏电脑接收端WinReceiver 无线传屏...

空空如也

1 2 3 4 5 ... 20
收藏数 608,148
精华内容 243,259
关键字:

发送