整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

(详解)从浏览器输入 URL 到页面展示过程发生了什

(详解)从浏览器输入 URL 到页面展示过程发生了什么?

荐阅读:

阿里架构师精选Nginx+Redis+Sping+SpringBoot源码级PDF文档分享

微服务+Docker完美教程,被阿里架构师汇集到这2份文档里面了!

引言

对于面试常问的从浏览器输入 URL 到页面展示过程发生了什么?,我想大家都或多或少能说出一二。但是,其实这个问题很有深度,而你是否回答的有深度,在很大程度上会影响到面试官对你的印象。

并且,网上各种资料都是浅尝辄止地讲解这个过程,经常会出现今天看到这个版本,明天看到另一个版本地情况。所以,这次我们就来深入浅出一下这整个过程~

一、Chrome 多进程架构

首先,在开始讲解整个过程前,我们需要认识一下 Chrome 多进程架构。因为,从浏览器输入 URL 到页面渲染的整个过程都是由 Chrome 架构中的各个进程之间的配合完成。

Chrome 的多进程架构:

  • 浏览器进程,它负责用户界面(地址栏、菜单等等)、子进程的管理(例如,进程间通信和数据传递)、存储等等
  • 渲染进程,它负责将接收到的 HTML 文档和 JavaScript 等转化为用户界面
  • 网络进程,它负责网络资源的请求,例如 HTTP请求、WebSocket 模块
  • GPU(图形处理器)进程,它负责对 UI 界面的展示
  • 插件进程,它负责对插件的管理

二、过程详解

2.1 解析输入

发生这个过程的前提,用户在地址栏中输入了 URL,而地址栏会根据用户输入,做出如下判断:

  • 输入的是非 URL 结构的字符串,则会用浏览器默认的搜索引擎搜索该字符串
  • 输入的是 URL 结构字符串,则会构建完整的 URL 结构,浏览器进程会将完整的 URL 通过进程间通信,即 IPC,发送给网络进程

2.2 请求过程

在网络进程接收到 URL 后,并不是马上对指定 URL 进行请求。首先,我们需要进行 DNS 解析域名得到对应的 IP,然后通过 ARP 解析 IP 得到对应的 MAC(Media Access Control Address)地址。

域名是我们取代记忆复杂的 IP 的一种解决方案,而 IP 地址才是目标在网络中所被分配的节点。MAC 地址是对应目标网卡所在的固定地址。

1. DNS 解析

而 DNS 解析域名的过程分为以下几个步骤:

  • 询问浏览器 DNS 缓存
  • 询问本地操作系统 DNS 缓存(即查找本地 host 文件)
  • 询问 ISP(Internet Service Provider)互联网服务提供商(例如电信、移动)的 DNS 服务器
  • 询问根服务器,这个过程可以进行递归和迭代两种查找的方式,两者都是先询问顶级域名服务器查找

2. 通信过程

首先,建立 TCP 连接,即三次握手过程

  • 客户端发送标有 SYN 的数据包,表示我将要发送请求。
  • 服务端发送标有 SYN/ACK 的数据包,表示我已经收到通知,告知客户端发送请求。
  • 客户端发送标有 ACK 的数据包,表示我要开始发送请求,准备被接受。



然后,利用 TCP 通道进行数据传输

  • 服务端接收到数据包,并发送确认数据包已收到的消息到客户端,不断重复这个过程
  • 客户端在发送一个数据包后,未接收到服务端的确定消息,则重新发送该数据包,即 TCP 的重发机制
  • 当接收完所有的数据包后,接收端会按照 TCP 头中的需要进行排序,形成完整的数据

最后,断开 TCP 连接,即四次握手过程

  • 客户端发送请求,申请断开连接,进入等待阶段,此时不会发送数据,但是会继续接收数据。
  • 服务端接收请求后,告知客户端已明白,此时服务端进入等待状态,不会再接收数据,但是会继续发送数据。
  • 客户端收到后,进入下一阶段等待。
  • 服务端发送完剩余的数据后,告知客户端可以断开连接,此时服务端不会发送和接收数据。
  • 客户端收到后,告知服务端我开始断开连接。
  • 服务端收到后,开始断开连接。



而这整个过程的客户端则是网络进程。并且,在数据传输的过程还可能会发生的重定向的情况,即当网络进程接收到状态码为 3xx 的响应报文,则会根据响应报文首部字段中的 Location 字段的值进行重新向,即会重新发起请求

3. 数据处理

当网络进程接收到的响应报文状态码,进行相应的操作。例如状态码为 200 OK 时,会解析响应报文中的 Content-Type 首部字段,例如我们这个过程 Content-Type 会出现 application/javascript、text/css、text/html,即对应 Javascript 文件、CSS 文件、HTML 文件。

详细的 MIME 类型讲解可以看 MDN

2.3 创建渲染进程

