Ajax

什么是Ajax

全称是Asynchronous JavaScript and XML,即异步的JavaScript和XML。利用了JavaScript在保证页面不被刷新,页面链接不改变的情况下与服务器交换数据并更新部分网页的技术
https://www.w3school.com.cn/ajax/ajax_xmlhttprequest_send.asp

实例引入

访问https://m.weibo.cn/u/2830678474
avatar

向下滑,滑几个微博之后,就会出现这种加载中的画面,这其实就是Ajax的加载过程

基本原理

分为3个步骤:
1)发送请求
2)解析内容
3)渲染网页

JavaScript可以实现页面的各种交互功能,Ajax也不例外,它也是由JavaScript来实现的
执行了如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
var xmlhttp;
if(window.XMLHttpRequest){
xmlhttp=new HttpRequest();
}else{
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP")
}

xmlhttp.onreadystatechange=function(){
if(xmlhttp.readyState==4&&xmlhttp.status==200)
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
}
xmlhttp.open("POST","/ajax/",true)
xmlhttp.end

这是JavaScript对Ajax最底层的实现,实际上就是新建了XMLHttpRequest对象,然后调用onreadystatechange属性设置了监听,然后调用open()和send()方法向某个链接发送了请求。由于设置了监听,所以当服务器返回响应时,onreadystatechange对应的方法便会被触发,然后在这个方法里解析响应内容即可

解析内容

得到响应之后,onreadystatechange属性对应的方法便会被触发,此时利用xmlhttp的responseText属性便会取到响应内容。这类似于python中利用requests向服务器发起请求,然后得到响应的过程。那么返回内容可能是HTML,也可能是JSON,接下来只需要在方法中用JavaScript进一步处理即可。

渲染网页

JavaScript有改变网页内容的能力,解析完响应内容之后,就可以调用JavaScript来针对解析完的内容对网页进行下一步处理了。比如,通过document.getElementById().innerHTML这样的操作,便可以对某个元素内的源代码进行更改,这样网页显示的内容就改变了,这样的操作也被称为DOM操作,即对Document网页文档进行操作。

Ajax分析方法

之前微博的例子,知道拖动刷新的内容由Ajax加载,而且URL没有变化,所以就需要借助开发者工具

查看请求

avatar
F12使用开发者工具,查看Elements选项卡中便会观察到网页的源代码,右侧便是节点的样式

接着切换到NetWork选项卡,刷新一下会出现很多条目,Ajax其实有其特殊的请求类型,叫做xhr,我们可以发现一个名称为getIndex开头的请求,其Type为xhr,这就是一个Ajax请求
avatar
avatar
再点一下Preview
avatar
这里返回的就是个人信息,如名称,简介,头像等,这是用来渲染个人主页所使用的数据
再切换到Response选项卡,从中观察到真实的返回数据
avatar
接下来切换到第一个请求,查看它的Response
avatar
发现只有不到50行代码,但是执行了一些JavaScript

过滤请求

使用开发者工具,筛选出所有的XHR
avatar
这样就可以清楚的看到XHR的URL Headers等内容了

Ajax结果提取

接下来用python模拟Ajax请求,把发过的微博爬取下来

分析请求

打开XHR过滤后,然后滑动页面会发现加载新的微博内容,并且不断会有Ajax请求发出

选定一个请求,分析它的参数信息。
avatar
可以看到这是一个GET类型的请求,参数有4个type,value,containerid和page

再看看其他请求,发现这几个参数始终如一。type始终为uid,value就是页面链接中的数字,这其实就是用户的id,containerid它就是107603加上用户id,改变的值就是page,是用来控制分页的

分析响应

avatar
这个内容是JSON格式的,开发者工具自动做了解析方便我们查看,可以看到最关键的部分就是cardlistInfo和cards:前者包含一个比较重要的信息total,观察后可以发现,它是微博的总数量;后者则是一个列表,随便展开一个查看
avatar
这里面的mblog比较重要,展开后发现正是微博的一些信息,比如attitudes_count(赞数目),comments_count(评论数目),reposts_count(转发数目),created_at(发布时间),text(微博正文)等

这样我们请求一个接口,就可以得到10条微博,请求时只需要改变page参数即可

实战演练

我们用Python来模拟这些请求,将他前10页的微博全部爬取下来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import requests
from urllib.parse import urlencode
from pyquery import PyQuery as pq
base_url="https://m.weibo.cn/api/container/getIndex?"

headers={
'Host':'m.weibo.cn',
'Referer':'https://m.weibo.cn/u/2830678474',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36',
'X-Requested-With':'XMLHttpRequest'
}
max_page = 10

def get_page(page):
params={
'type':'uid',
'value':'2830678474',
'containerid':'1076032830678474',
'page': page
}
url=base_url+urlencode(params)
try:
response=requests.get(url,headers=headers)
if response.status_code==200:
return response.json()
except requests.ConnectionError as e:
print('Error',e.args)

def parse_page(json):
if json:
items = json.get('data').get('cards')
for item in items:
item = item.get('mblog')
weibo = {}
weibo['id'] = item.get('id')
weibo['text'] = pq(item.get('text')).text()
weibo['attitudes'] = item.get('attitudes_count')
weibo['comments'] = item.get('comments_count')
weibo['reposts'] = item.get('reposts_count')
yield weibo


if __name__ == '__main__':
for page in range(1, max_page + 1):
json = get_page(page)
results = parse_page(json)
for result in results:
print(result)

这样就把前10页的微博都爬取下来了

总结

1.首先要判断是否为Ajax,具体方法就是下拉,看是否会有加载新内容,并关注开发中工具中的NetWork选项卡,查看有变化

2.在筛选XHR时,要注意其中的内容和URL变化,如果是靠翻页来更新内容的,就不是Ajax

3.URL变化也是判断是否为Ajax的重要选项,例如微博中的id等参数就没有变化,并且主页代码很少且执行了JS,综合一下就可以判断是否为Ajax

4.目前不一定能爬取到,例如今日头条中的图片,参数设置为了abstract,这样就没有办法爬取到了