爬取Ajax动态加载和翻页时url不变的网页+网站案例

最近在爬取一个网页的时候,遇到了需要对对多页表格的爬取,但是在对表格进行翻页的时候,url的地址并不会改变,而且网页的源代码中只有当前加载页出现的表格内容,并没有其余页所对应的<a href = ''>的内容,所以一开始纠结了很久,如何对这一类表格,或者说是对这一类在希望获取信息时无法获取跳转到其他页面的条件的情况进行爬取。后来查了很多知道,知道这是一种ajax书写的动态页面,ajax通过在后台与服务器进行少量数据交换, 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
于是,希望把这几天看到的内容做一个整理

###1.什么是Ajax

AJAX = 异步 JavaScript 和 XML。

AJAX 是一种用于创建快速动态网页的技术。

通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页面。

几个常见的用到ajax的场景。

比如你在逛知乎,你没有刷新过网页,但是你却能看到你关注的用户或者话题有了新动态的消息提示。

还比如,我们在看视频时,可以看到下面的评论没有完全全部加载出来,而是你向下拖动一点,它给你加载一点。

为什么要用到ajax呢?

从上述场景你应该也可以发现它的优点,

第一,方便与用户的交互,不用重新加载整个网页,就可以实现刷新,不用中断用户的行为。你正在看程序员如何找对象呢,此时来个消息推送,整个网页被刷新了,你说你气不气!

第二个呢,还是你在看程序员如何找对象,但是此时通信状况不好啊。回答加载不出来,页面就空白的卡那了,回答加载不出来,你说急不急!那这样咯,先给你看几个回答,在你看的时候我再悄悄的加载其它的数据,那不就解决了吗?就跟吃饭一个道理,你点了一桌子菜,难道菜全做好了再给你上吗?肯定不会的呀,做好一道上一道嘛,对不对。

第三,从服务端的发送过来的ajax数据,体积比较小。浏览器知道怎么渲染它,这样就减轻了服务端的压力,让客户端,也就是浏览器承担了一些任务。

Ajax技术的核心是XMLHttpRequest对象(简称XHR),可以通过使用XHR对象获取到服务器的数据,然后再通过DOM将数据插入到页面中呈现。虽然名字中包含XML,但Ajax通讯与数据格式无关,所以我们的数据格式可以是XML或JSON等格式。

XMLHttpRequest对象用于在后台与服务器交换数据,具体作用如下:

在不重新加载页面的情况下更新网页
在页面已加载后从服务器请求数据
在页面已加载后从服务器接收数据
在后台向服务器发送数据
ajax不是我们的重点,想深入学习的朋友自行了解ajax的核心机制XMLHttpRequest去吧。我们爬数据来着的哈。

###2.如何对Ajax页面进行爬虫
通过Ajax页面加载的页面,由于我们无法直接获取对应的href链接,那如何来获取对应的页面呢?
####1.利用Selenium模块
这几天查阅资料的过程中,看到了一个经常出现的用来模拟浏览器进行爬虫的模块——Selenium+PhantomJS或者Selenium+Chrome。因为本人用的是Chrome浏览器,所以在尝试的过程中我主要用的是Selenium+Chrome。

Selenium 是什么?一句话,自动化测试工具。它支持各种浏览器,包括 Chrome,Safari,Firefox 等主流界面式浏览器,如果你在这些浏览器里面安装一个 Selenium 的插件,那么便可以方便地实现Web界面的测试。具体相关的内容可以参考我的另一篇博客,这里先跳过这部分的讲解。因为在本次爬虫的过程中,我发现其实Slenium因为要启动浏览器然后进行各种模拟鼠标、键盘等的操作,其实对于爬虫来说对于有些需求并不需要,所以容易导致爬取得速度较慢。它可能更多的是用来去模拟人的行为去对某一网站进行操作,例如点击、拖拽、输入账号密码这样的一种自动化测试,如果你想利用电脑去抢票,或许就可以尝试用这种方法。但是对于获取数据,在这里仍然利用最简单但有效的requests模块仍然是一种行之有效的方法。

####2.利用Requests方法进行爬取
requests模块用的较多的有get和post方法。之前一直用的都是get方法,但是对于Ajax的网页,为什么在我们切换时网页的Url并不会改变,其中某些网站可能采用的就是它将网址后面的一些属性隐藏了。

例如某一网址为https://www.xxx.com/xxx?id=a&page=b&value=c 这样的网址显性的将属性添加到主体的后面,那么对于这类网站,我们就可以通过获取Query String Paramaters下的参数来构建网站,然后调用get方法,获取响应内容

另一种网址https://www.xxx.com/xxx 你会发现我们在页面上例如点击下一页或者切换内容时,网页的地址是不会变得。这种网址比较常见的可能就是在一些主体后的属性需要保密的场合,例如身份信息,毕竟你也不会希望这些信息直接出现显式的出现在网址上,供别人获取。在这种时候我们就可以采用post方法,利用From Data里的属性信息,来构造参数发送请求。

那我们如何判断一个网址的请求应该是get还是post呢?这个也很简单,我们利用开发者选项(F12),在网页的Headers下会有

