python编程
首发于python编程
ajax动态加载网页抓取

ajax动态加载网页抓取

AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。

举一个例子,打开这个页面,先不要动,观察右边滚动条的长度,然后当你把滚动条下拉到底之后,滚动条就变短了,即页面变长了,也就是说有一部分数据是这个时候才加载出来的。这个过程就是动态加载,基于ajax技术。我们可以看到在拉动滚动条的时候,页面上的数据变多了,但是URL始终没有变化,它不是像翻页那样将数据存到了另一个网页。下面我们来说明一下这种网页如何抓取。

本文分为如下几个部分

  • 两种查看网页源代码的方式
  • 分析网页请求
  • 抓取一页数据
  • 页面更新策略
  • 其他例子(知乎live,豆瓣电影,简书首页,网易云音乐)

两种查看网页源代码的方式

首先需要声明使用浏览器时两种查看网页源代码的方式是有所不同的,这里使用chrome浏览器来说明。

第一种是右键-检查,在element中查看网页源代码,这个模式带有折叠和选择功能,非常有利于我们找到要抓取信息的位置。这里的源代码是当前展示在我们面前的页面对应源代码。

第二种是右键-查看网页源代码,这是真正的网页源代码,我们请求网页网页获得的源代码(r.text)就和这个一模一样。

在很多时候,这两个位置显示的源代码是完全相同的,但是有时会有差异。比如现在这个动态加载的例子,当我将滚轮下拉之后,重新加载出了新的数据,这部分数据是会出现在“检查”的源代码中,但是不会出现在“查看网页源代码”中。后者就是这个URL最原始的源代码,不会因为之后执行的JS程序而改变,而“检查”中的源代码则是和当前页面样子保持一致的,是执行一些JS代码后重新生成的源代码。

有时候会遇到这样的情况:两个位置的源代码几乎是一模一样的,只是在一些小的标签属性或者某个数值上有差异。在解析网页时,我们常常基于“检查”中的源代码,当觉得自己解析代码没问题,但是又匹配不出来东西(或者错误的东西)的时候,可以考虑去看一下“查看网页源代码”那个页面,是不是在小的地方上有差异。

举一个例子,链家二手房的一个页面,看“税费”这里,你先刷新网页观察这个位置,会发现它会先加载出13.8,之后变成了45.

如果你用两种方法去查看源代码,会发现检查中是45,而查看网页源代码是未经改变的13.8。因此如果你要爬这个网站,没有经过任何处理,就会获得13.8,这是链家故意给你的假数据。

言归正传,我们要想抓取这个网页的数据,如果我们只是像以前一样提取r.text解析网页的话,其实只能获得最开始加载出来的数据,如何能把之后要加载的所有数据都抓取到是我们这里要讨论的问题。

分析网页请求

我们在页面中右键-检查,选择network,选择XHR,将左边网页的滚动条拖到最下面加载新的数据,即可看到network中出现了新的文件

这些是你拖动时浏览器帮你向网页发出了新的请求,而加载出来的数据则是这次请求获得的。也就是说,我们可以在新加载的这些文件中找到我们想要的数据,根据经验数据存在第一个URL中,点击这个URL,可以看到如下图所示(读者可以点一点其他URL,会发现其他文件对我们爬取数据没有什么帮助)

preview表示这个URL中的内容是什么,从上面的图中可以看到,这是一个json格式的数据,我们可以把每一项展开,看具体有什么信息

可以看到我们想要的关于文章的信息都在这里面了,而且有的字段没有显示在网页中,对于这部分信息就只能根据里面的键值对名猜这个字段是什么意思。

接下来我们就可以请求这个URL获得数据了,URL在headers里面查看

从json里面提取信息会比解析网页轻松很多,所以以后遇到一个爬虫任务,首先看网页请求中有没有类似这样的文件,有的话就可以直接请求这种json文件,不需要一步一步解析网页了。

一般这种json文件容易在XHR中找到,但是也不排除有时候要在all里面查找这样的文件。

请求的文件一般都非常多,在没有方向的时候只能一个一个点过去,但是其实也不麻烦,每个URL都关注preview,是json格式的数据就像刚才那样,其实很明显。

接下来,我们来写抓取数据的代码。

抓取一页数据

现在假设我们只想要文章的标题,我们只需要请求XHR中那个URL,将获得的内容像字典一样处理即可,代码如下

import requests

headers = {'cookie':'', # 传入你的cookies
           'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'}

def get_page():
    r = requests.get('https://www.csdn.net/api/articles?type=more&category=home&shown_offset=0',
                headers = headers)
    d = r.json()
    articles = d['articles']
    for article in articles:
        yield article['title']

for i in get_page():
    print(i)

其中r.json是将json格式的字符串转化为python对象,这里是转化成了一个字典。(读者可以试着调用r.text会发现是个json字符串,可以用json模块中的json.loads转化为字典对象,只是requests中提供了.json()更方便使用了而已)