当前需要渲染 HTML 时,则需要创建渲染进程,用于后期渲染 HTML。而对于渲染进程,如果是同一站点是可以共享一个渲染进程,例如 a.abc.com 和 c.abc.com 可以共享一个渲染渲染进程。否则,需要重新创建渲染进程

需要注意的是,同站指的是顶级域名二级域名相等

2.4 开始渲染

在创建完渲染进程后,网络进程会将接收到的 HTML、JavaScript 等数据传递给渲染进程。而在渲染进程接收完数据后,此时用户界面上会发生这几件事:

  • 更新地址栏的安全状态
  • 更新地址栏的 URL
  • 前进后退此时 enable,显示正在加载状态
  • 更新网页



2.5 渲染过程

大家都知道页面渲染的过程也是面试中单独会考的点,并且时常会由这个点延申出另一个问题,即如何避免回流和重绘。

渲染过程,是整个从理器输入 URL 到页面渲染过程的最后一步。而页面渲染的过程可以分为 9 个步骤:

  • 解析 HTML 生成 DOM 树
  • 解析 CSS 生成 CSSOM
  • 加载或执行 JavaScript
  • 生成渲染树(Render Tree)
  • 布局
  • 分层
  • 生成绘制列表
  • 光栅化
  • 显示

2.5.1 构建 DOM 树

由于网络进程传输给渲染进程的是 HTML 字符串,所以,渲染进程需要将 HTML 字符串转化成 DOM 树。例如:



需要注意的是这个 DOM 树不同于 Chrome-devtool 中 Element 选项卡的 DOM 树,它是存在内存中的,用于提供 JavaScript 对 DOM 的操作。

2.5.2 构建 CSSOM

构建 CSSOM 的过程,即通过解析 CSS 文件、style 标签、行内 style 等,生成 CSSOM。而这个过程会做这几件事:

  • 规范 CSS,即将 color: blue 转化成 color: rgb() 形式,可以理解成类似 ES6 转 ES5 的过程
  • 计算元素样式,例如 CSS 样式会继承父级的样式,如 font-size、color 之类的。



CSS Object Model 是一组允许用 JavaScript 操纵 CSS 的 API。详细 API 讲解可以看 MDN

2.5.3 加载 JavaScript

通常情况下,在构建 DOM 树或 CSSOM 的同时,如果也要加载 JavaScript,则会造成前者的构建的暂停。当然,我们可以通过 defer 或 sync 来实现异步加载 JavaScript。虽然 defer 和 sync 都可以实现异步加载 JavaScript,但是前者是在加载后,等待 CSSOM 和 DOM 树构建完后才执行 JavaScript,而后者是在异步加载完马上执行,即使用 sync 的方式仍然会造成阻塞。

而 JavaScript 执行的过程,即编译和运行 JavaScript 的过程。由于 JavaScript 是解释型的语言。所以这个过程会是这样的:

  • 针对每句代码进行分行处理,即 Token 化
  • 根据 Token,生成 AST(Abstract Sytanx Tree) 抽象语法树和创建上下文
  • 解释器解析和执行 AST,生成字节码。
  • 编译器针对需要反复执行的代码,生成对应的机器码,提高运行效率

2.5.4 生成渲染树(Render Tree)

在有了 DOM 树和 CSSOM 之后,需要将两者结合生成渲染树 Render Tree,并且这个过程会去除掉那些 display: node 的节点。此时,渲染树就具备元素和元素的样式信息。

2.5.5 布局

根据 Render Tree 渲染树,对树中每个节点进行计算,确定每个节点在页面中的宽度、高度和位置。

需要注意的是,第一次确定节点的大小和位置的过程称为布局,而第二次才被称为回流

2.5.6 分层

由于层叠上下文的存在,渲染引擎会为具备层叠上下文的元素创建对应的图层,而诸多图层的叠加就形成了我们看到的一些页面效果。例如,一些 3D 的效果、动画就是基于图层而形成的。

值得一提的是,对于内容溢出存在滚轮的情况也会进行分层

2.5.7 生成绘制列表

对于存在图层的页面部分,需要进行有序的绘制,而对于这个过程,渲染引擎会将一个个图层的绘制拆分成绘制指令,并按照图层绘制顺序形成一个绘制列表。

2.5.8 光栅化

有了绘制列表后,渲染引擎中的合成线程会根据当前视口的大小将图层进行分块处理,然后合成线程会对视口附近的图块生成位图,即光栅化。而渲染进程也维护了一个栅格化的线程池,专门用于将图块转为位图。

栅格化的过程通常会使用 GPU 加速,例如使用 wil-change、opacity,就会通过 GPU 加速显示

2.5.9 显示

当所有的图块都经过栅格化处理后,渲染引擎中的合成线程会生成绘制图块的指令,提交给浏览器进程。然后浏览器进程将页面绘制到内存中。最后将内存绘制结果显示在用户界面上。

而这个整个从生成绘制列表、光栅化、显示的过程,就是我们常说的重绘的过程

结语