Post方法下的From Data里的参数
这里写图片描述
或者
这里写图片描述
Get方法下的Query String Parameters里的参数
这里写图片描述

现在我们可以清楚看到两类网址在构造时候的区别。我们在用Requests模块对Ajax动态加载的网页进行爬虫时,首先就是需要对我们希望爬虫的页面构建网址,那么就是要清楚我们对希望爬取的网址,应该调用post方法还是get方法,以及它们需要获取的参数值有哪些,然后就是根据网页源码的内容,利用正则表达式或者Html的标签,去获得这些参数值。最后我们把获得的这些参数值保存在Python字典中,然后作为属性值,添加到网页主体的后面去,调用对应的Post函数或者Get函数发送请求。这样我们就达到了发送请求,获取响应的目的。

对于更多Get和Post的内容可以参考W3School
Get和Post

###3.案例演示
刚刚讲解了对于构造网址的两种方法,那么我们如何能找到对于Ajax动态加载的网页相对应的网址呢?例如对于翻页的表格或者翻页的网页,当url不变时,我如何才能找到被Ajax动态隐藏的部分,因为只有这样我们才能找到我们希望构建的网址主体以及后面的参数值。接下来,用两个网站的实例,来讲解寻找的流程。
###案例1:企查查网页
网站地址:http://www.qichacha.com/firm_06396efe66551d4ac07ee8cb41b0e325.html
(可能需要登陆后才能看到表格的更多信息)

假设我需要获取知识产权下的,专利信息、软件著作权、网站信息这三个表格的全部内容。对于有多页的表格,我们可以发现,当我们试图点击下一页的时候,页面除了当前表格的内容发生变化以外,其余内容包括url都没有变化。同时,当我们查看网页源代码尝试看看能不能通过每一页的<a href=''> href内的值构造网址来访问下一页时,也一无所获。这时候我们就需要换一种思路,去获取隐藏了的内容

####第一步:打开开发者选项(F12)
如果页面为空,刷新一下就会出现内容。会出现很多的内容,不用担心,之前说过Ajax技术的核心是XMLHttpRequest对象(简称XHR),于是我们找到XHR
这里写图片描述
点击左侧的文件,然后切换到Response选项

Response里的内容,其实正是我们调用Get或Post请求后返回的内容。所以我们可以点开左边的四个文件看看Response里的内容有没有我们需要的。

可以发现第三个文件对应的Response里的内容,正是当前页面下内容。但是分析后发现,这里的内容只有当前页面的信息,还是无法实现对表格的切换。这个时候怎么办呢?
####第二步:Ajax动态特性的体现
这个时候就是体现Ajax动态加载网页中的动态两字了。我们尝试一下手动点击任一表格的下一页(这里距离软件著作权),可以惊喜的发现,左侧的文件中多了一行
这里写图片描述

这里就保存了对于软件著作权表格第二页的内容。至此,我们就找到了我们希望获取的内容所对应的文件,或者说我们知道了当我们点击下一页时,网页的数据保存在哪里。那么接下来,就是利用Headers来构造网页,发送请求了

####第三步:利用Headers下的信息构造网页
在查看完Response知道左侧该文件就是我们希望访问的文件之后,接下来就是尝试构造网址发送请求。我们重新点击会Headers:
这里写图片描述
根据之前的讲解,相信到了这一步应该就不难理解了。
请求方式:Get方法
网址主体:http://www.qichacha.com/company_getinfos?
所需参数:unique、companyname、p、tab、box
所需要的参数也可以下来到Query String Parameters下获得:
这里写图片描述

至此,我们就完成了(1)对网页内容的分析,(2)对希望爬取的部分所处的文件进行查找(1.最简单的静态网页就是在源码中、2.对于源码中没有的尝试利用F12进行查找),(3)找到需要构建网址的主体和参数, (4)利用Get方法(或Post方法)发送请求。

在网页部分的寻找工作基本上到这里我们就已经完成了。接下来就是利用代码来实现构造网站,发送请求的过程了

####第四步:分析参数值的大体意义,构建参数值字典
首先我们知道了网址的主体,接下来就是对参数进行分析。因为参数值往往会根据我们的需要是不断改变的,所以我们要先分析一下我们需要改变的参数有哪些。
在本例中,参数值有5个
unique:需要我们用正则表达式或者Html标签去获得的
companyname:顾名思义就是公司名称,我们可以手动写,但是我们需要始终谨记一点就是,我们写的爬虫程序,不能仅仅针对某一网页或某一特定的网页,我们在编写的过程中,需要尽可能的提高我们程序的泛化能力。所以这里我们利用xPath表达式去自动获取公司名称
p:网页的页数,于是只要更改该键的值,我们就可以实现对每一页进行爬取。
tab:代表了知识产权的部分
box:作为不同的表格的标识。这里的rjzzq特指软件著作权表格

分析完之后,我们得出:
unique、company是需要自动获取的,
p代表页数,在爬取过程中应自动+1,
如果我们仅需要爬取知识产权下的内容,那么tab值应为恒定值:assets
box为表格的特征值,对不同的表格应赋对应的值。

