1、准备好工具并配置好工具

打开IE9,打开百度首页(www.baidu.com),然后按F12,调出F12开发人员工具,再切换到Network界面:

在调试之前,先去做一些配置上的准备工作:

(1)设置网页跳转时,已抓取的数据不被清除掉:

取消控制台和网络前面的对号,这样在网页分析过程中,由于从一个页面跳转到另外一个页面,所抓取的到内容,就不会被清空掉了。

(2)清除旧的cookie和缓存

为了后续的调试,不被之前的已登陆的账户的(缓存和cookie等)信息所影响,所以去都清除掉:

其中,简单解释一下:

A. 2个和清除cookie有关的:

缓存->清除会话 Cookie(S):清除当前会话,即访问当前这么一堆网页所涉及的cookie

缓存->清除域的 Cookie(O):清楚和当前网页所属的domain,此处为和baidu.com相关的cookie

B. 2个和缓存cache(即缓存的网页)有关的:

为了清除的更彻底,也顺带把cache都清理了

2、模拟操作过程,利用工具抓取所需的整个过程

点击“登陆”:


       可以看到,除了网页中跳出你所熟悉的登陆对话框之外,F12调试窗口中,就已经抓取到很多内容了。

然后输入用户名和密码,正常登陆,就可以看到网页调转到了http://www.baidu.com/index.php,并在对应的F12调试窗口中抓取到了很多内容:

好了,到此为止,我们的操作,基本就结束了。剩下的,就是从我们所已经抓取到的信息中,找到是如何登陆的。

3、分析网站登陆的内部逻辑过程

1. 找到登陆网站所涉及的最核心的地址

对于熟悉的人,可以直接从那一堆的url中,找到哪个是登陆的页面。而现在假定你不熟悉,教你如何找到真正的有价值的信息。对于此处,我们可以想到的一种办法是,通过直接搜索密码,而搜到哪里发送了我们的密码:

【小提示:显示内容时,设置为 自动换行】

当抓取出来的Request Body,Response Body等部分的内容中,单行内容太长,一行显示不下,不方便查看时,可以点击右键,选择Word wrap:

即可实现自动换行显示的效果了,方便查看了:

此处,很容易看到,此处和我们密码相关的url地址为https://passport.baidu.com/v2/api/?login,即以后如果想要写代码的话,所要访问的url地址,就是这个地址了。

2. 分析所提交的数据(post data)中的参数和值

此处的Request Body,就是对应的http的POST请求中所要提交的数据,简称为post data。

此处Request Body中完整的数据为:

ppui_logintime=6852&charset=utf-8&codestring=&token=5ab690978812b0e7fbbe1bfc267b90b3&isPhone=false&index=0&u=&safeflg=0&staticpage=http%3A%2F%2Fwww.baidu.com%2Fcache%2Fuser%2Fhtml%2Fjump.html&loginType=1&tpl=mn&callback=parent.bdPass.api.login._postCallback&username=crifan&password=xxxxxx&verifycode=&mem_pass=on

处理后变为:

ppui_logintime=6852
charset=utf-8
codestring=
token=5ab690978812b0e7fbbe1bfc267b90b3
isPhone=false
index=0
u=
safeflg=0
staticpage=http%3A%2F%2Fwww.baidu.com%2Fcache%2Fuser%2Fhtml%2Fjump.html
loginType=1
tpl=mn
callback=parent.bdPass.api.login._postCallback
username=crifan
password=xxxxxx
verifycode=
mem_pass=on

很明显,此处就是模拟网站登录的核心数据了,是在写代码时,对于url=https://passport.baidu.com/v2/api/?login提交POST请求时,所有要发送的一些参数和值了。


      此处,再重新简要的介绍一下,模拟登陆网站的基本逻辑:

想要模拟网站登陆,就要知道,要向什么url地址,发送什么样的数据,GET请求还是POST请求。

● GET请求只从服务器请求数据,不需要所谓的post data,但是往往需要在url后面添加上对应的?para1=val1&para2=value2之类的形式,此部分叫做query parameter,其本质上,有点类似于post data
      ● POST请求,在发送请求时,还需要提供对应的post data,此处即对应着IE9的F12中的Request Body。而余下的,发送请求时的其他相关参数设置,主要就是设置很多基本的参数,包括user-agent等,此处对应着那个Request Headers

