Ajax 分析与爬取实战

准备工作

  • 安装好 Python3
  • 了解 Python HTTP 请求库 requests 的基本用法
  • 了解 Ajax 基础知识和分析 Ajax 的基本方法

爬取目标

  以一个示例网站来实验一下 Ajax 的爬取,链接为:https://spa1.scrape.center/,该示例网站的数据请求是通过 Ajax 完成的,页面的内容是通过 JavaScript 渲染出来的。

  这个网格同样能实现翻页,可以单击页面最下方的页码来切换到下一页。

  单击每部电影进入对应的详情页,这些页面的结构也是一样的。

  要爬取的数据包括电影的名称、封面、类别、上映日期、评分、剧情简介等信息。

  要完成的目标:

  • 分析页面的加载逻辑
  • 用 requests 实现 Ajax 数据的爬取
  • 将每部电影的数据分别保存到 MongoDB 数据库

初步探索

  先尝试用之前的 requests 直接提取页面。

import requests

url = 'https://spa1.scrape.center/'
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.17b3aaa5.js rel=preload as=script><link href=/js/chunk-vendors.683ca77c.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.683ca77c.js></script><script src=/js/app.17b3aaa5.js></script></body></html>

  爬取的结果只有只有一点HTML内容,在浏览器中打开却可以看到很多信息。

  在 HTML 中,只能看到源码引用的一些 JavaScript 和 CSS 文件,并没有观察到任何电影信息。

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

  这些电影数据一般是通过Ajax加载的,JavaScript 在后台调用 Ajax 数据接口,得到数据之后,在对数据进行解析并渲染呈现出来的,得到最终的页面。所以要想爬取这个页面,直接爬取 Ajax 接口,再获取数据就好了。

  了解了 Ajax 分析的基本方法,下面分析 Ajax 接口的逻辑并实现数据爬取。

爬取列表页

  首先分析列表页的 Ajax 接口逻辑,打开浏览器开发者工具,切换到 Network 面板,勾选 Preserve Log 并切换到 XHR 选项卡。

  接着重新刷新页面,再单击第 2 页、第 3 页、第 4 页的按钮,这是可以观察到不仅页面上的数据发生了变化,开发者工具下方也监听到了几个 Ajax 请求。

  切换了 4 页,每次翻页也出现了对应的 Ajax 请求。 可以点击查看其请求详情,观察请求 URL 、参数和响应内容是怎样的。

  点开最后一个结果,观察到其 Ajax 接口的请求 URL 为 https://spa1.scrape.center/api/movie/?limit=10&offset=30,这里有两个参数:一个是limit,这里是 10;一个是 offset,这里是 30。

  观察多个 Ajax 接口的参数,可以总结出这个一个规律:limit 一直为 10,正好对应每页的 10 条数据;offset 在依次变大,页数每增加 ,offset 就加 10,因此其代表页面上的数据偏移量。例如第 2 页的 offset 为 10 就代表跳过 10 条数据,返回从 11 条数据开始的内容,再加上 limit 的限制,最终页面呈现的就是第 11 条 至第 12 条数据。

  观察一下响应内容:

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

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

  引入了 requests 库和 logging 库,并定义了 logging 的基本配置。接着定义了 INDEX_URL ,这里把 limit 和 offset 预留出来变成占位符,可以动态传入参数构造一个完整的列表页 URL。

  下面实现一下详情页的爬取。还是和原来一样,定义一个通用的爬取方法,代码如下:

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 字符串。

  接着在此基础上,定义一个爬取列表页的方法:

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

  定义一个 scrape_index 方法,接收一个参数 page,该参数代表列表页的页码。

  scrape_index 方法中,先构造了一个 url,通过字符串的 format 方法,传入 limit 和 offset 方法的值。这里 limit 直接使用了全局变量的 LIMIt值;offset 则是动态计算的,计算方法是页码数减一再乘 limit,例如第一页的 offset 是 0,第二页的 offset 就是 10,以此类推,构造好 url 后,直接调用 scrape_api 方法并返回结果即可。

  完成了列表页的爬取,每次发送 Ajax 请求都会得到 10 部电影的数据信息。

  由于这是爬取的数据已经是 JSON 类型了, 所以无需像之前那样去解析 HTML 代码来提取数据,爬到的数据已经是想要的结构化数据。

  这样秩序构造出所有页面的 Ajax 接口,就可以轻松获取所有列表页的数据了。

爬取详情页

  虽然已经拿到每一页的电影数据,但是实际上缺少一些信息,如剧情简介等信息,所以需要进一步进入详情页来获取这些信息。

  单击任意一部电影,如《霸王别姬》,进入其详情页,可以发现此时的页面 URL 已经变成了 https://spa1.scrape.center/detail/1,页面也成功展示了《教父》详情页的信息。

  另外可以观察到开发者工具中多了一个 Ajax 请求,其 URL 为