整个浏览器输入 URL 到页面渲染的过程涉及到的知识点非常广,如 Chrome 多进程的架构、HTTP 通信过程、浏览器解析 JavaScript 过程、浏览器绘制页面过程以及一些计算机的基础知识等等,并且,这整个过程的分析其实和 Chrome-devtools 密切相关,所以很好的使用 Chrome-devtools 是非常重要的,后续应该会出一篇关于使用 Chrome-devtools 的指南。当然,本篇文章仍然存在诸多不足,欢迎提 issue ~


作者:五柳
链接:https://juejin.im/post/5e871ee56fb9a03c832b0013

ower Query并不是一个专门的网抓或者爬虫工具,没有编程语言那么专业,实现的功能也比较有限,但其优势就是简单易学,且无缝对接excel,所见即所得。

本文将以纯新手的角度,介绍一些基础的网抓知识,尽可能让每个人都能学会。

网抓主要分为三个步骤:

1、抓包并分析请求

2、构建并发送请求

3、对返回的数据清洗。

背景知识:静态网页和动态网页

静态网页

首先来看一个最简单的案例:

http://quote.stockstar.com/stock/ranklist_a_3_1_1.html

这是一个静态页面,以html结尾,当你点击不同栏目不同页码的时候,URL也在相应的发生变化。

对服务器来说,静态页面是实实在在保存在服务器上的文件,每个网页都是一个独立的文件。所以比如我们要抓某一页的数据,只需要点击新建查询-从其他源-自网站,把对应的URL输入进去就可以了,这和我们导入本地xlsx文件的原理是一样的,至于如何批量抓取多页我们下面再讲。

但是静态页面缺点很明显:没有数据库支持,交互性差,非常不便于维护,所以大多数网站会采用动态页面设计,这就导致了我们想要抓取数据变得没那么简单。

动态网页

什么是动态页面?打个比方,打开百度首页,搜索关键词powerquery。

URL去掉无关参数精简后为:

https://www.baidu.com/s?wd=powerquery

搜索不同关键词只是wd=后面的部分在变化。

试想网民搜索关键词的可能性有无穷多,百度会为每一种可能做一个文件放在服务器里么?显然不会。

我们之所以能够看到不同的结果是因为当我们搜索关键词时,会使用GET方式向服务器提交带参数的请求,比如上面?后面的就是参数,wd是字段名,powerquery是值。当服务器接收到参数后,计算并返回给我们相应的结果。

那GET又是什么?这就要从HTTP协议开始说起了。

我们之所以能够使用浏览器访问网页,实际上就是浏览器向服务器提交请求(request),服务器收到请求后返回了响应(response)并把HTML代码发送给浏览器,浏览器解析并显示出来。

如上所说,对于动态页面我们请求的访问地址始终是同一个https://www.baidu.com/s,如果要让服务器知道我们想看什么,就需要一种东西来在客户端与服务器端之间进行消息传递,这就是HTTP协议请求。

HTTP协议请求主要有GET,POST,PUT,DELETE等等,分别对应查、改、增、删四个操作,其中最常见的就是GET和POST。GET仅请求资源,POST会附带一个Body包含用户数据。

GET请求的数据会附在URL之后,以?分割URL和传输数据,参数之间以&相连。上面百度的案例就是GET,再长点的话比如这样:

https://www.baidu.com/s?wd=power%20query%E7%BD%91%E6%8A%93&pn=10

这里有wd和pn两个参数,以&相连,中间的乱码是中文经过URL encoded转码后的结果,如果再有其他参数则继续在后面追加&即可。

POST对应改,请求提交的数据放置在HTTP包的Body中。比如你在下方发表评论,你并不能通过URL看出来你究竟向我提交了什么,而当你评论完,页面上出现了你的评论,我的网站已经被你修改了,这就是在提交POST请求。当然POST也可以用来查询数据,是一种更加安全的传递方式。

看到这里,我们已经知道了客户端和服务器端是如何进行信息传输的:客户端提交request,服务器返回response。注意我们这里说的是客户端,浏览器算一种,当然还有其他很多种,比如我们今天要讲的Power Query。

那么网抓的原理其实就是,找出我们所需要抓的数据,分析是浏览器向服务器提交了什么请求哪些参数,然后用Power Query构建同样的请求发送给服务器,服务器便会把对应的数据在Power Query中返回给我们。

了解了这个,下面我们就开始进行第一步:抓包。

抓包

网抓的关键在于抓包,一旦抓到包后面都好做。而在抓包的时候,一定要灵活变通,比如在抓一些电商网站数据时,PC端往往比较难抓,所以可以考虑PC转移动,去抓移动端网站,在Chrome中的F12窗口左上角可以一键切换PC和移动模式。再比如抓QQ好友列表,你直接抓软件是抓不到的,那你就思考除了在软件还可能在哪里有接口?比如QQ空间里@好友的时候,给好友充值QB的时候,都可以轻松获取到好友列表。一旦想通这点,你会发现网抓原来这么简单。抓包可以用浏览器自带的调试工具,按F12即可调出,比较轻量,但缺点是不能支持一些复杂的抓包,但话说回来Power Query本身就不太支持复杂的网抓,所以基本也够用了。

