【13】Ajax爬取案例实战

目录

 一、准备工作

二、爬取目标

三、初步探索:如何判断网页是经js渲染过的?        

四、爬取列表页

4.1 分析Ajax接口逻辑

4.2 观察响应的数据

4.3 代码实现

(1)导入库

(2)定义一个通用的爬取方法

(3)定义一个爬取列表页的方法

 五、爬取详情页

5.1分析详情页

5.2 如何将详情页与列表页关联起来

5.3 代码实现

六、main总调用

七、完整代码(简化版)


        上一节我们学习了 Ajax 的基本原理和分析方法,这一课时我们结合实际案例,学习 Ajax 分析和爬取页面的具体实现。

上期文章: 【12】Ajax的原理和解析-CSDN博客

 一、准备工作

        安装好 Python 3(最低为 3.6 版本),并能成功运行 Python 3 程序。

        了解 Python HTTP 请求库 requests 的基本用法。

        了解 Ajax 的基础知识和分析 Ajax 的基本方法。

二、爬取目标

以一个动态渲染网站为例来试验一下 Ajax 的爬取。其链接为:Scrape | Movie,页面如图所示。

         这个页面看似和我们上一课时的案例一模一样,但其实不是,它的后台实现逻辑和数据加载方式与上一课时完全不同,只不过最后呈现的样式是一样的!!

本课时我们需要完成的目标有:

        分析页面数据的加载逻辑;

        用 requests 实现 Ajax 数据的爬取;

        将每部电影的数据保存成一个 JSON 数据文件;

三、初步探索:如何判断网页是经js渲染过的?        

        尝试用之前的 requests 来直接提取页面,看看会得到怎样的结果。用最简单的代码实现一下 requests 获取首页源码的过程,代码如下:

import requestsurl = 'https://spa1.scrape.center/page/1'html = requests.get(url).textprint(html)

 运行结果如下:

可以看到我们只爬取到了这么一点 HTML 内容,只是一个空壳,没有数据,也就是说在 HTML 中我们只能在源码中看到引用了一些 JavaScript 和 CSS 文件,并没有观察任何有关电影数据的信息。

        如果遇到这样的情况,说明我们现在看到的整个页面是通过 JavaScript 渲染得到的,浏览器执行了 HTML 中所引用的 JavaScript 文件,JavaScript 通过调用一些数据加载和页面渲染的方法,才最终呈现了图中所示的页面。

        在一般情况下,这些数据都是通过 Ajax 来加载的, JavaScript 在后台调用这些 Ajax 数据接口,得到数据之后,再把数据进行解析并渲染呈现出来,得到最终的页面。所以说,要想爬取这个页面,我们可以通过直接爬取 Ajax 接口获取数据。

        在上一课时中,我们已经了解了用 Ajax 分析的基本方法。下面我们就来分析下 Ajax 接口的逻辑并实现数据爬取吧。

四、爬取列表页

4.1 分析Ajax接口逻辑

        首先我们来分析下列表页的 Ajax 接口逻辑,打开浏览器开发者工具,切换到 Network 面板,勾选上 「Preserve Log」并切换到 「XHR」选项卡,重新刷新页面,然后点击第 2 页、第 3 页、第 4 页的按钮,这时候可以看到页面上的数据发生了变化,同时在开发者工具下方会监听到几个 Ajax 请求,如图所示:

        由于我们切换了 10页,所以这里正好也出现了 10个 Ajax 请求,我们可以任选一个点击查看其请求详情,观察其请求的 URL、参数以及响应内容是怎样的,如图所示:

这里我们点开第 2 个结果,观察到其 Ajax 接口请求的 URL 地址为:https://spa1.scrape.center/api/movie/?limit=10&offset=10,这里有两个参数,一个是 limit,其值为 10,一个是 offset,它的值也是 10。 

        通过观察多个 Ajax 接口的参数,我们可以发现这么一个规律:limit 的值一直为 10,这就正好对应着每页 10 条数据;offset 的值在依次变大,页面每加 1 页,offset 就加 10,这就代表着页面的数据偏移量,比如第 2 页的 offset 值为 10 代表跳过 10 条数据,返回从第 11 条数据开始的结果,再加上 limit 的限制,就代表返回第 11~20 条数据的结果。

4.2 观察响应的数据