https://spa1.scrape.center/api/movie/1/,通过 Preview 选项卡也能看到 Ajax 请求对应的响应信息。

  稍加观察就可以发现,Ajax 请求的 URL 后面有一个参数是可变的,这个参数是电影的 id,这里是 30,对应的是《完美世界》这部电影。

  如果想要获取 id 为 50 的电影,只需要吧 URL 最后的参数改成 50 即可,即 https://spa1.scrape.center/detail/50/,请求这个新的 URL 便能获取 id 为 50 电影对应的数据了。

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

  详情页的数据提取逻辑分析完毕,继续考虑怎么和列表页关联起来,电影 id 从哪里来这些问题。回过头来看一下列表页的接口返回数据。

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

  先定义一个详情页的爬取逻辑,代码如下:

DETAIL_URL = 'https://spa1.scrape.center/api/movie/{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 即可。

  最后,定义一个总的调用方法,对以上方法串联调用,代码如下:

TOTAL_PAGE = 10
def main():
for page in range(1, TOTAL_PAGE + 1):
index_data = scrape_index(page)
for item in index_data.get('result'):
id = item.get('id')
detail = scrape_detail(id)
logging.info('detail data %s', detail_data)
if __name__ == '__main__':
main()

  定义一个 main 方法,该方法首先遍历获取页码 page,然后把 page 作为一个参数传递给 scrape_index 方法,得到列表的数据。接着遍历每个列表的每个结果,获取每部电影的 id。之后把 id 当作参数传递给 scrape_detail 方法来爬取每部电影的详情数据,并将此数据赋值给 detail_data,最后输出 detail_data 即可。运行结果如下:

  整个爬取工作已经完成,这里会依次爬取每个列表页的 Ajax 接口,然后依次爬取每部电影的详情页 Ajax 接口,并打印出每部的 Ajax 接口响应数据,而且都是 JSON 格式。至此所有电影的详情数据都爬取到了。

保存数据

  成功提取详情页信息之后,下一步就是保存数据。可以保存到MongoDB中。

  保存之前,确保有一个可以正常连接和使用的 MongoDB 数据库,以本地的 localhost 的 MongoDB 数据库为例来操作。

  将数据导入 MongoDB 需要用到 PuMongo 这个库。引入并配置:

MONGO_CONNECTION_STRING = 'mongodb://localhost:27017'
MONGO_DB_NAME = 'movies'
MONGO_COLLECTION_NAME = 'movies' client = pymongo.MongoClient(MONGO_CONNECTION_STRING)
db = client['movies']
collection = db['movies']
  • MONGO_CONNECTION_STRING:MongoDB的连接字符串,里面定义的是MongoDB的基本连接信息,这里是 host、port,还可以定义用户名、密码等内容。
  • MONGO_DB_NAME:MongoDB 数据库的名称。
  • MONGO_COLLECTION_NAME:MongoDB 的集合名称。

然后用MongoClient声明了一个连接对象client,并依次声明了存储数据的数据库和集合。

  定义了一个 save_data 方法,接收一个参数 data,也就是上面提取电影详情信息。这个方法里调用了 update_one 方法,其第一个参数为查询条件,根据name 进行查询:第二个参数是data对象本身,就是所有的数据,这里我们用 $set 操作符表示更新操作;第三个参数很关键,这里实际上是upsert参数,如果把它设置为 True,就可以实现存在即更新,不存在插人的功能,更新时会参照第一个参数设置的name 字段,所以这样可以防止数据库中出现同名的电影数据。

注意 实际上电影可能有同名现象,但此处场景下的爬取数据没有同名情况,当然这里更重要的是实现 MongoDB 的去重操作。

  改写 main 方法:

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)
logging.info('data saved successfully')

  增加了对 save_data 方法的调用,并添加一些日志信息。

 运行结果:

  连接数据库查看爬取结果:

