随笔:使用Python爬取知乎上相关问题的所有回答
项目中数据分析的需要自己从知乎某个专门的问题上爬数据,但众所周知,知乎的问题的显示方式有点胃疼(指滑动后下翻加载更多回答,还经常卡住),翻了翻网上的教程发现有的要么就是很老了要么就是付费的,本着开源共赢的原则,写一篇记录一下自己踩过的坑,也给后面人警醒。
阅读前必知:
- 本文的方法是2023年10月的,如果过了时间太久可能就不管用了,请注意时效性;
- 部分代码由GitHub Copliot完成,可能存在错误,但是结果应该没问题;
- 代码写的比较辣鸡勿喷,解决方案也有点繁琐,但能用的方法就是好方法~
看之前参考了知乎这篇文章
方法1 使用Web scraper
Web scraper是一个很好用的轻量级的0代码爬虫工具,只需要安装chrome插件就可以使用,在google商店搜就可以了,按F12打开是这样的:
具体使用过程这里不再赘述,记得一定要先选块再选内容。这个的原理和selenium类似,模拟滚到顶端然后再收集,其实这个用来轻量级爬虫是很好的,但对我的任务来说(我的任务有2k多条回答),很容易滑不到顶端然后出现闪退的情况,这里附上我的sitemap,对回答较少的问题应该是可以使用的 :
{"_id":"name","startUrl":["https://www.zhihu.com/question/xxxxxxxxx/answers/updated"],"selectors":[{"id":"block","parentSelectors":["_root"],"type":"SelectorElementScroll","selector":"div.List-item:nth-of-type(n+2)","multiple":true,"delay":2000,"elementLimit":2100},{"id":"content","parentSelectors":["block"],"type":"SelectorText","selector":"span[itemprop='text']","multiple":true,"regex":""},{"id":"user","parentSelectors":["block"],"type":"SelectorLink","selector":".AuthorInfo-name a","multiple":true,"linkType":"linkFromHref"},{"id":"date","parentSelectors":["block"],"type":"SelectorText",
"selector":".ContentItem-time span",
"multiple":true,"regex":""}]}
id就是名字(你这个任务的名字),然后url里面记得替换你要爬的问题id。
方法2 使用selenium
跟上面的原理差不多,滚动到最下面然后抓取页面,但跟上面存在相同的滚动满且卡顿、且知乎缓存导致爬不全的问题,这里也不多说直接附上代码,对小任务应该也是没问题的:
def scrape1(question_id):user_agents = ['Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36']url = f'https://www.zhihu.com/question/{question_id}' # 替换question_id# 创建一个Options对象,并设置headersoptions = Options()options.add_argument("user-agent=" + random.choice(user_agents))# 传入cookiecookies = json.load(open('cookie.json', 'r', encoding='utf-8'))# options.add_argument("--headless")# 创建WebDriver时传入options参数driver = webdriver.Chrome(options=options)driver.get(url)driver.delete_all_cookies()for cookie in cookies:driver.add_cookie(cookie)time.sleep(2)driver.refresh()time.sleep(5) # 等待页面加载完成# items = []# question = driver.find_element(By.CSS_SELECTOR, 'div[class="QuestionPage"] meta[itemprop="name"]').get_attribute(# 'content')# while True:# # 滚动到页面底部# print('scrolling to bottom')# driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")# time.sleep(random.randint(5, 8)) # 等待页面加载新内容的时间,根据实际情况进行调整## # 如果找到了页面底部元素就停止加载# try:# driver.find_element(By.CSS_SELECTOR, 'button.Button.QuestionAnswers-answerButton')# print('reached the end')# break# except:# pass#html = driver.page_source# 解析HTMLsoup = BeautifulSoup(html, 'html.parser')# 获取所有回答的标签answers = soup.find_all('div', class_='List-item')df = pd.DataFrame()contents = []answer_ids = []driver.quit()for answer in answers:# 获取回答的文本内容content = answer.find('div', class_='RichContent-inner').get_text()contents.append(content)df['answer_id'] = answer_idsdf['content'] = contentsdf.to_csv(f'{question_id}.csv', index=False, encoding='utf-8')
这里cookie自己准备,要么不好跳过最开始的登录过程。
方法3 使用requests配合beautiful soap
这也是我最后成功的方法,最主要的是支持断点接着工作(不用拖到底直接使用)
这里还参考了这篇文章:
https://blog.csdn.net/python03011/article/details/131307051?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169813072516800188539007%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=169813072516800188539007&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_ecpm_v1~times_rank-3-131307051-null-null.nonecase&utm_term=%E7%9F%A5%E4%B9%8E%E9%97%AE%E9%A2%98%E4%B8%8B%E6%89%80%E6%9C%89%E5%9B%9E%E7%AD%94&spm=1018.2226.3001.4450
原代码的核心代码是这样的:
#网址模板
template = 'https://www.zhihu.com/api/v4/questions/432119474/answers?include=data%5B*%5D.is_normal%2Cadmin_closed_comment%2Creward_info%2Cis_collapsed%2Cannotation_action%2Cannotation_detail%2Ccollapse_reason%2Cis_sticky%2Ccollapsed_by%2Csuggest_edit%2Ccomment_count%2Ccan_comment%2Ccontent%2Ceditable_content%2Cattachment%2Cvoteup_count%2Creshipment_settings%2Ccomment_permission%2Ccreated_time%2Cupdated_time%2Creview_info%2Crelevant_info%2Cquestion%2Cexcerpt%2Crelationship.is_authorized%2Cis_author%2Cvoting%2Cis_thanked%2Cis_nothelp%2Cis_labeled%2Cis_recognized%2Cpaid_info%2Cpaid_info_content%3Bdata%5B*%5D.mark_infos%5B*%5D.url%3Bdata%5B*%5D.author.follower_count%2Cbadge%5B*%5D.topics%3Bsettings.table_of_content.enabled%3B&offset={offset}&limit=5&sort_by=default&platform=desktop'for page in range(1, 100):#对第page页进行访问url = template.format(offset=page)resp = requests.get(url, headers=headers)#解析定位第page页的数据for info in resp.json()['data']:author = info['author']Id = info['id']text = info['excerpt']data = {'author': author,'id': Id,'text': text}#存入csvwriter.writerow(data)#降低爬虫对知乎的访问速度time.sleep(1)
但我试了下根本不符合我的要求,问题如下:
- 目前知乎改版后,excerpt属性并不能得到完整的答案;
- 目前知乎不用offset进行翻页了,而改用cursor,cursor很难找到规律,但实际上可以使用每个回答的next的指针。
成功思路
我的思路很简单,首先修改上面的代码获取answer_id,然后根据answer_id去爬每个对应的完整 回答。
首先说下模版网页如何获取。
我们点开我们想要的回答,刷新下找这个包:
[外链图片转存中…(img-pkPZH5Pz-1698149682893)]
这个就是我们要用的请求网址,可以看到offset一直是0,说明不管用了。
解决方法是先用一个起始的url0找到next:
import requests
import pandas as pd
import timetemplate = 'https://www.zhihu.com/api/v4/questions/30644408/feeds?cursor=1c4cacd45e70f24bd620bad51c605d59&include=data[*].is_normal,admin_closed_comment,reward_info,is_collapsed,annotation_action,annotation_detail,collapse_reason,is_sticky,collapsed_by,suggest_edit,comment_count,can_comment,content,editable_content,attachment,voteup_count,reshipment_settings,comment_permission,created_time,updated_time,review_info,relevant_info,question,excerpt,is_labeled,paid_info,paid_info_content,reaction_instruction,relationship.is_authorized,is_author,voting,is_thanked,is_nothelp;data[*].mark_infos[*].url;data[*].author.follower_count,vip_info,badge[*].topics;data[*].settings.table_of_content.enabled&limit=5&{offset}&order=default&platform=desktop&session_id=1698132896804376037'df = pd.DataFrame()
# df有三列,answer_id和content以及创建日期
df['answer_id'] = []
df['content'] = []
df['created_time'] = []answer_ids = []headers = {'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36'}cookies = {
# 填自己的z_0 cookie}
# 第一条使用模版,后面的都是next来获取
url0 = template.format(offset=0)
resp0 = requests.get(url0, headers=headers,cookies=cookies)
for data in resp0.json()['data']:answer_id = data['target']['id']# 添加answer_id到df中answer_ids.append(answer_id)
next = resp0.json()['paging']['next']for page in range(1,400):# 这里自己估算一下,每页是5条数据#对第page页进行访问resp = requests.get(next, headers=headers,cookies=cookies)print('正在爬取第' + str(page) + '页')for data in resp.json()['data']:answer_id = data['target']['id']# 添加answer_id到df中answer_ids.append(answer_id)next = resp.json()['paging']['next']time.sleep(3) # 这里是情况可快可慢# 将answer_ids写入df
df['answer_id'] = answer_ids
df.to_csv('answer_id.csv', index=True)
这样就得到了我们需要的回答的answer_id。
第二步,根据answer_id爬内容:
from bs4 import BeautifulSoup
import pandas as pd
import randomcontents = []batch = 0
for answer_id in answer_ids:print('正在爬取answer_id为{answer_id}的数据'.format(answer_id=answer_id))url = 'https://www.zhihu.com/question/30644408/answer/{answer_id}'.format(answer_id=answer_id)try:resp = requests.get(url, headers=headers, cookies=cookies)soup = BeautifulSoup(resp.text, 'html.parser')# 查找contentcontent = soup.find('div', class_='RichContent-inner').textcontents.append(content)print(content)except Exception as e:print(f'爬取answer_id为{answer_id}的数据时出现异常:{e}')breaktime.sleep(random.randint(1,4))# 每爬取100个回答就保存一次数据,保存在不同的文件中if len(contents) % 100 == 0:new_data = {'answer_id': answer_ids[:len(contents)], 'content': contents}new_df = pd.DataFrame(new_data)new_df.to_csv(f'text_{batch}.csv', index=True)batch += 1# new_data = {'answer_id': answer_ids[:len(contents)], 'content': contents}
# new_df = new_df.append(pd.DataFrame(new_data))
# new_df.to_csv('text1.csv', index=True)
这里爬100条保存一次,免得前功尽弃。
相关文章:

随笔:使用Python爬取知乎上相关问题的所有回答
项目中数据分析的需要自己从知乎某个专门的问题上爬数据,但众所周知,知乎的问题的显示方式有点胃疼(指滑动后下翻加载更多回答,还经常卡住),翻了翻网上的教程发现有的要么就是很老了要么就是付费的…...

ORB-SLAM安装过程遇到问题记录整理
一、ORB-SLAM2 1.c error: ‘decay_t’ is not a member of ‘std’ 如下图所示: 解决方法: 修改 ORB_SLAM的 CMAKELIST.txt文件, 将set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdc11") 修改为 set(CMAKE_CXX_STANDARD 14) 2…...
Ubuntu22.0.4安装svn服务
1、检查是否已安装 1.1、检查是否已安装 svnserve --version1.2、删除SVN遗留文件 sudo apt-get remove --purge subversion2、安装svn apt-get install subversion3、新建存储目录 sudo mkdir /data/svn sudo mkdir /data/svn/repository4、更改文件夹的读写权限 sudo…...
GNSS边坡位移监测仪在自然灾害应急能力提升工程领域的应用
GNSS边坡位移监测仪在自然灾害应急能力提升工程领域的应用 二、工作原理 GNSS的基本原理是测量出已知位置的卫星到用户接收机之间的距离,然后综合多颗卫星的数据就可知道接收机的具体位置。要达到这一目的,卫星的位置可以根据星载时钟所记录的时间在卫星…...

k8s客户端配置
K8s客户端安装 前提 K8s服务部署成功,如下 角色 IP地址 操作系统 主机名 Kubernetes版本 master节点 172.16.4.167 CentOS 7.9 k8s-master01 v1.28.2 工作节点1 172.16.4.168 CentOS 7.9 k8s-worker01 v1.28.2 工作节点2 172.16.4.169 CentOS 7.9…...

网络套接字编程
1.基础预备知识 1.1源ip和目的ip 在IP数据包头部中, 有两个IP地址, 分别叫做源IP地址, 和目的IP地址 源IP地址表示发起通信的设备的IP地址。它是数据包的出发点,标识了数据包的来源。当一个设备发送数据包到网络上的其他设备时,该数据包的源IP字段会被…...

Node编写更新用户信息接口
目录 前言 定义路由和处理函数 验证表单数据 实现更新用户基本信息的功能 前言 继前面几篇文章,本文介绍如何编写更新用户信息接口 定义路由和处理函数 路由 // 更新用户信息接口 router.post(/userinfo, userinfo_handler.updateUserinfo) 处理函数 // 导…...
Delphi解决 openssl DLL 与 Indy 的SSL/TLS 连接问题
昨天,突然间,我的一个 Delphi 程序无法连接到互联网上的各种WMS服务器。我收到以下错误消息: 使用 SSL 连接时出错。错误 1409442E:SSL 例程:ssl3_read_bytes:tlsv1 警报协议版本 由于我使用的是最新版本…...

单片机仿真设计打包项目
小伙伴们在仿真设计时会遇到各种各样的问题,网上的资料可能不全或者很贵。 这篇也不单纯为了打广告,主要是希望实实在在帮到学单片机的同学,大家不要一有问题就各种找dai zuo,做的好不好是一回事儿,关键是它费&#x…...

Java练习题-输出二维数组对角线元素和
✅作者简介:CSDN内容合伙人、阿里云专家博主、51CTO专家博主、新星计划第三季python赛道Top1🏆 📃个人主页:hacker707的csdn博客 🔥系列专栏:Java练习题 💬个人格言:不断的翻越一座又…...
Python调用ctype的动态库时出现的问题记录
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、动态库调用问题1.问题发现2.解决问题 总结 前言 提示:这里可以添加本文要记录的大概内容: 之前用公司算法同事写的c算法编译成的d…...

面试算法38:每日温度
题目 输入一个数组,它的每个数字是某天的温度。请计算每天需要等几天才会出现更高的温度。例如,如果输入数组[35,31,33,36,34],那么输出为[3,1,1,0ÿ…...
流程控制语句中的顺序结构、分支结构和循环结构以及示例和详细代码解释为什么这样写(1)
在流程控制语句中,我们通常可以将其分为三种基本结构:顺序结构、分支结构和循环结构。 1. 顺序结构:顺序结构是最简单的流程控制结构,代码按照编写的顺序依次执行,没有条件或循环的干扰。下面是一个顺序结构的示例代码…...
MFC Windows 程序设计[334]之自定义编辑框(附源码)
MFC Windows 程序设计[334]之自定义编辑框 程序之美前言主体运行效果核心代码逻辑分析结束语程序之美 前言 MFC是微软公司提供的一个类库(class libraries),以C++类的形式封装了Windows API,并且包含一个应用程序框架,以减少应用程序开发人员的工作量。其中包含大量Wind…...

MOS管特性及其几种常用驱动电路详解,电子工程师手把手教你
在电子工程中,MOS管(金属氧化物半导体场效应管)是一种非常重要的半导体元件。 在这篇文章中,我们将深入探讨MOS管的特性,以及几种常用的驱动电路的工作原理和设计方法。无论你是初学者还是经验丰富的电子工程师&#…...

C#,数值计算——分类与推理Phylo_wpgma的计算方法与源程序
1 文本格式 using System; using System.Collections.Generic; namespace Legalsoft.Truffer { public class Phylo_wpgma : Phylagglom { public override void premin(double[,] d, int[] nextp) { } public override double dminfn(double…...
Spring MVC 常用的注解
Controller:用于将一个类标记为 Spring MVC 控制器,处理 HTTP 请求和生成 HTTP 响应。RestController:类似于 Controller,但返回的数据会被自动转换为 JSON 或 XML 格式,通常用于构建 RESTful API。等于Controller Re…...

winodos下使用VS2022编译eclipse-paho.mqtt.c并演示简单使用的 demo
本文演示C语言如何使用eclipse-paho.mqtt.c库,包含自行编译库的步骤或者下载编译好的文件。 1.下载paho.mqtt.c库源码(zip 文件) 到官网选择C版本的paho源码进行下载 Eclipse Paho | The Eclipse Foundation 或者到下述连接下载 Releases ec…...

【Java 进阶篇】使用 Java 和 Jsoup 进行 XML 处理
XML(可扩展标记语言)是一种常用的数据交换格式,它被广泛用于在不同系统之间传递和存储数据。Java作为一种强大的编程语言,提供了多种方式来处理XML数据。其中,Jsoup 是一个流行的Java库,用于解析和操作XML文…...

QT图形视图框架绘制曲线图和Smith图
QT图形视图框架绘制曲线图和Smith图 QGraphicsView是Qt框架中的一个图形视图部件,用于显示和处理2D图形元素。它提供了强大的工具来创建交互式和自定义的图形应用程序。在绘制折线图和Smith图时,使用QGraphicsView有以下一些优点: 交互性&am…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...

R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
MySQL 主从同步异常处理
阅读原文:https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主,遇到的这个错误: Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一,通常表示ÿ…...
xmind转换为markdown
文章目录 解锁思维导图新姿势:将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件(ZIP处理)2.解析JSON数据结构3:递归转换树形结构4:Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...

【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...