接着我们再观察下响应的数据,切换到 Preview 选项卡,结果如图所示。 

        可以看到结果是一些 JSON 数据,它有一个 results 字段,这是一个列表,列表的每一个元素都是一个字典。观察一下字典的内容,发现我们可以看到对应的电影数据的字段了,如 name、alias、cover、categories,对比下浏览器中的真实数据,各个内容是完全一致的,而且这个数据已经非常结构化了,完全就是我们想要爬取的数据,真是得来全不费工夫。、

        这样的话,我们只需要把所有页面的 Ajax 接口构造出来,那么所有的列表页数据我们都可以轻松获取到了。

4.3 代码实现

(1)导入库

我们先定义一些准备工作,导入一些所需的库并定义一些配置,代码如下:

import requestsimport logginglogging.basicConfig(level=logging.INFO,format='%(asctime)s - %(levelname)s: %(message)s')INDEX_URL = 'https://dynamic1.scrape.center/api/movie/?limit={limit}&offset={offset}'

(2)定义一个通用的爬取方法

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)

        定义一个 scrape_api 方法,和之前不同的是,这个方法专门用来处理 JSON 接口,最后的 response 调用的是 json 方法,它可以解析响应的内容并将其转化成 JSON 字符串

(3)定义一个爬取列表页的方法

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

        定义了一个 scrape_index 方法,用来接收参数 page,page 代表列表页的页码。构造了一个 URL,通过字符串的 format 方法,传入 limit 和 offset 的值。这里的 limit 直接使用了全局变量 LIMIT 的值,offset 则是动态计算的,计算方法是页码数减 1 再乘以 limit,比如第 1 页的 offset 值就是 0,第 2 页的 offset 值就是 10,以此类推。构造好 URL 之后,直接调用 scrape_api 方法并返回结果即可。

这样我们就完成了列表页的爬取,每次请求都会得到一页 10 部的电影数据。

        由于这时爬取到的数据已经是 JSON 类型了,所以我们不用像之前一样去解析 HTML 代码来提取数据,爬到的数据就是我们想要的结构化数据,因此解析这一步这里我们就可以直接省略啦。

到此为止,我们就能成功爬取列表页并提取出电影列表信息了。

 五、爬取详情页

5.1分析详情页

        这时候我们已经可以拿到每一页的电影数据了,但是实际上这些数据还缺少一些我们想要的信息,如剧情简介等,所以我们需要进一步进入到详情页来获取这些内容。

        稍加观察我们就可以发现,Ajax 请求的 URL https://spa1.scrape.center/detail/2后面有一个参数是可变的,这个参数就是电影的 id,这里是 2,对应《这个杀手不太冷》这部电影。所以如果我们想要获取 id 为 50 的电影,只需要把 URL 最后的参数改成 50 即可,即 https://spa1.scrape.center/detail/50/,请求这个新的 URL 我们就能获取 id 为 50 的电影所对应的数据了。

同样的,它响应的结果也是结构化的 JSON 数据,字段也非常规整,我们直接爬取即可。

5.2 如何将详情页与列表页关联起来

        分析了详情页的数据提取逻辑,那么怎么把它和列表页关联起来呢?这个 id 又是从哪里来呢?我们回过头来再看看列表页的接口返回数据,如图所示:

        可以看到列表页原本的返回数据就带了 id 这个字段,所以我们只需要拿列表页结果中的 id 来构造详情页中 Ajax 请求的 URL 就好了。 

5.3 代码实现


DETAIL_URL = 'https://spa1.scrape.center/detail/{id}'def scrape_detail(id):url = DETAIL_URL.format(id=id)return scrape_api(url)

        这里我们定义了一个 scrape_detail 方法,它接收参数 id。这里的实现也非常简单,先根据定义好的 DETAIL_URL 加上 id,构造一个真实的详情页 Ajax 请求的 URL,然后直接调用 scrape_api 方法传入这个 URL 即可。 

六、main总调用

接着,我们定义一个总的调用方法,将以上的方法串联调用起来,代码如下:

TOTAL_PAGE = 10def 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)

        这里我们定义了一个 main 方法,首先遍历获取页码 page,然后把 page 当成参数传递给 scrape_index 方法,得到列表页的数据。接着我们遍历所有列表页的结果,获取每部电影的 id,然后把 id 当作参数传递给 scrape_detail 方法,来爬取每部电影的详情数据,赋值为 detail_data,输出即可。