而提交请求后,网站的服务器会给你反馈,返回数据和信息给你。此处对应的就是Response Headers和Response Body。

经常地,其中还涉及到cookie等信息。在发送之前,准备好,发送给服务器,服务器返回的信息中,往往也包含,更新后,cookie的值。

接下来,就是分析,如何获得所需的信息。


      先分析上述的post data中的值都是怎么来的。分析值是如何来的,以及顺带说说,写代码时,如何设置这些值。

在此之前,先解释一下,在代码中关于如何设置这些参数的值的规律和经验:

(1)对于有参数,但是值为空的哪些参数,一般来说,都是可以省略的。即写代码时,是可以去掉,忽略掉,这些参数的。当然,如果你抓取出来的参数是有值的,则需要考虑其值是怎么得到的,是否有意义,否则随便忽略掉某些参数,可能会导致模拟登陆失败的。

(2)对于看不太懂的参数的值的情况下,不妨先使用抓取出来的数据。尤其是一些参数,看不太懂,而且其值又明显不是那种,很可能会变化的数字之类的值,则一般情况下,也都是固定的值,所以,即使对于参数和值本身不太了解,也无所谓,也都可以直接在代码中,直接使用抓取出来的数据即可。即使会导致出错,一般来说,也可以通过后续的多次抓取和分析,看出来该值真正的规律。

在上面那一堆参数和值中:

(1)一些很明显,是固定的值,不需要考虑太多的值有:

charset=utf-8 -> 表示当前网页的编码是utf-8,我们写代码照着写即可,不需要改;
            codestring=  -> 此处为空,所以也可以不理会;
            isPhone=false -> 很明显,此处是通过PC登陆百度的,不是通过手机类的移动设备登陆的,所以是false。所以写代码时,也设置为false即可;

细心的读者,也很容易回想起,此处是对应着之前的登陆界面中的“手机登陆”:

如果是我们是通过“手机登陆”百度时,不出意外的话,肯定参数是isPhone=true

index=0 -> 未知,但是也没看出来是什么含义,所以也直接设置为0即可;
            u=-> 空值,同样设置空值即可;
            safeflg=0 -> 未知,所以也可以暂且不管,同样设置为0即可。
            username=crifan -> 很明显,是我们的账号,不多解释;
            password=xxxxxx -> 同理,是对应的密码;
            verifycode= ->此处为空,所以也可以不管; 
            mem_pass=on -> 很明显,是memory password的所写,即记住密码,对应的页面是,我们已经勾选的"记住我的登陆状态":

(2)另外一些就是不太容易一眼就看出来的值,需要简单解释一下的:

staticpage=http%3A%2F%2Fwww.baidu.com%2Fcache%2Fuser%2Fhtml%2Fjump.html –> http://www.baidu.com/cache/user/html/jump.html
            loginType=1 -> 未知,但是一般不知道的值,都可以先按照原先的值去设置即可;
            tpl=mn -> 未知,也还是同样设置即可;
            callback=parent.bdPass.api.login._postCallback -> 未知,也同样设置即可;

(3)再剩下的,就是需要去分析调查,才知道为何是这样值的了:

ppui_logintime=6852

此值6852,看起来就像是会变化的。但是到底如何得到的,则需要去分析分析了。

所以就去搜索6852,经过搜索,发现结果只能搜到此单独一处的6852,貌似没办法找到此数据如何得到的。再搜其参数ppui_logintime,然后另外在别的文件中也可以找到2处,其中一处是:

很明显,此处是javascript脚本https://passport.baidu.com/js/pass_api_login.js?v=20121018在其中根据实际情况计算出来的。


     【小提示:对于参数的处理策略】

对于涉及的很多参数,总的说,有两种策略:

一是,直接忽略此值,暂时不管。因为很多时候,有些参数,至少是这样看起来,不是那么重要的参数(重要的参数,相信我不说你自己也能看出来,是那些username,password之类的参数)。然后就去写程序去模拟了。而真的等到程序运行出错,服务器没有返回你所期望的信息的时候,再回来分析此参数,看看是不是这个参数所导致的。然后再试图去分析其真正的值;

二是,继续分析,甚至调试javascript代码,以便找到此值到底是如何一点点计算出来的。此过程可能会极其繁琐,也可能相对简单。要取决于此值被计算出来所经历的过程的复杂度。

