这篇文章给大家聊聊关于qq刷空间网站源码分享,以及刷空间网站空qq间对应的知识点,希望对各位有所帮助,不要忘了收藏本站哦。
PowerQuery并不是一个专门的网抓或者爬虫工具,没有编程语言那么专业,实现的功能也比较有限,但其优势就是简单易学,且无缝对接excel,所见即所得。
本文将以纯新手的角度,介绍一些基础的网抓知识,尽可能让每个人都能学会。
网抓主要分为三个步骤:
1、抓包并分析请求
2、构建并发送请求
3、对返回的数据清洗。
背景知识:静态网页和动态网页
静态网页
首先来看一个最简单的案例:
http://quote.stockstar.com/stock/ranklist_a_3_1_1.html\n
这是一个静态页面,以html结尾,当你点击不同栏目不同页码的时候,URL也在相应的发生变化。
对服务器来说,静态页面是实实在在保存在服务器上的文件,每个网页都是一个独立的文件。所以比如我们要抓某一页的数据,只需要点击新建查询-从其他源-自网站,把对应的URL输入进去就可以了,这和我们导入本地xlsx文件的原理是一样的,至于如何批量抓取多页我们下面再讲。
但是静态页面缺点很明显:没有数据库支持,交互性差,非常不便于维护,所以大多数网站会采用动态页面设计,这就导致了我们想要抓取数据变得没那么简单。
动态网页
什么是动态页面?打个比方,打开百度首页,搜索关键词powerquery。
URL去掉无关参数精简后为:
https://www.baidu.com/s?wd=powerquery\n
搜索不同关键词只是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\n
这里有wd和pn两个参数,以&相连,中间的乱码是中文经过URLencoded转码后的结果,如果再有其他参数则继续在后面追加&即可。
POST对应改,请求提交的数据放置在HTTP包的Body中。比如你在下方发表评论,你并不能通过URL看出来你究竟向我提交了什么,而当你评论完,页面上出现了你的评论,我的网站已经被你修改了,这就是在提交POST请求。当然POST也可以用来查询数据,是一种更加安全的传递方式。
看到这里,我们已经知道了客户端和服务器端是如何进行信息传输的:客户端提交request,服务器返回response。注意我们这里说的是客户端,浏览器算一种,当然还有其他很多种,比如我们今天要讲的PowerQuery。
那么网抓的原理其实就是,找出我们所需要抓的数据,分析是浏览器向服务器提交了什么请求哪些参数,然后用PowerQuery构建同样的请求发送给服务器,服务器便会把对应的数据在PowerQuery中返回给我们。
了解了这个,下面我们就开始进行第一步:抓包。
抓包
网抓的关键在于抓包,一旦抓到包后面都好做。而在抓包的时候,一定要灵活变通,比如在抓一些电商网站数据时,PC端往往比较难抓,所以可以考虑PC转移动,去抓移动端网站,在Chrome中的F12窗口左上角可以一键切换PC和移动模式。再比如抓QQ好友列表,你直接抓软件是抓不到的,那你就思考除了在软件还可能在哪里有接口?比如QQ空间里@好友的时候,给好友充值QB的时候,都可以轻松获取到好友列表。一旦想通这点,你会发现网抓原来这么简单。抓包可以用浏览器自带的调试工具,按F12即可调出,比较轻量,但缺点是不能支持一些复杂的抓包,但话说回来PowerQuery本身就不太支持复杂的网抓,所以基本也够用了。
建议使用Chrome浏览器,下面的案例全部是基于Chrome,所以不要问我其他浏览器怎么找。
以
http://221.215.38.136/grcx/kscx/list.action?kscxVo.jsp=ylypmlcx.jsp\n
为例,我们点击下方无论选多少页发现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中的RequsetURL表示请求的访问地址,RequestMethod表示请求所使用的方法为GET,所以我们可以通过这里判断是GET还是POST。
ResponseHeaders是服务器返回的响应头,因为我们目前主要是想构建请求所以这部分内容不重要。
RequestHeaders是请求头,其中Cookie经常会用到,Referer有可能会用到,User-Agent是让服务器识别客户的设备信息的参数,在其他语言的复杂网抓下有可能用到,但在PQ中基本用不到,这个后面再说。顺便说一下,当你访问一个网站的时候,你电脑什么系统、用的什么浏览器、显示器多大、分辨率多少、浏览轨迹这些信息已经全部暴露给对方了,想想是不是感觉很恐怖。
QueryStringParameters是查询参数,因为是GET方法,所以这些参数已经全部在最上面的RequsetURL的?后面了。
这里所看到的信息是已经经过解析的,在每个模块的右边还有一两个小按钮,viewsource表示查询源代码,viewURLencoded表示查询转码后的。
讲了这么多,那么我们如何确定目前所看到的这条记录是不是就是能够返回我们想要数据的请求呢?
首先我们回想下我们是如何找到这条记录的,是因为点击了第5页。
所以我们大致能够推断出应该是提交了一个字段名和page相关且值=5的参数,而看一下最下方的QueryStringParameters或者最上方的RequsetURL,其中有个page_ylypmlcxQuery=5,假如我们再点击第6页第7页,这个参数也会对应的变成6,7,所以基本能够确定就是它了。
又因为是GET,参数已经全部在URL里了,所以我们可以直接把RequsetURL复制粘贴到浏览器看一下。
我们在浏览器地址栏里把5改成6,7,8,看到数据也会跟着发生变化。这里除了5还有另一个参数,也不知道是什么玩意,有强迫症的可以去掉试试,变成
http://221.215.38.136/grcx/pages/kscx/ylypmlcx.jsp?page_ylypmlcxQuery=5\n
发现对结果没影响,那么要抓第5页的数据我们只需要把这个地址复制到PQ中就可以了。
继续下一个案例:
http://www.drugfuture.com/cndrug/national.aspx?ApprovalDateStart=2016-01-01&ApprovalDateEnd=2016-12-31\n
同样要求抓第5页。
F12,点击第5页,在Doc里发现一条记录如下图:
我们发现RequestMethod是POST,并且在URL中已经找不到控制页数的参数了,因为POST提交的参数在Body中。
在RequestHeaders中比之前多了一个Content-Type,这个参数用来描述Body的内容类型,所以一般POST类型都需要加上这个参数,否则服务器无法识别提交的Body内容。注意response里也有个Content-Type,不要填错了。
在最下方相比GET多了一个FormData的模块,其中包含__EVENTTARGET,__EVENTARGUMENT,__VIEWSTATE,__VIEWSTATEGENERATOR四个参数,这里就是我们上面一直所说的POST提交的Body内容。我们很容易发现__EVENTARGUMENT的值为Page$5就是控制页数的,继续点击第6页发现参数变成了Page$6,也就验证了我们的猜想。
所以同上一个案例相比,我们只需要把GET换成POST,提交上面的4个参数,再加一个Content-Type表明类型,即可抓到指定的数据。
以上介绍了使用浏览器自带调试工具分别实现GET和POST抓包的方法,但是毕竟案例比较简单,基本上都只有一条记录我们一下子就能找到。但是如果出现的记录很多,我们可以使用Fiddler来快速定位。
Fiddler是一款系统代理服务器软件,下载地址请自行百度。原本是客户端发送request,服务器返回response,而有了代理之后就相当于在客户端和服务器之间夹了个小三,客户端发送request给Fiddler,Fiddler再转发给服务器,反之亦如此。由于所有的网络数据都会经过Fiddler,自然Fiddler能够截获这些数据,实现网络数据的抓包。
安装好Fiddler后首先进行初始设置。
Rules,勾选”RemoveallEncodings”,”HideImageRequests”,”HideCONNECTs”,然后Tools-Options-HTTPS全部勾上。
还以上面POST那个案例为例,在浏览器中点击第5页,在Fiddler中按ctrl+F调出查找窗口,然后在想要抓取的页面中随便找一个比较有特征的数据,比如第5页中有一个产品名称叫做”维血康颗粒”。又因为我们要找的是服务器返回的response中的数据,所以我们可以选择ResponsesOnly以缩小查找范围。
这样我们需要的数据所在的请求就会被高亮标记出来。Fiddler界面有很多标签,我们选择”Inspectors”,此时界面分为三部分,左边为session框,右上是request框,右下是response框。
所以在Fiddler中我们要做的就是,ctrl+F查找,然后查看response框确认数据是否在范围内,然后在request框里找出请求参数。
request框和response框都提供了多种视图供我们查看,最好是都选择Raw,也就是原始数据。这里只是举了个例子,在实际应用中我们搜索的时候最好搜英文或数字,而不要搜中文,因为可能因为转码的问题导致搜不到。
刚才说到所有的网络数据都会经过Fiddler,所以使用Fiddler不仅可以抓到浏览器中的数据,甚至可以抓到一些应用软件中的数据,比如说抓QQ群成员。
打开QQ群资料-成员,刚打开的时候会看到短暂的”正在加载数据,请稍候”的提示。当加载完成后,Fiddler中多了几条记录,尝试搜索”施阳”,高亮定位到其中的一条记录,查看response发现,没错这正是我们要的数据。
一般点击出现”正在加载”的数据多是如此,大家都可以动手找一下QQ群文件。
不知不觉已经5000字下去了,但到现在似乎都和PowerQuery没多大关系。
抓包是网抓的第一步也是最重要的一步,这步没做好或者找错了那么后面都是徒劳。不管你是使用PQ,还是VBA,还是python,到这一步的方法都是一样的。
至此我们已经知道了浏览器之所以能够获取到数据是因为它到底做了什么,那么下面就开始进入下一步,把刚才浏览器做的事交给PowerQuery去做。
构建请求
在M语言中,实现网抓的核心函数是Web.Contents,它能够对指定的URL向服务器发出request并接受返回的response,先看下函数介绍。
Web.Contents(urlastext,optionaloptionsasnullablerecord)asbinary\n
第一参数就是文本形式的URL,第二参数是可省略的record,包含上图中那一大坨参数,其中有3个比较常见,其他比较少见就不讲了。
Query:也就是F12中的QueryStringParameters,之前也讲过这部分的参数也可以加在URL的?后面,以&相连,比如
=Web.Contents(“http://www.baidu.com/s?wd=powerquery”)\n
和
=Web.Contents(“http://www.baidu.com/s”,[Query=[wd=”powerquery”]])\n
两者是等价的,但是后者结构更清晰更便于修改和维护,所以在简单的情况下使用前者更方便,在参数比较多的复杂情况下更推荐后者。
Content:如果是GET则不填,一旦填写此参数将改为POST。填写内容就是F12里的FormData,然后点击viewsource所看到的一整串文本,同样参数与参数之间是以&连接的。在Fiddler中就是request框中Raw下的最底部的部分。
Headers:也就是F12中看到的RequestHeaders,其中当请求方式为Post时需要Content-Type,需要登录时要Cookie,部分防盗链网站需要Referer。
服务器返回的数据可能有很多种类型,这个我们先不管,这一步我们的目的就是构建带参数的request,获取目标数据的response,我们先全部统一获取源码,到下一步再讲如何转换。
Text.FromBinary能够将Web.Contents返回的binary解析出HTML源码,而不同网站的编码方式不同,中文网站常见的有GBK和UTF-8两种编码方式,一般在网页头部的meta部分都有声明。
所以如果网页采用GBK编码,就要给Text.FromBinary加上第二参数0,否则会乱码,下面有案例会讲到。
讲完这个,剩下的就是填空题了。
GET:
let\nurl=””,//RequsetURL中?前面的部分\nheaders=[Cookie=””],//如果不需要登录请删除整行,同时删除下一行中的Headers=headers\nquery=[],//QueryStringParameters,即RequsetURL中?后面的部分\nweb=Text.FromBinary(Web.Contents(url,[Headers=headers,Query=query]))\nin\nweb\n
POST:
let\nurl=””,\nheaders=[duration(0,0,0,5))\n
你会发现你的电脑算1+1还没你算的快。
进阶:动态交互
很多时候我们希望能够实现类似网页中的体验,输入指定条件比如开始和结束日期,根据指定条件抓取数据,如下图:
那么也很简单,只需要把需要查询的内容导入PQ,替换自定义函数中的变量即可,可参考案例篇附件。
另外值得一提的是,上面介绍过抓取需要登录的网站要加cookie,而cookie是有生命周期的,有时候你发现昨天抓到的数据今天就报错了,就是因为cookie过期了,你可以打开高级编辑器修改cookie的代码,但是显然太麻烦了。所以也可以把cookie写在单元格中,然后导入PQ,这样就可以实现在不打开PQ的情况下实现本需要修改代码的刷新。
调用API:
API即应用程序编程接口,调用API能够实现很多PowerQuery本身无法实现的功能,比如根据地址获取经纬度、翻译、分词、正则、机器人自动回复等等功能,可参考《使用PQ调用API》。
调用API的原理和网抓是一样的,其实很多网站的数据本身也是使用API调出来的。
一般开发文档都有详细说明,是使用GET还是POST,然后根据说明向服务器提交参数即可,返回的数据一般都是JSON。
部分API有免费限额,就是可以免费调用多少次,超出限额的需要收费,所以常见的比如地图、翻译等API都需要去开放平台注册为开发者,然后把自己的密钥加入到提交的参数中。
编程语言接口:
上面简单介绍了一下API,你可以把API理解成封装在服务器中的自定义函数,只需要向服务器提交函数所需要的参数,就能够返回你想要的结果。
那么服务器中的函数是怎么来的?那肯定是用编程语言来写的,比如PHP,Python等等,流程就是:你通过API提交参数,服务器中的编程语言写的程序来计算,得出结果并返回给你。
所以理论上只要是能够配置服务器环境的编程语言,都可以与PQ形成交互,比如《在PowerQuery中使用正则表达式》就是用PHP写的。
再比如使用Python的bottle包搭建本地服务器框架,便能够通过访问localhost与Python交互,可参考《M与Python交互》。
结语
此篇长文是施阳大神的手笔,来自于pqfans。本来我想写一篇:
但折腾来折腾去发现还是没法超越施阳这篇文章,于是发来头条分享。仅作了细微文字上的梳理。扩展链接给出了原文链接。
好了,文章到此结束,希望可以帮助到大家。