随笔:使用Python爬取知乎上相关问题的所有回答

项目中数据分析的需要自己从知乎某个专门的问题上爬数据,但众所周知,知乎的问题的显示方式有点胃疼(指滑动后下翻加载更多回答,还经常卡住),翻了翻网上的教程发现有的要么就是很老了要么就是付费的,本着开源共赢的原则,写一篇记录一下自己踩过的坑,也给后面人警醒。

阅读前必知:

  • 本文的方法是2023年10月的,如果过了时间太久可能就不管用了,请注意时效性;
  • 部分代码由GitHub Copliot完成,可能存在错误,但是结果应该没问题;
  • 代码写的比较辣鸡勿喷,解决方案也有点繁琐,但能用的方法就是好方法~

看之前参考了知乎这篇文章

方法1 使用Web scraper

Web scraper是一个很好用的轻量级的0代码爬虫工具,只需要安装chrome插件就可以使用,在google商店搜就可以了,按F12打开是这样的:

image

具体使用过程这里不再赘述,记得一定要先选块再选内容。这个的原理和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条保存一次,免得前功尽弃。

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

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

相关文章

【详细】Java网络通信 TCP、UDP、InetAddress

一、网络程序设计基础 1.局域网与因特网 为了实现两台计算机的通信&#xff0c;必须用一个网络线路连接两台计算机&#xff08;服务器<-->网络<-->客户机&#xff09;。 服务器是指提供信息的计算机或程序&#xff0c;客户机是指请求信息的计算机或程序。网络用…

基于java+swing+mysql实现的仓库商品管理系统

JavaSwingmysql用户信息管理系统 一、系统介绍二、功能展示三、项目相关3.1 乱码问题3.2 如何将GBK编码系统修改为UTF-8编码的系统&#xff1f; 四、其它1.其他系统实现 五、源码下载 一、系统介绍 本系统实现了两个角色层面的功能&#xff0c;管理员可以管理用户、仓库、商品…

Pinia中如何实现数据持久化操作

使用vue3中的pinia&#xff0c;我们可以在多个页面间共享数据&#xff0c;但是一旦我们关闭或刷新页面&#xff0c;这些数据就会丢失&#xff0c;因此&#xff0c;我们需要有一种数据持久化的解决方案。在记录vue3 使用vue3中的pinia&#xff0c;我们可以在多个页面间共享数据&…

【App 抓包提示网络异常怎么破?】

背景 当你测试App的时候,想要通过Fiddler/Charles等工具抓包看下https请求的数据情况,发现大部分的App都提示网络异常/无数据等等信息。以“贝壳找房”为例: 455 x 705 Fiddler中看到的请求是这样的: 619 x 215 你可能开始找证书的问题:是不是Fiddler/Charles的证书没有…

数字化工厂:连接、集成与数据融合

随着科技的不断发展&#xff0c;数字化工厂管理系统逐渐成为制造业的重要趋势。数字化工厂的核心在于连接、集成与数据融合&#xff0c;通过这些技术手段&#xff0c;实现对设备、生产线、工厂、供应商、产品、客户等各个环节的全面优化&#xff0c;提升企业的生产效率和产品质…

NET-MongoDB的安装使用

一&#xff0e;下载 MongoDB 点击 Select package 选择自己所需版本后点击下载&#xff0c;本文选用Windows 6.0版本以上 二、配置MongoDB 在 Windows 上&#xff0c;MongoDB 将默认安装在 C:\Program Files\MongoDB 中。 将 C:\Program Files\MongoDB\Server\version_numbe…

安装python虚拟环境

什么是虚拟环境&#xff1a; 虚拟环境的意义&#xff0c;就如同 虚拟机 一样&#xff0c;它可以实现不同环境中Python依赖包相互独立&#xff0c;互不干扰。 环境准备 安装python &#xff08;到官网下载Download Python​配置环境变量&#xff0c;cmd进入命令行输入 python…

王道计网:数据链路层

一、导论 将网络层的数据报分组封装成帧。 本质上是数据链路层封装之后&#xff0c;在物理层传输bit流&#xff0c;中间站点又会向上到数据链路层&#xff0c;这是一个实际过程&#xff0c;但是在单独考虑数据链路层时&#xff0c;我们可以忽略经过的物理层&#xff0c;直接认为…