建议使用Chrome浏览器,下面的案例全部是基于Chrome,所以不要问我其他浏览器怎么找。

http://221.215.38.136/grcx/kscx/list.action?kscxVo.jsp=ylypmlcx.jsp

为例,我们点击下方无论选多少页发现URL都不会变化,那么如何获取到比如第5页的数据呢?按下F12调出调试工具。如果不起作用的话就右击选择检查-然后点击Network。

先简单介绍一下控制台。最上面圈出来的那一排,最常见的Elements显示网页的结构,Network显示浏览器和服务器的通信。

我们选择Network后应该是空的,因为才刚刚呼出控制台监控通信,而之前的行为是没有监控的,下方文字提示"通信记录已激活,请执行request或按F5"。

下面要做的就是让浏览器发送请求,比如你要抓第5页你就点击第5页或者下一页,你要抓其他栏目你就点击对应的栏目,总之目的就是产生查询数据的行为,让浏览器监测到。

如果你的点击让页面产生了变化,那么就会出现一条条记录,每条记录都是一次请求,浏览器会对这些请求按照类型进行分类,如图中框出来的部分所示,我们要做的就是在产生的这么多条请求中找出是哪条请求让服务器返回了我们所要的数据。

其中All是所有类型,数据不可能在CSS,Font之类的里面,按照我的经验可能性DOC>XHR>JS>其他,当记录过多产生干扰时可以点击左上角圈出来的Clear清空重新查找。

下面请动动你们的小手和我一起做如下几个操作:1、打开上面的URL,2、按下F12调出Network窗口,3、拉到页面最下方点击第5页。

按照刚才说的优先级依次找一下:Doc有一条,XHR空的,JS空的,如图所示。

在Headers标签下分为了几个模块:

General中的Requset URL表示请求的访问地址,Request Method表示请求所使用的方法为GET,所以我们可以通过这里判断是GET还是POST。

Response Headers是服务器返回的响应头,因为我们目前主要是想构建请求所以这部分内容不重要。

Request Headers是请求头,其中Cookie经常会用到,Referer有可能会用到,User-Agent是让服务器识别客户的设备信息的参数,在其他语言的复杂网抓下有可能用到,但在PQ中基本用不到,这个后面再说。顺便说一下,当你访问一个网站的时候,你电脑什么系统、用的什么浏览器、显示器多大、分辨率多少、浏览轨迹这些信息已经全部暴露给对方了,想想是不是感觉很恐怖。

Query String Parameters是查询参数,因为是GET方法,所以这些参数已经全部在最上面的Requset URL的?后面了。

这里所看到的信息是已经经过解析的,在每个模块的右边还有一两个小按钮,view source表示查询源代码,view URL encoded表示查询转码后的。

讲了这么多,那么我们如何确定目前所看到的这条记录是不是就是能够返回我们想要数据的请求呢?

首先我们回想下我们是如何找到这条记录的,是因为点击了第5页。

所以我们大致能够推断出应该是提交了一个字段名和page相关且值=5的参数,而看一下最下方的Query String Parameters或者最上方的Requset URL,其中有个page_ylypmlcxQuery=5,假如我们再点击第6页第7页,这个参数也会对应的变成6,7,所以基本能够确定就是它了。

又因为是GET,参数已经全部在URL里了,所以我们可以直接把Requset URL复制粘贴到浏览器看一下。

我们在浏览器地址栏里把5改成6,7,8,看到数据也会跟着发生变化。这里除了5还有另一个参数,也不知道是什么玩意,有强迫症的可以去掉试试,变成

http://221.215.38.136/grcx/pages/kscx/ylypmlcx.jsp?page_ylypmlcxQuery=5

发现对结果没影响,那么要抓第5页的数据我们只需要把这个地址复制到PQ中就可以了。

继续下一个案例:

http://www.drugfuture.com/cndrug/national.aspx?ApprovalDateStart=2016-01-01&ApprovalDateEnd=2016-12-31

同样要求抓第5页。

F12,点击第5页,在Doc里发现一条记录如下图:

我们发现Request Method是POST,并且在URL中已经找不到控制页数的参数了,因为POST提交的参数在Body中。

在Request Headers中比之前多了一个Content-Type,这个参数用来描述Body的内容类型,所以一般POST类型都需要加上这个参数,否则服务器无法识别提交的Body内容。注意response里也有个Content-Type,不要填错了。

在最下方相比GET多了一个Form Data的模块,其中包含__EVENTTARGET,__EVENTARGUMENT,__VIEWSTATE,__VIEWSTATEGENERATOR四个参数,这里就是我们上面一直所说的POST提交的Body内容。我们很容易发现__EVENTARGUMENT的值为Page就是控制页数的,继续点击第6页发现参数变成了Page,也就验证了我们的猜想。

