Python:爬取动态网站

优采云 发布时间: 2022-06-18 01:00

  Python:爬取动态网站

  邮箱:

  目录

  4. 完整代码

  5. 相关推文

  温馨提示: 文中链接在微信中无法生效。请点击底部「阅读原文」。或直接长按/扫描如下二维码,直达原文:

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

  1. 动态网页特征

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

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

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

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

  同样的,如果涉及多页的数据,需要分析接口的变化规律:

  3. 实战案例

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

  3.1 分析网页结构

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

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

  

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

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

  3.2 请求接口数据

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

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

  也就是说,这个数据接口由 1 个主结构和 8 个参数构成。关于参数需要注意两点,一是有没有这个参数会不会影响数据获取,二是这个参数的含义和变化规律。具体的确定方法只有不断尝试:

  当然,有时候不知道含义也可以爬取数据,但是建议还是了解一下,一般不清楚含义但必需的参数保持默认值即可。在这里,我们省略一下不断尝试的结果。最终,发现了部分参数的基本含义:

  接下来我们就可以复制请求头,构造请求参数,请求需要的数据。

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

  查看返回结果, 代表请求数据成功。如果是 403 或 404 则说明请求不成功,可能需要检查电脑网络是否通畅、目标网址是否可以正常访问、headers 是否有正确设置等。

  3.3 解析网页数据

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

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

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

  3.4 储存爬取数据

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

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

  3.5 循环爬取数据

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

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

  4. 完整代码

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

  5. 相关推文

  Note:产生如下推文列表的 Stata 命令为:

  lianxh python, m

  安装最新版 lianxh 命令:

  ssc install lianxh, replace

  专题:文本分析-爬虫专题:Python-R-Matlab

  

  课程推荐:因果推断实用计量方法

  主讲老师:丘嘉平教授

  课程主页:

  

  New! Stata 搜索神器:lianxh 和 songbl

  搜: 推文、数据分享、期刊论文、重现代码 ……

  安装:

  . ssc install lianxh

  . ssc install songbl

  使用:

  . lianxh DID 倍分法

  . songbl all

  

  关于我们

  

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线