js实现将文本生成二维码(腾讯云cos)

示例 页面代码 import { getQCodeUrl } from /utils/cosInstance; import { PageContainer } from ant-design/pro-components; import { Access, useAccess } from umijs/max; import { Button, Image } from antd; import { useState } from react;const AccessPage: Reac…

16 用于NOMA IoT网络上行链路安全速率最大化的HAP和UAV协作框架

文章目录 摘要相关模型仿真实验仿真结果 摘要 优化无人机到HAP的信道分配、用户功率和无人机三维位置来研究上行安全传输解决非凸问题&#xff0c;采用K-means聚类算法&#xff0c;将成对的用户划分成不同的组&#xff0c;每个簇可以有相应的无人机服务&#xff0c;然后将构造…

vue3-admin-element框架登录如何修改?

1、找到vite.config.js文件 配置反向代理 2、找到src文件下的user.js文件 3、找到views文件下的login文件 打开找到comp文件夹找到LoginFrom.vue页面进去 把这个改成自己的账户数据&#xff08;密码不一致自行更改&#xff09; 4、找到store文件下的modules下的user.js 5、找到…

虹科 | 解决方案 | 汽车示波器 学校教学方案

虹科Pico汽车示波器是基于PC的设备&#xff0c;特别适用于大课堂的教学、备课以及与师生的互动交流。老师展现讲解波形数据&#xff0c;让学生直观形象地理解汽车的工作原理 高效备课 课前实测&#xff0c;采集波形数据&#xff0c;轻松截图与标注&#xff0c;制作优美的课件&…

pytest-yaml 测试平台-3.创建执行任务定时执行用例

前言 当项目用例编写完成后&#xff0c;需设置执行策略&#xff0c;可以用到定时任务设置每天几点执行。或者间隔几个小时执行一次。 创建定时任务 创建任务 勾选需要执行的项目以及运行环境 触发器可以支持2种方式&#xff1a;interval 间隔多久触发和 cron 表达式定时执行…

甲骨文真的要开放Java EE?

甲骨文表示&#xff0c;目前正在与可能的几个候选基金会&#xff0c;以及许可证持有者和社区在进行谈判。随着Java EE 8平台的确定&#xff0c;甲骨文在周四表示&#xff0c;目前正在考虑将Java Enterprise Edition技术转移到开源社区。 甲骨文在其博客中说道&#xff0c;这次的…

JAVA毕业设计104—基于Java+Springboot+Vue的医院预约挂号小程序(源码+数据库)

基于JavaSpringbootVue的医院预约挂号小程序(源码数据库)104 一、系统介绍 本系统前后端分离带小程序 小程序&#xff08;用户端&#xff09;&#xff0c;后台管理系统&#xff08;管理员&#xff0c;医生&#xff09; 小程序&#xff1a; 预约挂号&#xff0c;就诊充值&…

【LeetCode:1465. 切割后面积最大的蛋糕 | 贪心 + 排序】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

YTM32的增强型定时器eTMR外设模块详解

文章目录 eTMR外设简介eTMR工作机制系统框图引脚与信号计数器与时钟源输出比较模式PWM模式通道配对通道对的互补输出&#xff08;Complementary Mode&#xff09;双缓冲输出PWM&#xff08;Double Switch&#xff09;错误检测机制&#xff08;Fault Detection&#xff09; 输入…

SpringBoot使用WebSocket收发实时离线消息

引入maven依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> WebScoket配置处理器 import org.springframework.boot.web.servlet.ServletContextI…

如何为你的地图数据设置地图样式?

地图样式设置是GIS系统中非常重要的功能模块&#xff0c;水经微图Web版本最近对符号样式功能模块进行了升级。 你可以通过以下网址直接打开访问&#xff1a; https://map.wemapgis.com 现在我们为大家分享一下水经微图Web版中&#xff0c;如何为你标注的地图数据设置地图样式…

【干货】JVS低代码表单基础组件的配置与应用

表单的基础组件主要用于收集用户输入的数据&#xff0c;并对这些数据进行验证和处理。通过表单组件&#xff0c;用户可以输入各种类型的数据&#xff0c;如文本、数字、日期、选择项等。这些数据可以通过表单的提交按钮提交到服务器进行处理&#xff0c;从而使网站或应用程序能…