七、完整代码(简化版)

ref: 爬虫基础-Ajax爬取实战_request采集ajkx-CSDN博客  

import requests
import json
import logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s : %(message)s')BASE_URL = 'https://spa1.scrape.center/api/movie?limit={limit}&offset={offset}'
INDEX_URl = 'https://spa1.scrape.center/api/movie/{id}/''''通用爬取方法'''
def scrape_method(url):try:response = requests.get(url)if response.status_code == 200:return response.json()else:logging.error(f"请求{url}的状态码:{requests.status_codes}")except requests.RequestException as e:logging.error(e)'''爬取每一页的url'''
def scrape_baseUrl(limitA,offsetA):url = BASE_URL.format(limit=limitA,offset=offsetA)return scrape_method(url)'''爬取每一个电影'''
def scrape_indexRul(id):url = INDEX_URl.format(id = id)return scrape_method(url)def main():limitNum = 10# 一共11页offset依次传入0 10 20 .... 100for i in range(0,100,10):jsonObject = scrape_baseUrl(limitNum,i)logging.info(jsonObject.get('results'))# 此时results是dict类型,需要转换为json对象,再存入json文件for item in jsonObject.get('results'):id = item.get('id')indexData = scrape_indexRul(id)# 以追加的方式将每一部电影的所有属性都存入到bb.json文件中with open('bb.json', 'a', encoding='utf-8') as file:file.write(json.dumps(indexData, indent=2, ensure_ascii=False))file.write('\n')if __name__ == '__main__':main()

运行结果:

 

json文件:

 

受不了了。。。自己写的运行不出来。。。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/41388.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式八股RTOS与Linux---网络系统篇

前言 关于计网的什么TCP三次握手 几层模型啊TCP报文啥的不在这里讲,会单独分成一个计算机网络模块   这里主要介绍介绍lwip和socket FreeRTOS下的网络接口–移植LWIP 实际上FreeRTOS并不自带网络接口,我们一般会通过移植lwip协议栈让FreeRTOS可以通过网络接口收发数据,具体可…

全分辨率免ROOT懒人精灵-自动化编程思维-设计思路-实战训练

全分辨率免ROOT懒人精灵-自动化编程思维-设计思路-实战训练 1.2025新版懒人精灵-实战红果搜索关键词刷视频:https://www.bilibili.com/video/BV1eK9kY7EWV 2.懒人精灵-全分辨率节点识别(红果看广告领金币小实战):https://www.bili…

【更新中】【React】基础版React + Redux实现教程(Vite + React + Redux + TypeScript)

本项目是一个在react中,使用 redux 管理状态的基础版实现教程,用简单的案例练习redux的使用,旨在帮助学习 redux 的状态管理机制,包括 store、action、reducer、dispatch 等核心概念。 项目地址:https://github.com/Yv…

【MySQL】从零开始:掌握MySQL数据库的核心概念(四)

人们之所以不愿改变,是因为害怕未知。但历史唯一不变的事实,就是一切都会改变。 前言 这是我自己学习mysql数据库的第四篇博客总结。后期我会继续把mysql数据库学习笔记开源至博客上。 上一期笔记是关于mysql数据库的表格约束,没看的同学可以…

AP 场景架构设计(一) :OceanBase 读写分离策略解析

说明:本文内容对应的是 OceanBase 社区版,架构部分不涉及企业版的仲裁副本功能。OceanBase社区版和企业版的能力区别详见: 官网链接。 概述​ 当两种类型的业务共同运行在同一个数据库集群上时,这对数据库的配置等条件提出了较高…

CPU架构和微架构

