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

news2024/12/27 22:57:27

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

阅读前必知:

  • 本文的方法是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对象,并设置headers
    options = Options()
    options.add_argument("user-agent=" + random.choice(user_agents))
    # 传入cookie
    cookies = 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

    # 解析HTML
    soup = 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_ids
    df['content'] = contents
    df.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}
        #存入csv
        writer.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 time

template = '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 random

contents = []

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')
        # 查找content
        content = soup.find('div', class_='RichContent-inner').text
        contents.append(content)
        print(content)
    except Exception as e:
        print(f'爬取answer_id为{answer_id}的数据时出现异常:{e}')
        break
    
    time.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.coloradmin.cn/o/1128829.html

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

相关文章

ubuntu 安装 gnome 安装 xrdp

先安装xrdp 更新 apt-get sudo apt-get update && apt-get upgrade安装图形包 apt-get install xubuntu-desktop安装 xrdp apt-get install xrdp安装 xfce4 apt-get install xfce4配置 xfce4 Add xfce to the xfce desktop window manager autorun by fixing the …

静电模型PIC方法的Matlab仿真设计

任务要求: 采用PIC模拟方法的静电模型来模拟多环形电子注在圆柱系统中的运动轨迹。模拟电子枪阴极表面发射电子注,电子在静态场的作用下运动直至稳定的运动过程。其中 系统长:0.01m 系统半径:0.005m 入射的每个宏电子电流&#x…