注意:如果不用cookie,得到的不是首页上展示的数据。所以即使你没有登录,也把cookie复制过来。

接下来我们来抓取所有数据。

页面更新策略

我们再下拉网页,会发现新加载出了一个URL

查看它的headers,惊奇地发现它和前面那个是一模一样的,再查看它的请求头(request header),几乎也一样,所以我们可以试一试对这个页面连续请求两次,看是否会得到不同的数据——果然不同的数据。

所以我们可以定下抓取策略:不断地访问这个URL,直到获取不了数据为止。

我们连续请求20次,代码如下

import requests

headers = {'cookie':'', # 传入你的cookies
           'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'}

def get_page():
    r = requests.get('https://www.csdn.net/api/articles?type=more&category=home&shown_offset=0',
                headers = headers)
    d = r.json()
    articles = d['articles']
    print(len(articles)) # 每次看抓取到了多少信息
    for article in articles:
        yield article['title']

def get_pages():
    for i in range(20):
        yield from get_page()

l = list(get_pages())
len(l) # 看总共抓了多少条

这个代码的结果是,请求的20次中,前15次每次获取10个数据,后5次没有获取数据,说明我们只能获得150条信息。

这时我们再去看浏览器,会发现下拉滚动条它不会加载新的数据了,而且如果你刷新网页的话,就会得到这个界面。

这说明你的爬虫把这个cookie耗尽了,浏览器使用这个cookies也无法获得数据了,要再过一会儿请求才可以。

其他例子

对于ajax加载的页面的抓取都是大同小异,可能只是不同网站每次加载URL的设计不同。我们再举一些例子

  1. 知乎live,它也是通过鼠标下拉页面,加载新的数据,我们就会怀疑它是ajax加载的。同样的方法可以找到下面这个文件

可以看到它比CSDN多出了一个字段paging,里面显示了这一页是不是最后一页,下一页链接是什么。这样我们只需要下拉一次获得上图这样的页面,然后请求该页面,获取数据和下一个页面的URL,然后请求下一个页面,获取数据再得到下下个页面,通过判断is_end来结束循环。

或者说我们可以看当前的URL和下一个URL之间有没有规律,通过构造URL来循环。

读者如果没有试过的话,可能会怀疑这里的next真的是下一页的数据吗。那么就可以多次下拉网页,看加载出来的多个文件是否后一个的URL是前一个的next。

知乎live这种形式的ajax加载内容和URL设计应该是最常见的。我们可以看出它的URL其实是知乎的API(看那个URL的域名),即我们浏览网页时,页面是通过请求API获取的数据。API可以理解成数据接口,请求这个一个URL就可以获得相应数据,则这个URL就是一个接口。本专栏后面会专门写一篇文章介绍API的。

2.豆瓣电影

点击上面标签:电影,热门、最新、豆瓣高分等,每次点击会发现浏览器中显示的URL都是没变的,包括点击下面的点来翻页,URL也没变,这就说明这里的数据可能是用ajax加载的,到检查中的network的XHR中看一下

发现数据确实是用ajax加载的,我们只要请求这个URL即可获取数据。

这时我们选择最新、豆瓣高分那些标签,可以看到XHR中加载出了新的文件,如下图所示

我们只要分析这些文件的URL有什么规律,就可以抓取这个窗口中所有标签的电影数据了。

3. 简书首页

简书首页也是拖动滚动条会加载出新的数据,但是它和上面我们说的不太一样,它加载出的文件不是json格式的,而是XML格式的,这个格式其实和我们平常说的HTML源代码的样子没什么区别,解析提取数据也没什么区别。只是这时就要解析而不能简单地从字典里提取数据了

preview并不是指这个URL对应的源代码,而是它渲染出来的样子,可以在response里看源代码(json格式的也是一样)

简书首页的爬取坑比较多,除了这个,还有比如URL构造,还有某些页莫名其妙报错,具体代码可以自行百度简书首页爬虫。

4. 网易云音乐

比如这里看薛之谦的歌

如果你点击下面第几页的话,会发现URL没变,说明也是要用类似的方法获取数据。通过点击第几页,看XHR中新增的文件,请求这些URL获取数据。但是这里有个大坑,请求这个URL使用的是POST请求,而且需要上传加密的参数

想要爬取这个网页,首先需要了解POST请求,其次需要去分析网页加载的JS代码,破解这些加密的参数,这是非常困难的爬虫了,我个人不会JS也无法破解,只是放在这里展示各种各样的加载方式,供读者参考。(不过对于这种网页,还有selenium这种万能工具,在不要求抓取速度的情况下,是可以不需要破解这些加密手段抓取到数据的)

专栏信息

专栏主页:python编程

专栏目录:目录

爬虫目录:爬虫系列目录

版本说明:软件及包版本说明

编辑于 2018-05-12

文章被以下专栏收录