最后我们以Python字典的格式保存这些参数值

    data = {
    'unique':'',
    'companyname':'',
    'p':1,
    'tab':'assets',
    'box':'rjzzq'
    }

####第五步:构造网址发送请求

我们利用Requests模块,对网址进行构建和发送请求。
我们利用Requests.get()方法来构造网址。因为网站的主体为:
http://www.qichacha.com/company_getinfos?
所以可以定义url = 'http://www.qichacha.com/company_getinfos?'
然后将上面构造的参数字典添加到网址主体的后面,这里我们就需要借助get函数的params属性:

s  = requests.session()
s.get(url, params = data)

就是这么简单。
至此,我们终于完成了整个流程的构建,当我们发送请求后,对于返回的Response内的内容,就可以像处理静态网页的方法一样,利用xPath路径或者BeautifulSoup方法去解析节点,获取我们希望获取的内容。

###案例2:上海证券交易所
网址:http://www.sse.com.cn/disclosure/listedinfo/regular/
之所以再将这个网址,就是发现有些网页,同样当你点击下一页或操作页面时,网页内容或者Url不会改变,但是网页隐藏的内容却并不一定都在XHR下,也是提醒不要一味的只认准XHR,还是要抓住Ajax的动态 ,说白了,就是多点点,看看左侧的文件内容里面有没有新出现的文件。

这里就不详细讲解这个案例了,有兴趣的可以自己按上面的步骤尝试一下,只不过你会发现你打开XHR里面没有任何内容,无论你刷新还是点击。这个时候不要气馁,回到‘All’下,再去观察,说不定会发现意向不到的收获

这里写图片描述

###4.总结
讲了这么多,在最后还是总结一下整体的思路:

一、判断网页类型
首先判断待爬取得网页是静态类型的,还是动态加载的。最主要的区别就是,静态网页的内容,在网页源代码内基本都会体现,因为这类网页在切换时都需要加载全部网页内容。而动态加载的网页你会发现你希望爬取得部分在网页源代码内没有体现,这个时候你就可以考虑利用开发者选项(F12)对网页进行分析

二、找到待爬内容的文件
对于静态网页,我们直接利用get发送请求,然后对返回的内容利用xPath路径或BeautifulSoup进行解析,一层一层筛选节点来获取我们希望得到的内容。

对于动态网页,直接分析网页的源代码往往没有很好的效果,因为这类网站不会直接把所以的内容显示在源代码里,更多的内容都在源代码内被隐藏了。所以这时利用F12对网页进行分析,在XHR中查看网页的Response值,看看哪一个文件是我们需要的。找到对应的文件后,返回Headers下观察文件的Requests Url和Query String Parameters,了解该网址的主体和参数值,思考如何获取参数值。在获取参数值后,将参数值以键-值对的形式保存在Python字典中,并利用get()方法的params属性,添加到网址主体后面,达到构建网址的目的。

三、判断网页的请求方式
根据Headers下的 Requests Method :GET(或POST),来确定相应的请求方式
对于GET,则调用get(url, params = data)方法
对于POST, 则调用post(url, data = data)方法
(注意属性的不同)

###5.利用headers属性模拟浏览器
在爬虫中还有一些技巧,就是利用get()函数的headers属性来模拟浏览器的头,也可以实现模拟浏览器的登陆。这一部分就不细致讲了,贴一下代码和照片
这里写图片描述

request_headers = {
    'Accept':'text/html, */*; q=0.01',
    'Accept-Encoding':'gzip, deflate',
    'Accept-Language':'zh-CN,zh;q=0.9',
    'Connection':'keep-alive',
    'Cookie':'PHPSESSID=c19cp9qv2tspcni8bt......',
    'Host':'www.qichacha.com',    'Referer':'http://www.qichacha.com/firm_06396efe66551d4ac07ee8cb41b0e325.html',
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36',
    'X-Requested-With':'XMLHttpRequest'
    }
s = requests.session()
s.get(url, params = data, headers = request_headers)

当然不是每次都需要把所有的字段值都copy下来,主要的还是User-Agent这个键值,所以大部分时候只用保留这个字段值添加,也可以达到目的。

headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'}

只是当你发现只添加这个键仍然会被网页阻挡例如返回Response【403】,这时你再去添加其他键值

其实对于动态网页的爬取,最核心的内容还是如何去寻找被网页隐藏了得内容,只要我们找到了被隐藏的内容,就可以构造网址,返送请求。然后对于返回的内容,其实也就相当于对静态网页的分析了。
所以说了这么多,爬虫还是需要多去自己动手实践,你自己多爬几个网站,遇到了不同的类型。就知道该如何去处理遇到不同的问题。
其实对于大部分的爬虫来说,真正更为复杂的是对返回的数据进行处理。因为我们始终要记得在爬虫时,我们的程序不能过拟合,应该具有很好的泛化能力,所以我们在编写的过程中,就需要去思考在不同情况下如何也能让程序很好的跑起来。这就比较锻炼逻辑思维能力,和对数据的敏感和处理能力。

最后分享一篇知乎的论文,写的比较简练,也可以作为参考
爬取Ajax动态加载和翻页时url不变的网页

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JermeryBesian

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值