此处,在表面看起来,这个参数ppui_logintime,大概意思是登陆的时间,所以推测是服务器为了记录你本地登陆百度的时间,和能否登陆百度这个过程本身,应该不会产生根本的影响,所以此处就可以采用策略一,暂时忽略不管。万一真的有影响,再回来继续分析也不迟。


token=5ab690978812b0e7fbbe1bfc267b90b3

此值5ab690978812b0e7fbbe1bfc267b90b3,很明显,是需要从别的地方找到的。所以就去分析此值是如何来的。

同理,继续去搜5ab690978812b0e7fbbe1bfc267b90b3,然后是可以搜到的,然后通过点击搜索框中的向前和向后的按钮,是可以找到这个2/68 条记录,对应url:https://passport.baidu.com/v2/api/?getapi&class=login&tpl=mn&tangram=true 的这处的:

对于上述所搜到的内容,很明显可以看出,就是我们在通过网页登陆百度首页过程中,通过IE9的F12抓取出来的记录知道了,其内部还是会先去访问:https://passport.baidu.com/v2/api/?getapi&class=login&tpl=mn&tangram=true,然后会获得Response Body,即(服务器所返回的)html源码,其中包括了:

bdPass.api.params.login_token='5ab690978812b0e7fbbe1bfc267b90b3';

此时,你应该就明白了,到时候我们去写代码时,想要获得上述token的值的话,就需要先去对url=https://passport.baidu.com/v2/api/?getapi&class=login&tpl=mn&tangram=true发送GET请求,获得对应的html代码,然后从中分析出token的值5ab690978812b0e7fbbe1bfc267b90b3;

而写到此,基本逻辑过程,也相对清楚了。

但是有人很快会想到,即使上述代码写出来了,又如何能确保的确已经模拟登陆成功了,即如何验证此处模拟登陆百度首页成功了呢?

此处,根据经验,主要通过两方面来验证:

一是返回的html代码,即对应着F12中的Response Body。
           二是cookie,如果你成功登陆了服务器,那么其所返回的值中中,对于cookie,一般都是会有对应的,和成功登陆有关的新的cookie返回给你的,以及另外更新一些原先发送的一些cookie的值。

此处,需要特别提示一句,如果你在最开始没有去清除cookie,则很可能看到的cookie结果是这样的:

即,登陆前后的cookie,都有BDUSS,PTOKEN,STOKEN,SAVEUSERID。

这是因为,之前通过别的账号,以及同样的账号登陆过,所以IE9已经在本地记录了相关的cookie。所以,在访问该url时,能看到Sent中已经存在了类似的cookie。

总的来说,可以通过返回的html和cookie,来验证是否登录成功了。

而一般来说,通过验证cookie,是最有效的。因为很多时候,某些网站登陆成功和登陆失败,所显示的页面可能是同一个。但是登陆成功的话,基本都会有对应的,新的,和登陆有关的cookie,返回的。


一般来说,实际上,对于很多不是很复杂的网站,到这一步,就完全就够了,就能够成功模拟登陆了。

但是,后来经过代码的证实,如上的流程,实际上是行不通的,因为对于去访问:https://passport.baidu.com/v2/api/?getapi&class=login&tpl=mn&tangram=true,实际上,返回的html是:

var bdPass=bdPass||{};
bdPass.api=bdPass.api||{};
bdPass.api.params=bdPass.api.params||{};
bdPass.api.params.login_token='the fisrt two args should be string type:0,1!';
bdPass.api.params.login_tpl='mn';
document.write('<script type="text/javascript" charset="UTF-8" src="https://passport.baidu.com/js/v2ApiUsedTangramFunctions.js?v=20121018"></script>');
document.write('<script type="text/javascript" charset="UTF-8" src="https://passport.baidu.com/js/pass_api_login.js?v=20121018"></script>');

其中的:bdPass.api.params.login_token=’the fisrt two args should be string type:0,1!’;是无法正确获得我们所需要耳朵token的值的。

所以,接下来,就是继续去想办法,找到此处没有正确获得返回的html的原因。

不过,首先要知道的,无论何时,从抓取出来的数据来看,只要你程序是完整模拟了整个浏览器所发送的所有的数据,此处即IE9所发送的request headers和post data(Request body),那么,程序所获得的返回值,就应该也和所抓取到的数据一样,即应该就可以从返回的html(response body)中获得所需的token的值了。

