爬虫学习记录————ajax动态爬取
ajax爬取原理与解析
Ajax,全称为 Asynchronous JavaScript and XML,即异步的 JavaScript 和 XML。它不是一门编程语言,而是利用 JavaScript 在保证页面不被刷新、页面链接不改变的情况下与服务器交换数据并更新部分网页的技术。
传统的网页,如果你想更新其内容,那么必须要刷新整个页面。有了 Ajax,便可以在页面不被全部刷新的情况下更新其内容。在这个过程中,页面实际上在后台与服务器进行了数据交互,获取到数据之后,再利用 JavaScript 改变网页,这样网页内容就会更新了。
原理
初步了解了 Ajax 之后,我们再来详细了解它的基本原理。发送 Ajax 请求到网页更新的过程可以简单分为以下 3 步:
- 发送请求
- 解析内容
- 渲染网页
下面我们分别详细介绍一下这几个过程
发送请求
一个静态的网站需要向服务器发送请求然后获得了html文件后渲染,ajax也需要向服务器发送请求,获得html或者json后进行渲染,发送请求的过程一般通过JavaScript进行。
var xmlhttp;
if (window.XMLHttpRequest) {
//code for IE7+, Firefox, Chrome, Opera, Safari
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").innerHTML=xmlhttp.responseText;
}
}
xmlhttp.open("POST","/ajax/",true);
xmlhttp.send();
这是 JavaScript 对 Ajax 最底层的实现,这个过程实际上是新建了 XMLHttpRequest 对象,然后调用 onreadystatechange 属性设置监听,最后调用 open() 和 send() 方法向某个链接(也就是服务器)发送请求。
前面我们用 Python 实现请求发送之后,可以得到响应结果,但这里请求的发送由 JavaScript 来完成。由于设置了监听,所以当服务器返回响应时,onreadystatechange 对应的方法便会被触发,我们在这个方法里面解析响应内容即可。
解析内容
得到响应之后,onreadystatechange 属性对应的方法会被触发,此时利用 xmlhttp 的 responseText 属性便可取到响应内容。这类似于 Python 中利用 requests 向服务器发起请求,然后得到响应的过程。
返回的内容可能是 HTML,也可能是 JSON,接下来我们只需要在方法中用 JavaScript 进一步处理即可。比如,如果返回的内容是 JSON 的话,我们便可以对它进行解析和转化。
渲染网页
JavaScript 有改变网页内容的能力,解析完响应内容之后,就可以调用 JavaScript 针对解析完的内容对网页进行下一步处理。比如,通过 document.getElementById().innerHTML 这样的操作,对某个元素内的源代码进行更改,这样网页显示的内容就改变了,这种对 Document 网页文档进行如更改、删除等操作也被称作 DOM 操作。
上例中,document.getElementById(“myDiv”).innerHTML=xmlhttp.responseText这个操作便将 ID 为 myDiv 的节点内部的 HTML 代码更改为服务器返回的内容,这样 myDiv 元素内部便会呈现出服务器返回的新数据,网页的部分内容看上去就更新了。
可以看到,发送请求、解析内容和渲染网页这 3 个步骤其实都是由 JavaScript 完成的。
我们再回想微博的下拉刷新,这其实是 JavaScript 向服务器发送了一个 Ajax 请求,然后获取新的微博数据,将其解析,并将其渲染在网页中的过程。
因此,真实的数据其实都是通过一次次 Ajax 请求得到的,如果想要抓取这些数据,我们需要知道这些请求到底是怎么发送的,发往哪里,发了哪些参数。如果我们知道了这些,不就可以用 Python 模拟这个发送操作,获取到其中的结果了吗?
Ajax 分析
在进行刷新页面时,网页本身的url并没有发生改变,如何找到ajax请求呢?我以崔老师学习的网站为例。
import requests
url = 'https://dynamic1.scrape.cuiqingcai.com/'
html = requests.get(url).text
print(html)
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=/favicon.ico><title>Scrape | Movie</title><link href=/css/chunk-700f70e1.1126d090.css rel=prefetch><link href=/css/chunk-d1db5eda.0ff76b36.css rel=prefetch><link href=/js/chunk-700f70e1.0548e2b4.js rel=prefetch><link href=/js/chunk-d1db5eda.b564504d.js rel=prefetch><link href=/css/app.ea9d802a.css rel=preload as=style><link href=/js/app.652ace7f.js rel=preload as=script><link href=/js/chunk-vendors.2540653d.js rel=preload as=script><link href=/css/app.ea9d802a.css rel=stylesheet></head><body><noscript><strong>We're sorry but portal doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/js/chunk-vendors.2540653d.js></script><script src=/js/app.652ace7f.js></script></body></html>
我们可以看到只有很少的html文件被返回,但是页面的内容很多不在html文件里。打开浏览器的检查模式,通过xhr可以看到请求,观察请求头文件,即可获得对应的url。
通过xhr中看请求头文件获得对应的url,在这个网站里详情页和主界面页之前的url有一定规律,可以利用程序不断更新获取。
ajax爬取实例
# 引入需要的包
import requests
import logging
import json
from os import makedirs
from os.path import exists
RESULTS_DIR = 'results'
exists(RESULTS_DIR) or makedirs(RESULTS_DIR)
logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(levelname)s: %(message)s')
INDEX_URL = 'https://dynamic1.scrape.cuiqingcai.com/api/movie/?limit={limit}&offset={offset}'
# 爬取网站的基本信息
def scrape_api(url):
logging.info('scraping %s...', url)
try:
response = requests.get(url)
if response.status_code == 200:
return response.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)
# 对每一个主界面获得url
LIMIT = 10
def scrape_index(page):
url = INDEX_URL.format(limit = LIMIT, offset = LIMIT * (page - 1))
return scrape_api(url)
# 对详情页的解析
DETAIL_URL = 'https://dynamic1.scrape.cuiqingcai.com/api/movie/{id}'
def scrape_detail(id):
url = DETAIL_URL.format(id=id)
return scrape_api(url)
# 存储数据
def save_data(data):
name = data.get('name')
data_path = f'{RESULTS_DIR}/{name}.json'
json.dump(data, open(data_path, 'w', encoding='utf-8'), ensure_ascii=False, indent = 2)
# 链接调用
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)
save_data(detail_data)
if __name__ == '__main__':
main()
学习总结
我的理解,ajax就相当于一种持续连接,一次并不获得所有的对象,再需要的时候再次请求对象,进行解析渲染。而静态的网页一般就是直接获得所有的对象,然后渲染出来。相比之下,利用ajax可以使前后端分离,灵活性更高,但是爬取步骤繁琐。
上一篇: python网络爬虫教程(四):详解requests库
下一篇: Prewitt和Sobel算子
推荐阅读
-
通过抓取淘宝评论为例讲解Python爬取ajax动态生成的数据(经典)
-
【Python爬虫案例学习】Python爬取淘宝店铺和评论
-
python爬取Ajax动态加载网页过程解析
-
【Python爬虫案例学习2】python多线程爬取youtube视频
-
Python3爬虫学习之将爬取的信息保存到本地的方法详解
-
Python3爬虫学习之MySQL数据库存储爬取的信息详解
-
python爬虫学习教程之兼职网数据爬取
-
爬虫(三)通过Selenium + Headless Chrome爬取动态网页
-
Python爬虫学习==>第十章:使用Requests+正则表达式爬取猫眼电影
-
python爬虫学习之爬取超清唯美壁纸