所以同上一个案例相比,我们只需要把GET换成POST,提交上面的4个参数,再加一个Content-Type表明类型,即可抓到指定的数据。

以上介绍了使用浏览器自带调试工具分别实现GET和POST抓包的方法,但是毕竟案例比较简单,基本上都只有一条记录我们一下子就能找到。但是如果出现的记录很多,我们可以使用Fiddler来快速定位。

Fiddler是一款系统代理服务器软件,下载地址请自行百度。原本是客户端发送request,服务器返回response,而有了代理之后就相当于在客户端和服务器之间夹了个小三,客户端发送request给Fiddler,Fiddler再转发给服务器,反之亦如此。由于所有的网络数据都会经过Fiddler,自然Fiddler能够截获这些数据,实现网络数据的抓包。

安装好Fiddler后首先进行初始设置。

Rules,勾选"Remove all Encodings","Hide Image Requests","Hide CONNECTs",然后Tools-Options-HTTPS全部勾上。

还以上面POST那个案例为例,在浏览器中点击第5页,在Fiddler中按ctrl+F调出查找窗口,然后在想要抓取的页面中随便找一个比较有特征的数据,比如第5页中有一个产品名称叫做"维血康颗粒"。又因为我们要找的是服务器返回的response中的数据,所以我们可以选择Responses Only以缩小查找范围。

这样我们需要的数据所在的请求就会被高亮标记出来。Fiddler界面有很多标签,我们选择"Inspectors",此时界面分为三部分,左边为session框,右上是request框,右下是response框。

所以在Fiddler中我们要做的就是,ctrl+F查找,然后查看response框确认数据是否在范围内,然后在request框里找出请求参数。

request框和response框都提供了多种视图供我们查看,最好是都选择Raw,也就是原始数据。这里只是举了个例子,在实际应用中我们搜索的时候最好搜英文或数字,而不要搜中文,因为可能因为转码的问题导致搜不到。

刚才说到所有的网络数据都会经过Fiddler,所以使用Fiddler不仅可以抓到浏览器中的数据,甚至可以抓到一些应用软件中的数据,比如说抓QQ群成员。

打开QQ群资料-成员,刚打开的时候会看到短暂的"正在加载数据,请稍候"的提示。当加载完成后,Fiddler中多了几条记录,尝试搜索"施阳",高亮定位到其中的一条记录,查看response发现,没错这正是我们要的数据。

一般点击出现"正在加载"的数据多是如此,大家都可以动手找一下QQ群文件。

不知不觉已经5000字下去了,但到现在似乎都和Power Query没多大关系。

抓包是网抓的第一步也是最重要的一步,这步没做好或者找错了那么后面都是徒劳。不管你是使用PQ,还是VBA,还是python,到这一步的方法都是一样的。

至此我们已经知道了浏览器之所以能够获取到数据是因为它到底做了什么,那么下面就开始进入下一步,把刚才浏览器做的事交给Power Query去做。

构建请求

在M语言中,实现网抓的核心函数是Web.Contents,它能够对指定的URL向服务器发出request并接受返回的response,先看下函数介绍。

Web.Contents(url as text, optional options as nullable record) as binary

第一参数就是文本形式的URL,第二参数是可省略的record,包含上图中那一大坨参数,其中有3个比较常见,其他比较少见就不讲了。

Query:也就是F12中的Query String Parameters,之前也讲过这部分的参数也可以加在URL的?后面,以&相连,比如

=Web.Contents("http://www.baidu.com/s?wd=powerquery")

=Web.Contents("http://www.baidu.com/s", [Query=[wd="powerquery"]])

两者是等价的,但是后者结构更清晰更便于修改和维护,所以在简单的情况下使用前者更方便,在参数比较多的复杂情况下更推荐后者。

Content:如果是GET则不填,一旦填写此参数将改为POST。填写内容就是F12里的Form Data,然后点击view source所看到的一整串文本,同样参数与参数之间是以&连接的。在Fiddler中就是request框中Raw下的最底部的部分。

Headers:也就是F12中看到的Request Headers,其中当请求方式为Post时需要Content-Type,需要登录时要Cookie,部分防盗链网站需要Referer。

服务器返回的数据可能有很多种类型,这个我们先不管,这一步我们的目的就是构建带参数的request,获取目标数据的response,我们先全部统一获取源码,到下一步再讲如何转换。

Text.FromBinary能够将Web.Contents返回的binary解析出HTML源码,而不同网站的编码方式不同,中文网站常见的有GBK和UTF-8两种编码方式,一般在网页头部的meta部分都有声明。

所以如果网页采用GBK编码,就要给Text.FromBinary加上第二参数0,否则会乱码,下面有案例会讲到。

讲完这个,剩下的就是填空题了。

GET:

let
 url="", //Requset URL中?前面的部分
 headers=[Cookie=""], //如果不需要登录请删除整行,同时删除下一行中的Headers=headers
 query=[], //Query String Parameters,即Requset URL中?后面的部分
 web=Text.FromBinary(Web.Contents(url,[Headers=headers,Query=query]))
in
 web

POST:

let
 url="",
 headers=[#"Content-Type"="",Cookie=""], //Content-Type必填,如不需要登录Cookie可省略
 query=[],
 content="",
 web=Text.FromBinary(Web.Contents(url,[Headers=headers,Query=query,Content=Text.ToBinary(content)]))
in
 web

其中的""和[]就是需要你自己去填的,建议在编辑器软件中编辑好再粘贴到Power Query的高级编辑器中,在此推荐一款好用的编辑器——Sublime Text,轻量方便颜值高。

下面结合案例来做填空题:抓QQ邮箱收件箱。

F12,点击收件箱-下一页,在Doc里出现一条mail_list,观察发现是GET方式,所以用第一个模板。

又因为邮箱肯定是需要登录才能访问的,所以要加上cookie。

把代码编辑好复制到高级编辑器,发现返回的源码有乱码,再看一眼编码方式是GBK,所以加上第二参数0,结果正确,你收件箱里某一页的数据就都出来了。

很简单吧,你可以再尝试抓QQ空间,百度网盘这些,方法都一样。

再来举个特殊情况的案例:http://bond.sse.com.cn/bridge/information/。

F12,点击下一页,这回是在JS里有个commonQuery.do,也不难找到,是GET方式,但是把Request URL复制粘贴到浏览器地址栏却发现打不开,用Power Query也无法抓到数据。

简单来说就是网站做了防盗链处理,需要加上Request Headers里的Referer。

当然特殊情况也不止这一种,如果你很确定数据就在这条请求里,但是就是出不来,思考下什么原因?因为F12中本来有很多参数,我们只填了几个必填的参数,其他都省略了,出不来数据说明我们把某个必要的参数给省略了,那么可以把F12中看到的所有参数全部填上去试下,多半都能返回正确结果。

另外目前许多网站都部署了SSL认证的HTTPS协议,相当于HTTP的加强版,更加安全,比如本文一开始讲到的百度的案例。

但是Power Query目前对HTTPS支持不是太友好,所以如果碰到URL是https开头的请改成http,否则很可能会出错。

一般来说默认提交的GET或POST参数有很多,但很多都是无效或者不相关的参数,去掉也不影响结果,所以像我这种有强迫症的就习惯挨个把参数去掉测试。

本节介绍了如何在Power Query中构建参数并向服务器发出请求,这是最简单的一步,其实就是填空题套模板。

这步完成后,可以大致浏览下返回的源码,看下我们要的数据是否在其中,如果没问题就进行下一步——数据清洗。

数据清洗

经过上两步,我们已经抓到所需要的数据,但是格式比较乱,我们需要对其清洗成为规范的表格。

服务器返回的数据,有可能是HTML,JSON,XML等格式,举几个例子,请分别复制到浏览器打开比较下区别:

HTML:

http://datacenter.mep.gov.cn:8099/ths-report/report!list.action?xmlname=1462261004631

普通的网页结构,最简单的一种情况,HTML源码中包含table标签,使用Web.Page能够直接解析成表格,再深化出table即可。

JSON:

http://platform.sina.com.cn/sports_all/client_api?_sport_t_=football&_sport_s_=opta&_sport_a_=teamOrder&app_key=3571367214&type=10&season=2016

纯文本形式的结构化数据,一个字段对应一个值,使用Json.Document解析。但解析出来的不是表格而是record,除了我们要的数据还可能包含状态码、页数等等,所以需要找到数据所在的list,使用Table.FromReocrds还原成表。不会也没关系,到这一步剩下的基本靠纯界面操作就能完成。

XML:

http://www.cffex.com.cn/sj/hqsj/rtj/201710/18/index.xml

与JSON类似,都是纯文本形式的结构化数据,但没有JSON常见,使用Xml.Tables解析。

以上都属于结构化数据,就是能够通过函数直接或间接转换为表格,但很多时候我们遇到的可能是一些非结构化数据,比如要抓本站所有文章的标题,它既不是表格,也不是JSON,函数解析不出来,那要怎么搞呢?

常见的有正则,XPath,CSS Selector等方法,但很遗憾PQ一个都不支持。。。所以PQ在处理这些数据的时候就比较痛苦了,只能保持第二步中Text.FromBinary解析出来的源码,然后当作文本来用Text类函数提取。

Web.Contents返回的数据类型为binary,Text.FromBinary把binary解析为text,我们可以直接使用上面三个函数来替换Text.FromBinary的位置解析binary,也可以套在Text.FromBinary的外面来解析text,理论上效果是一样的,但是有些时候却直接解析不出来,必须加一个Text.FromBinary,比如案例篇的练习题。

接下来讲很多人关心的翻页问题,如何批量抓取多个网页然后合并呢?

以第一个静态页的案例为例

http://quote.stockstar.com/stock/ranklist_a_3_1_1.html

我们先写出完整的代码:

let
 源=Web.Page(Web.Contents("http://quote.stockstar.com/stock/ranklist_a_3_1_1.html")){0}[Data]
in
 源

URL结尾的a_3_1_1中最后一个1表示页数,往后翻会依次变成23456..现在要抓1到10页,那么我们只要把最后一个数改成23456..依次抓一遍即可。

但问题是抓不同页数的代码只是改了一个数字,其他部分都是一样的,我们不可能要抓10页就把代码重复10遍,这太麻烦了,所以可以把变化的部分做成变量封装为自定义函数,写成

fx=(page)=> Web.Page(Web.Contents("http://quote.stockstar.com/stock/ranklist_a_3_1_1"&Text.From(page)&".html")){0}[Data]

然后用List.Transform遍历循环{1..10},分别调用自定义函数fx得到一个包含10张表的列表,最后用Table.Combine将10张表合并为一张表,写成:

let
 fx=(page)=> Web.Page(Web.Contents("http://quote.stockstar.com/stock/ranklist_a_3_1_1"&Text.From(page)&".html")){0}[Data],
 结果=Table.Combine(List.Transform({1..10},fx))
in
 结果

注意,页数是数字,与URL的文本相连时要用Text.From进行数据类型转换。

同理,如果要批量抓取多日期、多ID之类的,只要更改自定义函数中的变量即可。

而如果我们不是要抓前10页而是所有页,而且事先是不知道一共有多少页的怎么办?如果返回的是JSON,大部分情况下数据中都会包含一个叫做totalpage的字段,所以可以分两步,第一步先随便提交一个页码拿到totalpage,可参考案例篇附件。

但是比如你现在正在使用我介绍的方法批量抓取我的网站数据,如果再多几个你这样的,那我的网站基本上就炸了。

一直如此高频率地访问网站,那得给服务器带来多大的压力。

所以很多网站会采取防爬虫措施,如果访问太频繁就会被封IP。PQ虽然不支持代理IP池之类的,但是延时还是支持的。

如果你要抓的网站会封IP,那么可以在自定义函数外面嵌套Function.InvokeAfter,设置每爬一次停顿个5秒。

比如

=Function.InvokeAfter(()=>1+1,#duration(0,0,0,5))

你会发现你的电脑算1+1还没你算的快。

进阶:动态交互

很多时候我们希望能够实现类似网页中的体验,输入指定条件比如开始和结束日期,根据指定条件抓取数据,如下图:

那么也很简单,只需要把需要查询的内容导入PQ,替换自定义函数中的变量即可,可参考案例篇附件。

另外值得一提的是,上面介绍过抓取需要登录的网站要加cookie,而cookie是有生命周期的,有时候你发现昨天抓到的数据今天就报错了,就是因为cookie过期了,你可以打开高级编辑器修改cookie的代码,但是显然太麻烦了。所以也可以把cookie写在单元格中,然后导入PQ,这样就可以实现在不打开PQ的情况下实现本需要修改代码的刷新。

调用API:

API即应用程序编程接口,调用API能够实现很多Power Query本身无法实现的功能,比如根据地址获取经纬度、翻译、分词、正则、机器人自动回复等等功能,可参考《使用PQ调用API》。

调用API的原理和网抓是一样的,其实很多网站的数据本身也是使用API调出来的。

一般开发文档都有详细说明,是使用GET还是POST,然后根据说明向服务器提交参数即可,返回的数据一般都是JSON。

部分API有免费限额,就是可以免费调用多少次,超出限额的需要收费,所以常见的比如地图、翻译等API都需要去开放平台注册为开发者,然后把自己的密钥加入到提交的参数中。

编程语言接口:

上面简单介绍了一下API,你可以把API理解成封装在服务器中的自定义函数,只需要向服务器提交函数所需要的参数,就能够返回你想要的结果。

那么服务器中的函数是怎么来的?那肯定是用编程语言来写的,比如PHP,Python等等,流程就是:你通过API提交参数,服务器中的编程语言写的程序来计算,得出结果并返回给你。

所以理论上只要是能够配置服务器环境的编程语言,都可以与PQ形成交互,比如《在Power Query中使用正则表达式》就是用PHP写的。

再比如使用Python的bottle包搭建本地服务器框架,便能够通过访问localhost与Python交互,可参考《M与Python交互》。

结语

此篇长文是施阳大神的手笔,来自于pqfans。本来我想写一篇:

但折腾来折腾去发现还是没法超越施阳这篇文章,于是发来头条分享。仅作了细微文字上的梳理。扩展链接给出了原文链接。

加国考的小伙伴们看过来!

中央机关及其直属机构2021年度考试录用公务员笔试成绩今早公布。中央机关和省级(含副省级)直属机构职位合格分数线为总分不低于105分,且行政职业能力测验不低于60分。国考调剂公告今天也同步发布。

考生可点击本条微信左下角的“阅读原文”登录后查询考试成绩,或在电脑中打开网址http://bm.scs.gov.cn/pp/gkweb/core/web/ui/business/auth/login.html查询。

各档分数线确定

国家公务员局说,今年划定笔试合格分数线时,既考虑到新录用公务员必须具备的基本素质,又兼顾了不同层级机关招考职位对公务员能力的不同要求,采取了分层划线的方法,同时对西部地区和艰苦边远地区基层职位予以一定的政策倾斜。其中,中央机关和省级(含副省级)直属机构职位合格分数线为总分不低于105分,且行政职业能力测验不低于60分;市(地)级和县(区)级直属机构职位合格分数线为总分不低于95分,且行政职业能力测验不低于50分;西部地区和艰苦边远地区(山西、内蒙古、吉林、黑龙江、广西、重庆、四川、贵州、云南、西藏、陕西、甘肃、青海、宁夏、新疆等15个省区市)市(地)级和县(区)级直属机构职位,定向招录大学生村官、“三支一扶”等服务基层项目人员和在军队服役5年(含)以上的高校毕业生退役士兵职位,非通用语职位,以及特殊专业职位合格分数线为总分不低于90分,且行政职业能力测验不低于45分。此外,中国银保监会、中国证监会职位和公安机关人民警察职位统一组织了专业科目笔试,专业科目笔试合格分数线为不低于45分。

各职位进入面试的人员名单将根据规定的面试比例,按照笔试成绩从高到低的顺序确定,面向社会统一公布。面试工作由各招录机关具体实施。面试前,将在“中央机关及其直属机构2021年度考试录用公务员专题网站”发布面试公告,载明面试人员名单、面试最低分数线、面试时间、面试地点、联系方式以及其他须知事项。对于公共科目笔试合格人数与拟录用人数之比未达到规定面试比例的部分职位,国家公务员局将在今年1月组织公开调剂,后续还将针对出现人员空缺的职位,面向社会统一进行补充录用。

国家公务员局根据公务员法和公务员录用有关规定,对违反考试纪律的人员作出了考试成绩无效、取消考试资格、限制报考等处理,进一步严肃考风考纪。

中央机关及其直属机构2021年度

考试录用公务员调剂公告

中央机关及其直属机构2021年度考试录用公务员笔试成绩已经公布,部分职位笔试合格人数与拟录用人数之比未达到规定的面试比例,根据中央机关及其直属机构2021年度考试录用公务员工作实施方案,需要面向社会公开调剂补充面试人选。现将有关事项公告如下:

一、调剂的原则和条件

(一)调剂在公共科目考试内容相同(即指报考人员应答的是同一类试卷)的职位之间进行;

(二)已进入首批面试名单的报考人员不得参加调剂;

(三)参加调剂的报考人员只能申请一个调剂职位;

(四)申请调剂的报考人员应当符合调剂职位规定的资格条件和要求;

(五)申请调剂的报考人员公共科目笔试成绩应当同时达到原报考职位的最低合格分数线和拟调剂职位的最低合格分数线;

(六)进入调剂职位面试的人员不得参加原报考职位的递补。

二、调剂程序

(一)报考人员从即日起,可以登录“中央机关及其直属机构2021年度考试录用公务员专题网站”(以下简称“专题网站”,http://bm.scs.gov.cn/kl2021)查询调剂职位、调剂人数、考试类别、资格条件、招录机关联系方式等。

(二)2021年1月12日8:00至1月14日18:00期间,报考人员通过报名时的用户名和密码登录“专题网站”,填报申请调剂的相关信息。报考人员对调剂职位所需专业、学历、学位、资格条件以及备注内容等信息需要咨询时,请直接与招录机关联系。在提交调剂申请时间结束之前,报考人员可以改变申请调剂的职位。

非网上报名的报考人员参加调剂时,请直接与拟申请调剂的招录机关联系,在2021年1月14日18:00前提交书面调剂申请。

(三)2021年1月15日8:00至1月16日18:00期间,招录机关对涉及本机关(单位)职位的网上调剂申请和书面调剂申请一并进行资格审查。资格审查时,按照调剂人员公共科目笔试成绩从高分到低分的顺序进行。公共科目笔试总成绩相同的,按行政职业能力测验科目的成绩排序。公共科目笔试总成绩和行政职业能力测验科目成绩都相同的,一并进入资格审查。审查合格人数达到规定的调剂人数后,招录机关不再对其他人员进行资格审查。

三、查询调剂结果

调剂结束后,将形成进入面试的人选名单,在“专题网站”统一公布。2021年1月18日之后,可以登录“专题网站”查询调剂结果。

打开网址查询http://bm.scs.gov.cn/pp/gkweb/core/web/ui/business/auth/login.html,登录后可查分哦

资料:国家公务员局

来源: 上海发布