欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

爬虫学习记录————ajax动态爬取

程序员文章站 2022-03-22 16:56:09
...

GitHub地址:https://github.com/yunlong-G/learn/blob/master/spider_learn/ajax%E5%8A%A8%E6%80%81%E7%88%AC%E5%8F%96.ipynb

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。爬虫学习记录————ajax动态爬取
通过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可以使前后端分离,灵活性更高,但是爬取步骤繁琐。