Ajax,全称Asynchronous JavaScript and XML,即异步的JavaScript和XML。它不是一门编程语言,而是利用JavaScript在保证页面不被刷新、页面链接不改变的情况下与服务器交换数据并更新部分网页内容的技术。
对于传统网页,要更新内容则需要刷新页面,而Ajax可以在页面不被刷新的情况下更新。(这个过程实际是页面在后台与服务器进行了数据交互,获取数据后,再利用JavaScript改变网页。)
网页中的“下滑查看更多”的选项等…
从发送Ajax请求到网页更新的这个过程分3步:发送请求、解析内容、渲染网页。
这是JavaScript对Ajax最底层的实现:
var xmlhttp;
if (window.XMLHttpRequest){
xmlhttp=new XMLHttpRequest();
} else {//code for IE6,IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function (){
if (xmlhttp.readyState == 4 && xmlhttp.status == 200){
document.getElementById("myDiv").innerText=xmlhttp.responseText;
}
}
xmlhttp.open("POST","/ajax/",true);
xmlhttp.send();
服务器返回响应后,onreadystateschange属性对应的方法就被触发了,此时利用xmlhttp的responseText属性便可以得到响应内容。
JavaScript有改变网页内容的能力,因此解析完响应内容之后,就可以调用JacaScript来基于解析完的内容对网页进行下一步处理了。
Ajax有其特殊的请求类型,叫做xhr。在 加载过程中使用检查工具的页面中,我们可以看到一个以getIndex开头的请求信息,其type就为xhr,意味着这就是一个Ajax请求。
右侧可以观察这个Ajax请求的Repuest Headers、URL和Response Headers等信息。其中Request Header中有一个信息为**X-Requested-With:XMLHttpRequest,**这就标记了此请求时Ajax请求,
点击Preview就能看到响应的内容,这些内容是JSON格式的。JavaScript接收到这些数据后,再执行相应的渲染方法,整个页面就渲染出来了。
所以说,呈现给我们的真实数据并不是最原始的页面返回的,而是执行JavaScript后再次向后台发送Ajax请求,浏览器拿到服务器返回的数据后进一步渲染得到的。
利用Chrome开发者工具的筛选功能能够筛选出所有Ajax请求。在请求的上方有一层筛选栏,直接单击XHR,就能显示所有Ajax的请求了。
随便点开其中一个条目,都可以清楚地看到其Request URL,Request Headers,Reponse Headers,Response Body等内容,此时想要模拟Ajax请求的发送和数据的提取就非常容易了。(用程序模拟这些Ajax请求)
https://spa1.scrape.center/
用最简单的代码实现一下requests获取网站首页源码的过程:
import requests
url = "https://spa1.scrape.center/"
html = requests.get(url).text
print(html)
观察多个Ajax的参数,总结:limit一直为10,正好对应每页的10条数据;offset在依次变大,页数每加1,offset就加10,因此其代表页面的数据偏移量。
结果就是一些JSON数据,其中有一个results字段,是一个列表,列表中每一个元素都是一个字典,观察字典内容,里面正好可以看到对应电影数据的字段,如name、alias、cover、categories。我们只需要构造出所有页面的Ajax接口,就可以轻松获取内容了:
import logging
import requests
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s: %(message)s') # 定义logging的基本配置
INDEX_URL = 'https://spa1.scrape.center/api/movie/?limit={limit}&offset={offset}' # 把limit和offset预留出来变成占位符,可以动态传入参数构造一个完整的URL。
# 专门用来处理JSON接口
def scrape_api(url):
logging.info('scraping %s...', url)
try:
response = requests.get(url)
if response.status_code == 200:
return response.json() # 解析响应内容并将其转化成JSON字符串
logging.error('get invalid status code %s while scraping %s', response.status_code, url)
except requests.RequestException:
logging.error('error occurred while scraping %s', url, exc_info=True)
LIMIT = 10
# 爬取列表页
def scrape_index(page):
url = INDEX_URL.format(limit=LIMIT, offset=LIMIT * (page - 1)) # 通过字符串的format方法传入limit和offset的值。
return scrape_api(url)
这样就完成了列表页的爬取,每次发送Ajax请求都会得到10部电影的数据信息。
由于这时爬取到的数据已经是JSON类型了,所以无需像之前那样去解析HTML代码来提取数据,爬取到的数据已经是我吗想要的结构化数据。
单击任意一电影,发现URL变成https://spa1.scrape.center/detail/40,页面也成功展示了详情页。
可以在开发者工具发现出现了一个Ajax请求,其URL为https://spa1.scrape.center/api/movie/40,通过Preview也能看到 Ajax请求对应的响应信息。
# 爬取详情页的爬取逻辑
DETAIL_URL = 'https://spa1.scrape.center/api/movie/{id}'
def scrape_detail(id):
url = DETAIL_URL.format(id=id)
return scrape_api(url)
# 总的调用方法:
TOTAL_PAGE = 10
def main():
for page in range(1, TOTAL_PAGE + 1):
index_data = scrape_index(page)
for item in index_data.get('results'):
id = item.get('id')
detail_data = scrape_detail(id)
logging.info('detail data %s', detail_data)
if __name__ == '__main__':
main()