RedHat8升级GLIBC_2.29,解决ImportError: /lib64/libm.so.6: version `GLIBC_2.29

问题背景 在做大模型微调训练时,执行python脚本时出现如下报错: 查看当前服务器版本,确实没有GLIBC_2.29的 strings /lib64/libm.so.6 | grep GLIBC_ GLIBC_2.2.5 GLIBC_2.4 GLIBC_2.15 GLIBC_2.18 GLIBC_2.23 GLIBC_2.24 GLIBC_2.25 GLIB…

Java后端开发——实现登录验证程序

一、实现一个简单登录验证程序 实现一个简单的用户登录验证程序,如果用户名是 abc ,密码是 123,则显示欢迎用户的信息,否则显示“用户名或密码不正确”。 【分析】 该案例采用 JSP 页面只完成提交信息和验证结果的显示&#xff…

零基础Linux_22(多线程)线程控制和和C++的多线程和笔试选择题

目录 1. 线程控制 1.1 线程创建(pthread_create) 1.2 线程结束(pthread_exit) 1.3 线程等待(pthread_join) 1.4 线程取消(pthread_cancel结束) 1.5 线程tid(pthread_self()) 1.6 线程局部存储(__thread) 1.7 线程分离(pthread_detach) 2. C的多线程 3. 笔试选择题 答…

百济神州:受专利侵权诉讼影响,股价暴跌,估值已被华尔街大幅下调

来源:猛兽财经 作者:猛兽财经 百济神州股价暴跌估值已被下调 今年以来,百济神州(BGNE)在美股的股价已经下跌了26.1%。在2023年10月18日的的交易日结束时,百济神州的最后最后交易价为167.54美元,与2023年1月20日的52周…

Unity DOTS系列之Filter Baking Output与Prefab In Baking核心分析

最近DOTS发布了正式的版本, 我们来分享一下DOTS里面Baking核心机制,方便大家上手学习掌握Unity DOTS开发。今天给大家分享的Baking机制中的Filter Baking Output与Prefab In Baking。 对啦!这里有个游戏开发交流小组里面聚集了一帮热爱学习游戏的零基础…

优化单元测试效率:Spring 工程启动耗时统计

相关文章: Java Agent 的简单使用 本文相关代码地址:https://gitee.com/dongguabai/blog 单元测试在软件项目的可持续发展中扮演着不可或缺的角色,这一点毫无疑问。不久前,公司大佬在内部分享时也提到过:单元测试是…

“动捕设备+飞兔渲染软件”,激发数字人短视频营销新动力

随着短视频行业持续破发,短视频成为目前吸引流量最快的方式之一。同时,元宇宙催生众多虚拟主播、虚拟偶像、虚拟IP等,以数字人形象结合短视频形式,在社交平台上频频出圈。如虚拟美妆达人“柳夜熙”一条视频涨粉150w,但…

[卷积神经网络]FasterNet论文解析

一、概述 FasterNet是CVPR2023的文章,通过使用全新的部分卷积PConv,更高效的提取空间信息,同时削减冗余计算和内存访问,效果非常明显。相较于DWConv,PConv的速度更快且精度也非常高,识别精度基本等同于大型…

【计算机网络(1)】计算机网络体系结构1:计算机网络概述

文章目录 概念 & 功能 & 发展计算机网络的概念计算机网络的功能计算机网络的发展网络的本质 组成 & 分类计算机网络的组成计算机网络的分类 概念 & 功能 & 发展 计算机网络的概念 1. 网络 网一样的东西或网状系统。其中(有线电视网络、电信网…

亲测解决no module named ‘PyQt5.QtCore‘

如果是在windows上遇到这个问题,升级一下VS Studio即可。 运行坏境和问题 Win10 Anaconda 解决方法 升級vs studio from 2022 preview to 2022。

springboot配置注入增强(四)使用框架实现自定义数据源和自定义属性解析

1.代码 框架代码已经上传到gitee中 代码链接:https://gitee.com/summer-cat001/config-enhance jar包:https://gitee.com/summer-cat001/config-enhance/tree/master/build/libs 2.使用方式 2.1 引入jar包 引入本地jar包或者把jar包上传到自己的ma…

年薪20w+,做测试的第4年,从手工测试到自动化测试每一步都是艰难的~

自己已经做了好几年的手工测试了,越来越觉得如果一直在手工测试的道路上前进,并不会有很大的发展,所以通过自己的努力,已经成功的转入自动化测试的方向,那么想快速的转入自动化方向,我们应该怎么做呢&#…

Linux的命令基本格式

因为对服务器来讲,图形界面会占用更多的系统资源,而且会安装更多的服务、开放更多的端口,这对服务器的稳定性和安全性都有负面影响。其实,服务器是一个连显示器都没有的家伙,要图形界面干十么?说到这里&…

python—openpyxl操作excel详解

前言 openpyxl属于第三方模块,在python中用来处理excel文件。 可以对excel进行的操作有:读写、修改、调整样式及插入图片等。 但只能用来处理【 .xlsx】 后缀的excel文件。 使用前需要先安装,安装方法: pip install openpyxl…

基于二维小波变换的散斑相位奇异构造算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 图(1)表示散斑原图像,(2)表示对(1)图像进行x轴方向的极化分析的小波相位图,呈周期的水平条纹,(3)表示对(1)图像…

大数据Doris(十二):扩容缩容

文章目录 扩容缩容 一、FE 扩容和缩容 1、增加 FE 节点 2、 删除 FE 节点

非接触式外径测量仪 光电在线检测

非接触式的检测方式可以在不损伤产品表面的情况下,进行高精度的检测,它能对一些高温、熔融等不易测量的轧材检测,适用面更广。光电非接触式的外径测量仪同样是非接触式的检测方式,完成了线缆电缆、橡胶、塑料等产品的高精度检测。…

YOLOv7改进:新颖的上下文解耦头TSCODE,即插即用,各个数据集下实现暴力涨点

💡💡💡本文属于原创独家改进:上下文解耦头TSCODE,进行深、浅层的特征融合,最后再分别输入到头部进行相应的解码输出,实现暴力暴力涨点 上下文解耦头TSCODE| 亲测在多个数据集实现暴力涨点,对遮挡场景、小目标场景提升也明显; 收录: YOLOv7高阶自研专栏介绍: …