第5章 Ajax数据爬取

目录

  • [1. 什么是Ajax](#1. 什么是Ajax)

  • [2. Ajax分析方法](#2. Ajax分析方法)

    • [2.1 分析案例](#2.1 分析案例)
    • [2.2 过滤请求](#2.2 过滤请求)
  • [3. Ajax分析与爬取实战](#3. Ajax分析与爬取实战)

  • 使用requests获取的是原始HTML文档

  • 浏览器中的页面是JavaScript处理数据后生成的结果

  • 数据的来源

    • 通过Ajax加载
    • 包含在HTML文档中
    • 经过JavaScript和特定算法计算后生成
  • Ajax加载数据

    • 方式:异步
      • 原始页面最初不包含某些数据
      • 当原始页面加载成功后,再向服务器请求某个接口获取数据
        • 发送Ajax请求
      • 然后将数据处理并呈现在网页上

1. 什么是Ajax

  • Ajax(Asynchronous JavaScript and XML) :异步的 JavaScript 和 XML
    • 不是一门编程语言
    • 利用 JavaScript 在保证页面不被刷新、页面链接不改变的情况下与服务器交换数据并更新部分网页的技术

1.1 实例引入

  • 下滑查看更多
  • 下滑后加载的动画:Ajax 加载的过程

1.2 基本原理

  • 从Ajax请求到网页更新的这个过程可以分为3步
    • 发送请求
    • 解析内容
    • 渲染网页

发送请求

  • JavaScript对Ajax最底层的实现
js 复制代码
var xmlhttp;
if (window.XMLHttpRequest) {
    xmlhttp = new XMLHttpRequest();
} 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.send();

解析内容

js 复制代码
var xmlhttp;
if (window.XMLHttpRequest) {
    xmlhttp = new XMLHttpRequest();
} 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.send();

渲染网页

js 复制代码
var xmlhttp;
if (window.XMLHttpRequest) {
    xmlhttp = new XMLHttpRequest();
} 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.send();

2. Ajax分析方法

2.1 分析案例

  • 微博 (weibo.cn)
  • Ajax 的请求类型:xhr
  • 如果 Request Headers 中有一个信息为XMLHttpRequest,则此请求就是 Ajax 请求
    • 以 getIndex 开头的请求
  • 可以在 Preview 查看响应的内容
  • 也可以在 Response 查看真实返回的数据

2.2 过滤请求

  • 点击 Network 中的 XHR 选项,显示所有的 Ajax 请求

3. Ajax分析与爬取实战

  • 爬取 Scrape | Movie
    • 与 2.5(Scrape | Movie)不同
      • 数据请求是通过 Ajax 实现的
      • 页面内容通过 JavaScript 渲染出来的
      • 只是呈现样式是一样的

3.1 爬取目标

  • 爬取电影的名称、封面、类别、上映时间、评分、剧情简介等内容
  • 分析页面数据的加载逻辑
  • 用 requests 实现 Ajax 数据的爬取
  • 将每部电影数据分别保存到 MongoDB 数据库

3.2 初步探索

python 复制代码
import requests

url = "https://spa1.scrape.center/"
html = requests.get(url).text

print(html)
  • 获取到的 html 资源较少
  • 整个页面都是JavaScript渲染得到的,浏览器执行了HTML中引用的JavaScript文件,JavaScript通过调用一些数据加载和页面渲染方法,才最终呈现出浏览器中的显示效果
  • 数据一般是通过Ajax加载的,JavaScript在后台调用Ajax数据接口

3.3 爬取列表页

分析

  • 请求URL的limit恒定为10,offset为已经已经翻过的电影数量( ( 当前页数 − 1 ) ∗ 10 (当前页数-1)*10 (当前页数−1)∗10)
  • 根据响应内容可以发现所需数据皆在其中

实现

基础配置
python 复制代码
import logging

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s %(levelname)s: %(message)s')

INDEX_URL = "https://spa1.scrape.center/api/movie/?limit={limit}&offset={offset}"
爬取页面内容(获取页面的JSON内容)
python 复制代码
import requests

def scrape_api(url):
    logging.info(f"scraping {url}...")
    try:
        response = requests.get(url)

        if response.status_code == 200:
            return response.json()
        logging.error(
            f"Status code: {response.status_code} while scraping {url}")
    except requests.RequestException:
        logging.error(f"Error while scraping {url}", exc_info=True)
爬取列表页(爬取指定列表页)
python 复制代码
LIMIT = 10

def scrape_index(page):
    url = INDEX_URL.format(limit=LIMIT, offset=LIMIT * (page - 1))
    return scrape_api(url)

合并

python 复制代码
import logging
import requests

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s %(levelname)s: %(message)s')

INDEX_URL = "https://spa1.scrape.center/api/movie/?limit={limit}&offset={offset}"
LIMIT = 10


def scrape_api(url):
    logging.info(f"scraping {url}...")
    try:
        response = requests.get(url)

        if response.status_code == 200:
            return response.json()
        logging.error(
            f"Status code: {response.status_code} while scraping {url}")
    except requests.RequestException:
        logging.error(f"Error while scraping {url}", exc_info=True)


def scrape_index(page):
    url = INDEX_URL.format(limit=LIMIT, offset=LIMIT * (page - 1))
    return scrape_api(url)

3.4 爬取详情页

分析

  • url:最后的一个参数为此电影的id
  • 电影的id:Ajax请求返回的数据中含有电影对应的id

实现

爬取详情页
python 复制代码
DETAIL_URL = "https://spa1.scrape.center/api/movie/{id}"


def scrape_detail(id):
    url = DETAIL_URL.format(id=id)
    return scrape_api(url)
串联调用
python 复制代码
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(f"detail data {detail_data}")
            

if __name__ == "__main__":
    main()

合并

python 复制代码
import logging
import requests

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s %(levelname)s: %(message)s')

INDEX_URL = "https://spa1.scrape.center/api/movie/?limit={limit}&offset={offset}"
DETAIL_URL = "https://spa1.scrape.center/api/movie/{id}"
LIMIT = 10
TOTAL_PAGE = 10


def scrape_api(url):
    logging.info(f"scraping {url}...")
    try:
        response = requests.get(url)
        if response.status_code == 200:
            return response.json()
        logging.error(
            f"Status code: {response.status_code} while scraping {url}")
    except requests.RequestException:
        logging.error(f"Error while scraping {url}", exc_info=True)


def scrape_index(page):
    url = INDEX_URL.format(limit=LIMIT, offset=LIMIT * (page - 1))
    return scrape_api(url)


def scrape_detail(id):
    url = DETAIL_URL.format(id=id)
    return scrape_api(url)


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(f"detail data {detail_data}")


if __name__ == "__main__":
    main()

3.5 保存数据(MongoDB)(后期补充)

相关推荐
萧鼎31 分钟前
Python并发编程库:Asyncio的异步编程实战
开发语言·数据库·python·异步
学地理的小胖砸32 分钟前
【一些关于Python的信息和帮助】
开发语言·python
疯一样的码农32 分钟前
Python 继承、多态、封装、抽象
开发语言·python
全栈开发圈1 小时前
新书速览|Java网络爬虫精解与实践
java·开发语言·爬虫
小白学大数据1 小时前
JavaScript重定向对网络爬虫的影响及处理
开发语言·javascript·数据库·爬虫
Python大数据分析@1 小时前
python操作CSV和excel,如何来做?
开发语言·python·excel
黑叶白树1 小时前
简单的签到程序 python笔记
笔记·python
Shy9604181 小时前
Bert完形填空
python·深度学习·bert
上海_彭彭2 小时前
【提效工具开发】Python功能模块执行和 SQL 执行 需求整理
开发语言·python·sql·测试工具·element
zhongcx012 小时前
使用Python查找大文件的实用脚本
python