15158846557 在线咨询 在线咨询
15158846557 在线咨询
所在位置: 首页 > 营销资讯 > 网站运营 > Python如何爬动态网页,详细教程,小菜鸟一个?

Python如何爬动态网页,详细教程,小菜鸟一个?

时间:2024-02-10 16:45:01 | 来源:网站运营

时间:2024-02-10 16:45:01 来源:网站运营

Python如何爬动态网页,详细教程,小菜鸟一个?:
在之前的推文中,我们介绍了如何爬取一个简单的静态网站——「Python 爬取静态网站:以历史天气为例」,但是在实际过程中,常常会遇到需要爬取动态网站数据的情况。在本文中,我们也将通过一个比较简单的案例,来介绍爬取动态网站数据的基本思路和步骤。

1. 动态网页特征

首先,简单回顾一下动态网页的特征:

从源代码看,动态网页的数据不会出现在网页源代码中,而是被 “藏” 起来了; 从网址特征看,请求新数据时 (如翻页),网址不会变化。

2. 动态网页爬取的基本思路

动态网页数据爬取通常有两种方法:

分析数据接口,找到数据藏在哪,然后请求接口的数据; 通过 Selenium 模拟浏览器点击的方式获取数据。 在本次介绍中,我们将通过获取接口的方式来爬取动态网页的数据。由于动态网页结构会更加复杂一些,我们主要的精力是在解析网页结构这一步。但是在找到了数据接口后 (知道数据 “藏” 在哪),数据的爬取也比较简单。具体来看,爬取动态网页数据主要可分为以下几步:

分析网页结构,查找数据接口; 构造请求头,请求接口数据; 解析接口数据; 储存数据。 同样的,如果涉及多页的数据,需要分析接口的变化规律:

分析单页网页结构,查找数据接口; 分析接口变化规律,构造接口参数; 循环请求、获取并解析数据; 储存数据。

3. 实战案例

接下来,我们以爬取 bilibili 视频评论为例,来具体介绍如何通过 Python 爬取动态网页的数据。主要内容包括:

如何分析动态网页结构、查找数据接口; 如何请求接口数据; 如何解析 json 格式数据; 如何把数据实时存入 csv 文件; 如何循环爬取多页数据。

3.1 分析网页结构

在动态网页的数据爬取中,分析网页结构至关重要。因为我们需要找到数据 “藏” 在哪,否则不知道应该去哪请求数据。在本案例中,我们将爬取「bilibili」的数据,具体选择 bilibili 入站第一名的视频「【才浅】15 天花 20 万元用 500 克黄金敲数万锤纯手工打造三星堆黄金面具」的评论数据。

按照惯例,我们先看一下网页源代码,由于信息太多了,可以直接使用搜索功能。在网页源代码页面,搜索评论内容,发现没有这个数据。

那怎么办呢?我们就要找一找这个数据到底藏在哪了。通常,查找动态网页的数据接口有以下几个步骤:

在页面右键鼠标选择 检查; 在检查页面选择 Network; 在 Network 页面选择类型,数据接口一般藏在 Fetch/XHR 或者 JS 中; 找到数据接口。




问题又来了,动态网页里的数据很多,有时候就算筛选了类型,也还是有很多页面。要找到我们需要的数据,真的是大海捞针,那怎么办呢?有一个小技巧,就是利用搜索功能。在检查页面,搜索一下评论内容,立马就找到了数据藏在哪里!

3.2 请求接口数据

历经千辛万苦,终于找到了数据藏的位置,接下来就是要把数据获取下来。这里简单三个步骤就可以完成:

查找接口的网址:分析接口网址的情况; 确定请求头数据:通常请求动态网页数据需要比较完整的请求头,这时可以直接把 Request Headers 里面的内容直接复制; 请求数据:在有了数据接口的位置后,先尝试是否能够成功获取数据,可以直接通过 requests 请求数据。

我们先来看看接口网址 (Request URL) 的情况,可以看到网址包括以下几个部分:

主要结构:https://api.bilibili.com/x/v2/reply/main; 网址参数:callback、jsonp、next、type、oid、mode、plat、_。 也就是说,这个数据接口由 1 个主结构和 8 个参数构成。关于参数需要注意两点,一是有没有这个参数会不会影响数据获取,二是这个参数的含义和变化规律。具体的确定方法只有不断尝试:

通过增删参数看是否会影响请求的结果,或者请求结果有何变化; 通过变化网页看接口地址的变化规律,比如翻页、评论排序等,看参数如何变化。 当然,有时候不知道含义也可以爬取数据,但是建议还是了解一下,一般不清楚含义但必需的参数保持默认值即可。在这里,我们省略一下不断尝试的结果。最终,发现了部分参数的基本含义:

callback:代表一个获取数据的查询动作和时间戳,不是必需; jsonp:代表获取的数据格式,不是必需; next:代表页码,翻页循环时需要; type:含义暂时不明,但是必需,且不变,值为 1; oid:代表视频的 av 号,如果需要爬取多个视频的评论,需要从这个参数入手; mode:代表评论的排序方式,2 = 按时间倒序,3 = 按热度排序 (默认排序)。不是必需,默认值为 3; plat:含义暂时不明,不是必需; _:代表当前的 Unix 时间戳,不是必需。 接下来我们就可以复制请求头,构造请求参数,请求需要的数据。

# 导入模块import requestsimport time# 网址url = "https://api.bilibili.com/x/v2/reply/main" # 接口网址的主要结构# 请求头数据headers = { 'accept': '*/*', 'accept-encoding': 'gzip, deflate, br', 'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8', 'referer': 'https://www.bilibili.com/video/BV16X4y1g7wT', 'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': 'Windows', 'sec-fetch-dest': 'script', 'sec-fetch-mode': 'no-cors', 'sec-fetch-site': 'same-site', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ' '(KHTML, like Gecko) Chrome/100.0.4896.60 Safari/537.36' # 根据测试不需要 cookie 信息也可以获取数据 # 需要 cookie 的话在自己的浏览器中复制,这里涉及隐私就不放 cookie 信息了}# 构造请求参数params = { # 'callback': 'jQuery17201888299578386794_' + str(round(time.time() * 1000)), # 'jsonp': 'jsonp', 'next': 0, # 页码 'type': 1, 'oid': 715024588, # 视频av号 # 'mode': 3, # 评论排序方式 # 'plat': 1, # '_': str(round(time.time() * 1000)) # 生成当前时间戳}# 通过get方法请求数据response = requests.get(url, headers=headers, params=params)查看返回结果,<Response [200]> 代表请求数据成功。如果是 403 或 404 则说明请求不成功,可能需要检查电脑网络是否通畅、目标网址是否可以正常访问、headers 是否有正确设置等。

3.3 解析网页数据

请求成功后,我们再来看请求回来的数据是什么样的,如何根据获取自己需要的数据。回到数据接口中,我们可以看到数据是通过 json 格式存储的,而每条评论的数据在 data 下面的replies中。

因此,我们用 json 解析请求到的数据,并把需要的评论数据提取出来。

# 导入模块import jsonimport timeresponse.encoding = 'utf-8' # 修改编码格式data_json = json.loads(response.text) # 通过 json 解析数据comment_list = data_json['data']['replies'] # 获取 data 下面的 replies 列表comments = [] # 构建空列表保存每页的评论数据for i in range(len(comment_list)): # 循环获取每条评论的数据 comment = { 'id': comment_list[i]['rpid'], # 评论id # 评论时间,由时间戳转换 'time': time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(comment_list[i]['ctime'])), 'parent': comment_list[i]['parent'], # 父评论id 'like': comment_list[i]['like'], # 点赞数 'user_id': comment_list[i]['member']['mid'], # 评论用户id 'user_name': comment_list[i]['member']['uname'], # 用户名 'content': comment_list[i]['content']['message'] # 评论内容 # 需要其他数据的可以再在 json 中查看并获取对应的名称 } comments.append(comment) # 每页的评论数据

3.4 储存爬取数据

接下来,把爬取到的数据存入 csv 文件。当然,还是建议爬取一页保存一页。同时,使用 utf-8 格式保存数据,因此打开数据文件时,也要使用同样的格式。

# 导入模块import csv# 保存数据的文件路径save_path = 'bilibili.csv'# 将数据写入 csv with open(save_path, 'a', newline='', encoding='utf-8') as fp: csv_header = ['id', 'time', 'parent', 'like', 'user_id', 'user_name', 'content'] # 设置表头,即列名 csv_writer = csv.DictWriter(fp, csv_header) # 如果文件不存在,则写入表头;如果文件已经存在,则直接追加数据不再次写入表头 if fp.tell() == 0: csv_writer.writeheader() csv_writer.writerows(comments) # 写入数据

3.5 循环爬取数据

终于成功获取了一页的数据,接下来就要循环获取更多数据了。这里也分为三个步骤:

分析接口网址的变化规律 (通常是参数的变化); 根据规律构造网址; 循环获取数据。 由于前面已经详细分析过接口的参数变化,这里不再具体说明。通过分析,翻页变化的参数是 next,所以只要变化这个参数就可以进行翻页。另外,如果要爬取不同视频的评论,则要通过 av 号来循环,也就是 oid 参数。

4. 完整代码

# -*- coding: utf-8 -*-# Author: W.Y.# Email: wangyingchn@outlook.com# Date: 2022/4/12# 导入模块import requests # 请求数据import time # 时间模块import json # json 模块,储存数据import csv # 保存数据# 请求数据def get_response(page): url = 'https://api.bilibili.com/x/v2/reply/main' # 接口网址的主要结构 # 请求头数据 headers = { 'accept': '*/*', 'accept-encoding': 'gzip, deflate, br', 'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8', 'referer': 'https://www.bilibili.com/video/BV16X4y1g7wT', 'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': 'Windows', 'sec-fetch-dest': 'script', 'sec-fetch-mode': 'no-cors', 'sec-fetch-site': 'same-site', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ' '(KHTML, like Gecko) Chrome/100.0.4896.60 Safari/537.36' # 根据测试不需要 cookie 信息也可以获取数据 # 需要 cookie 的话在自己的浏览器中复制,这里涉及隐私就不放 cookie 信息了 } # 构造请求参数 params = { # 'callback': 'jQuery17201888299578386794_' + str(round(time.time() * 1000)), # 'jsonp': 'jsonp', 'next': page, # 页码 'type': 1, 'oid': 715024588, # 视频av号 'mode': 3, # 评论排序方式 # 'plat': 1, # '_': str(round(time.time() * 1000)) # 生成当前时间戳 } # 通过get方法请求数据 response = requests.get(url, headers=headers, params=params) return response# 解析数据def parse_data(response): response.encoding = 'utf-8' # 修改编码格式 data_json = json.loads(response.text) # 通过 json 解析数据 comment_list = data_json['data']['replies'] # 获取 data 下面的 replies 列表 comments = [] # 构建空列表保存每页的评论数据 for i in range(len(comment_list)): # 循环获取每条评论的数据 comment = { 'id': comment_list[i]['rpid'], # 评论id 'time': time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(comment_list[i]['ctime'])), # 评论时间,由时间戳转换 'parent': comment_list[i]['parent'], # 父评论id 'like': comment_list[i]['like'], # 点赞数 'user_id': comment_list[i]['member']['mid'], # 评论用户id 'user_name': comment_list[i]['member']['uname'], # 用户名 'content': comment_list[i]['content']['message'] # 评论内容 # 需要其他数据的可以再在 json 中查看并获取对应的名称 } comments.append(comment) # 每页的评论数据 return comments# 保存数据def save_data(comments, save_path): with open(save_path, 'a', newline='', encoding='utf-8') as fp: # 设置表头,即列名 csv_header = ['id', 'time', 'parent', 'like', 'user_id', 'user_name', 'content'] csv_writer = csv.DictWriter(fp, csv_header) # 如果文件不存在,则写入表头;如果文件已经存在,则直接追加数据不再次写入表头 if fp.tell() == 0: csv_writer.writeheader() csv_writer.writerows(comments) # 写入数据# 定义爬取函数def crawler(page, save_path): time.sleep(2) # 暂停 2 秒,避免请求过于频繁 response = get_response(page) # 请求数据 comments = parse_data(response) # 解析数据 save_data(comments, save_path) # 储存数据 print(f'成功爬取第{page+1}页')if __name__ == '__main__': save_file = 'bilibili.csv' # 保存路径 total_counts = 1000 # 爬取 1000 条评论 # 如果要爬取所有评论,可以改成全部评论数。 # 如果要爬取多个视频的评论,可以通过下面的代码,爬取第一页的时候返回所有的评论数 # total_counts = data_json['data']['cursor']['all_count'] # 页码循环,每页有 20 条评论,所以通过总评论数计算页码 for p in range(total_counts//20 + 1): crawler(p, save_file)详细内容参见连享会推文

相关推文

Note:产生如下推文列表的 Stata 命令为:
. lianxh Python
. songbl Python
安装最新版 lianxh/ songbl 命令:
. ssc install lianxh, replace
. ssc install songbl, replace

关键词:教程,详细,动态,小菜

74
73
25
news

版权所有© 亿企邦 1997-2025 保留一切法律许可权利。

为了最佳展示效果,本站不支持IE9及以下版本的浏览器,建议您使用谷歌Chrome浏览器。 点击下载Chrome浏览器
关闭