Ajax分析与爬取实战的更多相关文章

  1. PYTHON 爬虫笔记九:利用Ajax+正则表达式+BeautifulSoup爬取今日头条街拍图集(实战项目二)

    利用Ajax+正则表达式+BeautifulSoup爬取今日头条街拍图集 目标站点分析 今日头条这类的网站制作,从数据形式,CSS样式都是通过数据接口的样式来决定的,所以它的抓取方法和其他网页的抓取方 ...

  2. Ajax数据的爬取(淘女郎为例)

    mmtao Ajax数据的爬取(淘女郎为例) 如有疑问,转到 Wiki 淘女郎模特抓取教程 网址:https://0x9.me/xrh6z 判断一个页面是不是 Ajax 加载的方法: 查看网页源代码, ...

  3. 爬虫七之分析Ajax请求并爬取今日头条

    爬取今日头条图片 这里只讨论出现的一些问题,代码在最下面github链接里. 首先,今日头条取消了"图集"这一选项,因此对于爬虫来说效率降低了很多: 在所有代码都完成后,也许是爬取 ...

  4. Java爬虫_资源网站爬取实战

    对 http://bestcbooks.com/  这个网站的书籍进行爬取 (爬取资源分享在结尾) 下面是通过一个URL获得其对应网页源码的方法 传入一个 url  返回其源码 (获得源码后,对源码进 ...

  5. 初识scrapy,美空网图片爬取实战

          这俩天研究了下scrapy爬虫框架,遂准备写个爬虫练练手.平时做的较多的事情是浏览图片,对,没错,就是那种艺术照,我骄傲的认为,多看美照一定能提高审美,并且成为一个优雅的程序员.O(∩_∩ ...

  6. python爬虫调用搜索引擎及图片爬取实战

    实战三-向搜索引擎提交搜索请求 关键点:利用搜索引擎提供的接口 百度的接口:wd="要搜索的内容" 360的接口:q="要搜索的内容" 所以我们只要把我们提交给 ...

  7. Ajax介绍及爬取哔哩哔哩番剧索引追番人数排行

    Ajax,是利用JavaScript在保证页面不被刷新,页面链接不改变的情况下与服务器交换数据并更新部分网页的技术.简单的说,Ajax使得网页无需刷新即可更新其内容.举个例子,我们用浏览器打开新浪微博 ...

  8. Python知乎热门话题数据的爬取实战

    import requestsfrom pyquery import PyQuery as pq url = 'https://www.zhihu.com/explore'headers = { 'u ...

  9. python爬虫实战---爬取大众点评评论

    python爬虫实战—爬取大众点评评论(加密字体) 1.首先打开一个店铺找到评论 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经 ...

  10. python爬取微信小程序(实战篇)

    python爬取微信小程序(实战篇) 本文链接:https://blog.csdn.net/HeyShHeyou/article/details/90452656 展开 一.背景介绍 近期有需求需要抓 ...

随机推荐

  1. 技术干货| 阿里云基于Hudi构建Lakehouse实践探索「内附干货PPT下载渠道」

    ​简介: 阿里云高级技术专家王烨(萌豆)在Apache Hudi 与 Apache Pulsar 联合 Meetup 杭州站上的演讲整理稿件,本议题介绍了阿里云如何使用 Hudi 和 OSS 对象存储 ...

  2. 表格存储 SQL 查询多元索引

    ​简介: 多元索引是表格存储产品中一个重要的功能,多元索引使用倒排索引技术为表格存储提供了非主键列上的快速检索功能,另外也提供了统计聚合功能.表格存储近期开放了SQL查询功能,SQL引擎默认从原始表格 ...

  3. Java根据URL截图的4种方式

    方案选择 XHTMLRenderer(不要用) PhantomJs(三方库,已停更) Puppeteer(Chrome团队开发和维护) Selenium(支持多浏览器.多语言,服务器需要安谷歌浏览器) ...

  4. 羽夏闲谈——解决 MSI 安装包指定账户已存在

    序   前几天用VS2022,升级到17.1.0版本,发现模板用不了了,但能正常打开之前用它创建的项目.我重装试图修复该问题,解决雪上加霜,报错如下: 未能安装包"Microsoft.Vis ...

  5. iOS LLVM 中的宏定义

    在阅读 Objc 库源码时常常会遇到很多宏定义,比如宏 SUPPORT_INDEXED_ISA.SUPPORT_PACKED_ISA,代码如下所示: // Define SUPPORT_INDEXED ...

  6. 鸿蒙Blank

    Blank组件占剩余空间,类似占位组件一样

  7. three.js 物体要使用光线投射技术,计算是否点击位置与物体有交叉

    原生 DOM 还用原生的 DOM 点击事件,要注意开启 pointerEvents CSS3DRenderer 是一个新的渲染器,需要在渲染循环调用并适配 labelRenderer.domEleme ...

  8. Nokia 5GC 产品概览

    目录 文章目录 目录 Nokia SR OS Nokia NSP NFM-P Nokia 7750 SR-MG 5G User Plane Forwarding Mobile Gateway Non- ...

  9. c语言在Linux中的使用

    gcc版本升级 如何验证gcc正常使用,编译c以及运行 过程 要验证GCC(GNU Compiler Collection)是否正常使用,您可以按照以下步骤进行操作: 检查GCC是否安装:打开终端或命 ...

  10. npm包离线安装

    npm包离线安装 npm包的安装,在Internet联网机器上通过npm install轻松搞定的事情,在离线或者纯内网环境下就变得异常艰难,本文就来讲一讲离线安装npm包的方法. 通过 npm in ...