python分析百家号文章评论并进行爬取
这篇文章是分析百家号的文章评论,并提取下面文章的评论该文章仅供交流使用,请勿非法使用,如有侵犯,请联系删帖
文章将分为2个板块,分析和代码
一. 分析
选取一篇文章,然后打开开发者工具进行抓包
转到开发者工具的Network,在Filter上输入comment进行筛选,这样做的目的是准确的筛选出有关评论的数据包
(如果没出来的话,可以下图中红框内的进行进一步筛选)
通过上面的筛选发现出现3个包,点进去看一下,发现第一个包是我们所需要的数据,分析第一个数据包里面的一些参数,如图所示
如果在不确定哪些参数是变动的,可以进行再一次抓包对比,最后发现除了start、num、callback和ts变动的,了解js的知道这个callback是个回调函数
是生成的随机字母加数字,这个固定对数据包无影响,而其他参数中ts是13位时间戳,start是页数,但是变动是加上后面num数,所以num是每页的评论数
在返回的结果中发现下图的情况,返回的并不是json的格式,而在写代码的过程中发现了返回的数据是Unicode的返回。后续想通过utf-8的编码后再解码发现行不通,一直报错
后面尝试使用json方式来提取数据,因为除去下图的前面的_boxjsonp0e0bb351()就可以得到完整json,可以使用正则提取出来,然后同json模块load然后提取
二.代码
代码每段都附上了注释说明,考虑每个楼都有相应的回复者,那么就需要提取对应的回复者。最后选取了用json的方式存储数据
其中在上面返回的数据编码问题上,有好的解决方案可以说明。有错误的话指出来,有什么好的代码写法可以交流一下。
import requests
import time
import re
import json
import math
#设置协议头
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
"Connection": "close"
}
#提取前面的链接cookie值
res = requests.get(
"https://www.baidu.com/s?cl=3&tn=baidutop10&fr=top1000&wd=%E4%BA%BA%E6%B0%91%E6%97%A5%E6%8A%A5%3A%\
E6%89%AB%E7%A0%81%E7%82%B9%E9%A4%90%E4%B8%8D%E8%AF%A5%E6%98%AF%E5%94%AF%E4%B8%80%E9%80%89%E6%8B%A9&rsv_idx=2&rsv\
_dl=fyb_n_homepage&hisfilter=1",
headers=headers)
#提取cookie为字典形式
cookie = res.cookies.get_dict()
data_dict = {}
count = 0
i = 0
while True:
time.sleep(3)
#生成13位时间戳
current_milli_time = lambda: int(round(time.time() * 1000))
now_time = current_milli_time()
#完善协议头
headers.update({'Accept': '*/*',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh-CN,zh;q=0.9',
#设置来路的,不设置这个无法返回评论数据
'Referer': 'https://baijiahao.baidu.com/s?id=1690446693112251324&wfr=spider&for=pc'})
#根据自己的url改变其中的变动参数
url = f"https://ext.baidu.com/api/comment/v2/comment/list?thread_id=1004000038741948&reply_id=&start={i * 20}&num=20&appid=22862035&order=12&inner_order=9&use_list=1&callback=_boxjsonpcd1d7651&use_uk=1&ts={now_time}"
i += 1
res = requests.get(url, headers=headers, cookies=cookie)
#替换返回内容包含\/的链接
new = res.text.replace(r"\/", "/")
#使用正则提取出来
full_json = "{" + re.search(r'_boxjsonpcd1d7651\(\{(.*?)\}\)', new).group(1) + "}"
#load已经完整的json
new = json.loads(full_json)
#在无法提取数据的时候报错跳出死循环
try:
data = new['ret']['list']
except Exception as e:
print(e)
break
for da in data:
#count计数几楼
count += 1
#提取具体内容,可自己决定提取什么内容
uname = da['uname']
like_count = da['like_count']
text = da['content']
reply_count = da['reply_count']
print(f"评论者:{uname}\n评论的内容:{text}\n喜欢数:{like_count}\n")
reply_list = []
if reply_count == "0":
print("该评论无回复数")
else:
reply_id = da['reply_id']
#每页10个,用全部回复数除以10,然后进一整数方式估算出页数,当然这里可以使用死循环
page = math.ceil(int(reply_count) / 10)
for n in range(page):
time.sleep(3)
current_milli_time = lambda: int(round(time.time() * 1000))
now_time = current_milli_time()
#提取评论中的回复数据,返回的方式和提取跟上面一样
new_url = f"https://ext.baidu.com/api/comment/v2/comment/detail?thread_id=1004000038741948&reply_id={reply_id}&start={n * 10}&num=10&appid=22862035&order=9&use_list=0&callback=_boxjsonp370b5194&use_uk=1&ts={now_time}"
res1 = requests.get(new_url, headers=headers, cookies=cookie)
new1 = res1.text.replace(r"\/", "/")
full_json = "{" + re.search(r'_boxjsonp370b5194\(\{(.*?)\}\)', new1).group(1) + "}"
new1 = json.loads(full_json)
#以防估算出错,用try来避免
try:
new_data = new1['ret']['list']
except Exception as e:
print(e)
break
for rp in new_data:
f_uname = rp['uname']
f_like_count = rp['like_count']
f_text = rp['content']
#把所有回复数据用字典的形式放在列表里
reply_list.append({"replier": f_uname, "reply_content": f_text, "reply_likes": f_like_count})
#保存进字典里面
data_dict.update({f"floor_{count}": {"commenter": uname, "content": text, "like_count": like_count,
"reply_list": reply_list}})
print(data_dict)
#爬取完后进行保存数据
with open("data.json", "w", encoding="utf-8") as f:
json.dump(data_dict, f)
分析过程有些地方可能没说到,代码上有附上说明,如果在对返回的数据有更好的解决方法可以交流一下。
如有侵权和不当的地方,请联系删贴,文章仅供交流,谢谢。
本帖最后由 fanvalen 于 2021-2-2 13:29 编辑
好把我看错了我又去测试一下
页:
[1]