CPU架构(CPU Architecture) CPU架构是指处理器的整体设计框架,定义了处理器的指令集、寄存器、内存管理方式等。它是处理器设计的顶层规范,决定了软件如何与硬件交互。 主要特点: 指令集架构(ISA, Instr…

6.4 模拟专题:LeetCode1419.数青蛙

1.题目链接:数青蛙 - LeetCode 2.题目描述 给定一个字符串 croakOfFrogs,表示青蛙的鸣叫声序列。每个青蛙必须按顺序发出完整的 “croak” 字符,且多只青蛙可以同时鸣叫。要求计算最少需要多少只青蛙才能完成该字符串,若无法完成…

Linux 搭建dns主域解析,和反向解析

#!/bin/bash # DNS主域名服务 # user li 20250325# 检查当前用户是否为root用户 # 因为配置DNS服务通常需要较高的权限,只有root用户才能进行一些关键操作 if [ "$USER" ! "root" ]; then# 如果不是root用户,输出错误信息echo "…

Leetcode 二进制求和

java solution class Solution {public String addBinary(String a, String b) {StringBuilder result new StringBuilder();//首先设置2个指针, 从右往左处理int i a.length() - 1;int j b.length() - 1;int carry 0; //设置进位标志位//从2个字符串的末尾向前遍历while(…

【NLP 49、提示工程 prompt engineering】

目录 一、基本介绍 语言模型生成文本的基本特点 提示工程 prompt engineering 提示工程的优势 使用注意事项 ① 安全问题 ② 可信度问题 ③ 时效性与专业性 二、应用场景 能 ≠ 适合 应用场景 —— 百科知识 应用场景 —— 写文案 应用场景 —— 解释 / 编写…

【NLP 43、文本生成任务】

目录 一、生成式任务 二、seq2seq任务 1.模型结构 2.工作原理 3.局限性 三、自回归语言模型训练 Decoder only 四、自回归模型结构:文本生成任务 —— Embedding LSTM 代码示例 🚀 数据文件 代码流程 Ⅰ、模型初始化 Ⅱ、前向计算 代码运行流程 Ⅲ、加载…

vscode 通过Remote-ssh远程连接服务器报错 could not establish connection to ubuntu

vscode 通过Remote-ssh插件远程连接服务器报错 could not establish connection to ubuntu,并且出现下面的错误打印: [21:00:57.307] Log Level: 2 [21:00:57.350] SSH Resolver called for "ssh-remoteubuntu", attempt 1 [21:00:57.359] r…

Linux之编辑器vim命令

vi/vim命令: 终端下编辑文件的首选工具,号称编辑器之神 基本上分为三种模式,分别是 命令模式(command mode)>输入vi的命令和快捷键,默认打开文件的时候的模式插入模式(insert mode&#x…

第一天学爬虫

阅读提示:我今天才开始尝试爬虫,写的不好请见谅。 一、准备工具 requests库:发送HTTP请求并获取网页内容。BeautifulSoup库:解析HTML页面并提取数据。pandas库:保存抓取到的数据到CSV文件中。 二、爬取步骤 发送请求…

MySQL实战(尚硅谷)

要求 代码 # 准备数据 CREATE DATABASE IF NOT EXISTS company;USE company;CREATE TABLE IF NOT EXISTS employees(employee_id INT PRIMARY KEY,first_name VARCHAR(50),last_name VARCHAR(50),department_id INT );DESC employees;CREATE TABLE IF NOT EXISTS departments…

windows下安装sublime

sublime4 alpha 4098 版本 下载 可以根据待破解的版本选择下载 https://www.sublimetext.com/dev crack alpha4098 的licence 在----- BEGIN LICENSE ----- TwitterInc 200 User License EA7E-890007 1D77F72E 390CDD93 4DCBA022 FAF60790 61AA12C0 A37081C5 D0316412 4584D…

激光线检测算法的FPGA实现

激光线检测算法的FPGA实现 1. 常见的激光线检测算法 激光线检测中常用的三种算法 MAX(最大值法)、THRESH(阈值法)、COG(灰度重心法) 分别具有以下特点和工作原理: 1.1 MAX(最大值法…

小样本微调大模型

一、环境搭建 conda create -n dseek python=3.10 conda activate dseek pip install bitsandbytes Pip install numpy python -m pip install --upgrade pip setuptools wheel 安装cuda,torch,Unsloth, huggingface,wandb等,见前述章节; 微调服务器配置:单机笔记本显卡4…

深入理解指针(2)(C语言版)

文章目录 前言一、数组名的理解二、使用指针访问数组三、一维数组传参的本质四、冒泡排序五、二级指针六、指针数组七、指针数组模拟二维数组总结 前言 在上一篇文章中,我们初步了解了指针的基本概念和用法。今天,我们将继续深入探索指针在数组、函数传…

高效内存管理:x86-64架构中的分页机制

在 x86-64 架构的世界里,内存分页机制扮演着举足轻重的角色,它就像是一座桥梁,连接着虚拟地址与物理地址。简单来说,内存分页机制就是将线性地址(也就是虚拟地址)切分成一个个固定大小的页,并把…