而此处之所有没有获得,对照上述所抓取的数据去看,则很可能是,request headers中某些值,比如cookie值,referer等值,没有赋值正确,导致返回的html不对。

所以,接下来,就是想办法,尝试一点点,完全找到上那些cookie的值,referer等的值,都是从哪里来的。

好的,现在接着就以其中一个最复杂的cookie:SAVEUSERID,来说明,到底是如何分析出来的,找到最开始的SAVEUSERID是从哪里来的。所能想到的,就是先去搜索SAVEUSERID。并且,是利用从最开始的地方,往后去搜索:

然后搜出来,第一个出现的位置是:

可以看到,当前的url地址是 16/80,即一共抓取到了80条记录,现在这个是第16个。

这意味着,在此之前15条记录中,都没有出现SAVEUSERID,而第一次出现此SAVEUSERID,是在第16条记录,url地址为:

https://passport.baidu.com/img/v2/small_blank.gif

切换到summary view,更加容易看清整个抓取的记录的情况:

但是,此处,没有出现我们所期待的,是访问某个url,然后返回的cooki中,包括了此SAVEUSERID。


【小提示:cookie的生成和出现的时机】

所谓cookie,一般都是访问服务器,然后服务器返回的cookie。然后之后才是访问其他的,后续的地址时,sent这些cookie,然后后续访问的地址所返回的信息中,可能会再次更新值/删除本地的,旧的cookie,以及也可能返回新的,增加新的本地的cookie。

甚至是第一次出现此cookie,是代码本身,比如javascript中通过代码生成的cookie。

但是无论如何,一般第一次出现一个cookie,最常见的都是服务器返回的cookie。其实少数可能是由期间运行到的javascript代码所生成的。

所以,接下来,还要想办法,找到此cookie,SAVEUSERID,第一次出现是在什么时候。

对此,需要用到我之前自己折腾的时候所学到的经验。

是去查看本地缓存文件和cookie中,看看是否有之前所保存的cookie。

因为,之前通过IE9的F12所执行的删除cookie时,其虽然的确删除了“当前domain”的cookie,但是对于非当前domain的cookie,并没有删除掉。即,本来是通过点击Cache->Clear session cookies  Cache->Clear cookies for domain打算删除掉关于百度的所有的cookie的,结果,实际上,此时只去删除了当前domian=www.baidu.com所相关的cookie,而对于passport.baidu.com,由于和www.baidu.com不是同一个domain,所以,本地那些passport.baidu.com相关的cookie,还是存在的,所以通过自己手动找到电脑中放cookie和缓存的地方,还是可以找到 Cookie:cli@passport.baidu.com/ 的:

这里,才是真正SAVEUSERID所在的位置,而为何之前找不到SAVEUSERID第一次出现的位置,是因为此处本地域名(domain)为passport.baidu.com所对应的cookie中包含了SAVEUSERID,没有被删除掉,所以,上述IE9的F12抓取到的第一次出现的SAVEUSERID的时候,就已经有了值,且在访问地址https://passport.baidu.com/v2/api/?getapi&class=login&tpl=mn&tangram=true 时使用了。

此时,知道原因了,那就可以再去去完整清除所有的cookie和cache了。


      重新登录后再去重新搜索想要分析的SAVEUSERID,然后搜索出来的第一个是:

很明显,其就是我们所期望的了,因为其中的SAVEUSERID是通过Set-Cookie所获得的,是访问服务器的某个地址(https://passport.baidu.com/v2/api/?login),然后服务器所返回的cookie值。

如此,我们才能有机会,去写代码,去模拟访问此路径,获得此cookie,然后有后续的可能,利用此cookie,访问后续其他地址,最后完整模拟登陆网站的全部过程的。

而相应的,访问https://passport.baidu.com/v2/api/?login之前,需要用到BAIDUID。所以又用同样的分析方法,去找到BAIDUID这个cookie的最开始的来源(又重新打开浏览器,重新分析了一次):

因此,即为:

先访问http://www.baidu.com/去获得对应的BAIDUID
      接着去访问https://passport.baidu.com/v2/api/?login,其中发送的数据中,包括BAIDUID,返回数据中,得到SAVEUSERID。

而此时,其实访问https://passport.baidu.com/v2/api/?login本身就是我们所追求的目标,模拟登陆百度。

所以后续的SAVEUSERID,其实此处是可以不用,只是去通过校验cookie,而验证登陆是否成功时,会涉及到而已。


      然后再去回头看之前所说的:https://passport.baidu.com/v2/api/?getapi&class=login&tpl=mn&tangram=true

再次重现抓取所看到的结果为:

就容易看懂了,即需要在访问:https://passport.baidu.com/v2/api/?getapi&class=login&tpl=mn&tangram=true时,提供BAIDUID这个cookie。

另外,再确认一下,访问:https://passport.baidu.com/v2/api/?login正确登陆时,所返回的cookie:

可见,其中至少包括:BDUSS,PTOKEN,STOKEN,SAVEUSERID(其中,对于原先域名为baidu.com的PTOKEN,是被删除掉的,此处暂可忽略)

3、总结模拟登陆网站的基本流程

至此,对于想要模拟登陆百度首页:http://www.baidu.com/的内部逻辑过程,基本上就很清楚了:

顺序 访问地址 访问类型 发送的数据 需要获得/提取的返回的值
1 http://www.baidu.com/ GET 返回的cookie中的BAIDUID
2 https://passport.baidu.com/v2/api/?getapi&class=login&tpl=mn&tangram=true GET 包含BAIDUID这个cookie 从返回的html中提取出token的值
3 https://passport.baidu.com/v2/api/?login

POST

一堆的post data,其中token的值是之前提取出来的 需要验证返回的cookie中,是否包含BDUSS,PTOKEN,STOKEN,SAVEUSERID

对于上述流程,按理来说,去使用代码,Python或C#等,去实现出来,即可。

不过,关于模拟登陆时所需要的数据,多解释一下。

按理来说,完整的模拟网站登陆的话,其实应该是从头到尾的,分析出浏览器(IE9)本身是如何访问网站的,然后把所有的逻辑搞懂,数据的来源都分析清楚,即如上述过程,对于访问https://passport.baidu.com/v2/api/?login所需要的那么一堆参数,都去搞懂具体的含义,以及参数的值,是怎么获得的。而实际上,很多时候,模拟网站登陆,或者是抓取网页信息的时候,只需要最关心的那些核心参数即可。因为,服务器,很可能,只是去判断那些核心参数,比如上述的username,password,及其他几个参数,然后就可以正确返回你所需要的信息,即html,cookie等,就可以成功实现模拟登陆的目的了。但是,话说回来,具体需要哪些,最基本的参数,还是需要通过写程序,去一点点测试出来的。而之所以给大家介绍上述的概念,目的是为了,在你觉得自己能看懂参数的大概含义的时候,很多时候,能看出该参数不要也无所谓的时候,那就可以先去测试基本的数,而暂时忽略其他相对次要的参数。由此,在一定程度上,提高做事情的效率而已。当然,在忽略参数的时候,也要注意,不要轻易忽略很多参数,否则也是很可能影响到程序模拟登陆的正确性的。具体的尺度的把握,就一点:根据情况而定,自己看着办。

【总结】

至此,关于模拟登陆网站,如何一步步的分析出内部逻辑过程,就完成了。

总结下来就是,先去用工具“录制”你所有的操作,然后再去利用工具去分析和登陆有关那些url的相关的信息,主要是post data有哪些参数,以及其值是如何获得的。

转载于:https://www.cnblogs.com/Ymete/archive/2012/12/31/2840474.html

(How to)使用IE9的F12开发人员工具分析模拟登陆网站(百度首页)的内部逻辑过程相关推荐

  1. 【教程】手把手教你如何利用工具(IE9的F12)去分析模拟登陆网站(百度首页)的内部逻辑过程

    声明:本文章转载自crifan的技术人生 [教程]手把手教你如何利用工具(IE9的F12)去分析模拟登陆网站(百度首页)的内部逻辑过程 重要提示: 1.此贴,以后不再更新: 2.想要看更新的内容,请移 ...

  2. IE调试网页之一:F12 开发人员工具简介

    F12 开发人员工具是可帮助生成和调试网页的一套工具. 编写出色的网页需要编码知识以及适当的工具来发现和调试难免会出现的问题.Windows Internet Explorer 9 提供所呈现代码的视 ...

  3. Win10系统 IE11浏览器调用F12开发人员工具,打开后底部显示空白

    Win10系统 IE11浏览器点击F12开发人员工具,打开后底部显示空白. 经过尝试发现该问题与安全选项里的一个策略有关系. 在安全选项里,找到"用户帐户控制:以管理员批准模式运行所有管理员 ...

  4. IE调试网页之六:使用 F12 开发人员工具调试 HTML 和 CSS (Windows)

    F12 开发人员工具可帮助你查找和修复 HTML 和级联样式表 (CSS) 代码中的错误.如果不使用这些工具,则可能很难在源代码中发现这些错误. 通过在 Windows Internet Explor ...

  5. F12 开发人员工具调试 HTML 和 CSS

    IE调试网页之六:使用 F12 开发人员工具调试 HTML 和 CSS (Windows) F12 开发人员工具可帮助你查找和修复 HTML 和级联样式表 (CSS) 代码中的错误.如果不使用这些工具 ...

  6. IE的F12开发人员工具不显示问题

    IE的F12开发人员工具不显示问题: 按下F12之后,开发人员工具在桌面上看不到,但是任务栏里有显示.将鼠标放在任务栏的开发人员工具上,出现一片透明的区域,选中之后却出不来.将鼠标移动到开发人员工具的 ...

  7. ie console f12开发人员工具

    由于学习了console的方便,所以在写js的时候,写console的时候,并没有删除这个测试代码,所以造成了在IE中不能正常运行Js的情况.但是当我打开ie的开发人员工具的时候,所有的代码就都运行正 ...

  8. F12开发人员工具如何使用、抓包、调试代码

    F12开发人员工具如何使用.抓包.调试代码 前言 提示:这里我给大家推荐360极速浏览器,这个版本的浏览器F12开发人员工具很强大,非常适合抓包和调试js代码,当然了其他的浏览器也是可以的,功能上基本 ...

  9. 测试人员如何使用浏览器的f12_如何使用 F12 开发人员工具调试网页

    本帖最后由 qin明月 于 2013-4-16 16:02 编辑 使用 F12 开发人员工具调试 JavaScript 错误 使用 F12 开发人员工具,Web 开发人员能够在无需离开浏览器的情况下快 ...

  10. 善于使用F12开发人员工具来快速调试js代码

    使用F12工具快速调试扣出的js代码 前言 该文章讲述,如何善于使用F12开发人员工具来高效的调试代码,这里以360极速浏览器为案例,并且推荐使用这款浏览器,非常高效. 一.打开浏览器,打开F12开发 ...

最新文章

  1. window与共享文件夹访问相关的四个服务
  2. SQL Server 2012笔记分享-52:可用性指标
  3. 【牛客 - 297D】little w and Exchange(上下界贪心)
  4. Bootstrap HTML编码语法规范
  5. Source InSight context 窗口丢失的解决办法
  6. java 最少使用(lru)置换算法_[内附完整源码和文档] 基于C#的可视化虚拟存储器管理(LUR算法)...
  7. 计算机无法进系统咋办,详解电脑无法进入系统怎么办
  8. Web开发入门型服务器使用心得
  9. H.265与H.264对比有哪些优势
  10. 【SELinux】vendor_file_contexts没有被编译到vendor/etc/selinux/路径下
  11. Pspice的文本输入方法
  12. 安装python与编译工具vs code(中文版)和pycharm(中文版)
  13. Spring的学习之路(必看)
  14. 老司机 iOS 周报 #41 | 2018-10-29
  15. 跨平台C++框架Qt教程:如何编写地图综合应用-点聚合|附源码
  16. CSS样式中字体大小,建议font-size使用em
  17. mysql 配置文件详解
  18. 易买网更多新闻代码_新闻 | 0515演员李沇熹结婚等更多资讯
  19. 前端基础之《ECMAScript 6(6)—数组》
  20. 深圳城市治理权限再放开,平安能做什么

热门文章

  1. 防止win10自动更新——指定一个错误的升级服务器地址
  2. python 打印下标和值
  3. linux设置进程优先级工具,linux性能分析优化之基础指标参数(3) - 进程优先级 cpu nice priority NI PR %ni %nice...
  4. 互联网吞噬传统书店:全球大批书店倒闭
  5. 使用dns-over-https 手动测试域名解析
  6. 天才小毒妃 第922章 把金针还给我
  7. OMG,史上最全的37个APP推广渠道来啦!
  8. 计算机编程的好处,青少年学习编程的好处有哪些?
  9. 解锁中智集团30平方米数据中心备受追捧的密码
  10. PS教程:如何拼图调色出高大上的作品