原理:微博的评论数据是通过服务器异步传输过来的,并且是分多页的,相信很多小伙伴有了分页的网址,就能把评论数据解析出来,但是微博的评论数据是通过瀑布流分页,获取下一页的url也就是此次爬取的难点。
通过评论页的url,以及掉我头发的分析之后,
首页评论url:https://weibo.com/aj/v6/comment/big?ajwvr=6&id=4441135474874422&filter=all&from=singleWeiBo&__rnd=1574405759566
第二页评论url:https://weibo.com/aj/v6/comment/big?ajwvr=6&id=4441135474874422&root_comment_max_id=4441415025138397&root_comment_max_id_type=&root_comment_ext_param=&page=2&filter=all&sum_comment_number=11&filter_tips_before=1&from=singleWeiBo&__rnd=1574405986800
可以看出第二页url比第一页多了几个参数,其中root_comment_max_id与sum_comment_number决定这能否获取下一页评论数据
root_comment_max_id是上一页最后一条评论的comment_id-1
sum_comment_number是已经展示的评论数。
(简单吧,拿头发换的)
这样我们就能通过首页url构造出下一页的url,如此循环得到所有评论页的url
以下是其评论数据:
爬取流程如下:
首先,从文件中读取要爬取的微博的网址
if __name__ == '__main__':
# 创建一个保持会话的session
s = requests.Session()
with open('wb_url.txt','r') as f:
for url in f:
# print(url)
spider = Spider(url,s)
spider.run()
有了具体某篇微博的网址后,可以得到该微博评论的id,然后拼出评论首页的url
# 得到第一页评论网址,和该博客评论id
def get_first_comment(self):
res = self.get_response(self.page_url)
# 得到该微博的id
com_id = re.search(r'按热度.*?id=(.*?)&filter=all.*?">按时间', res.text).group(1)
# 然后拼出其首页评论的网址
com_url = 'https://weibo.com/aj/v6/comment/big?ajwvr=6&id=%s&filter=all&from=singleWeiBo&__rnd=1574358480600' % com_id
return com_url, com_id
有了评论首页地址,我们就可以通过其源码提取到我们需要的root_comment_max_id并计算出sum_comment_number。这样我们就可以拼出下一评论页的url
#通过评论源码得到下一页的url
def get_next_comment(self,com_data,com_id):
# 得到最后一个评论的comment_id
# print(com_data)
html = etree.HTML(com_data)
com_ls = html.xpath('//div[@node-type="root_comment"]')
# 评论为空时的容错
try:
com_max_id = com_ls[-1].xpath('./@comment_id')[0]
except:
return 'NO'
com_max_id = str(int(com_max_id)-1)
length = len(com_ls)
self.sum_comment_number += length
# print(com_max_id, length)
next_url = 'https://weibo.com/aj/v6/comment/big?ajwvr=6&id={cid}&root_comment_max_id={mid}&root_comment_max_id_type=&root_comment_ext_param=' \
'&page={page_num}&filter=all&sum_comment_number={count_num}&filter_tips_before=1&from=singleWeiBo&__rnd=1574357153056'.format(cid=com_id,mid=com_max_id,page_num=self.page_num,count_num=self.sum_comment_number)
return next_url
有了评论页的url,我们只需解析一下服务器返回的评论数据,得到我们需要的数据,并保存
def save_comment_data(self,com_data):
# 提取干净评论 用户名+内容+时间+主页url
html = etree.HTML(com_data)
# 得到该评论源码的所有评论
uls = html.xpath('//div[@class="list_con"]')
for ul in uls:
user = ul.xpath('./div[@class="WB_text"]/a/text()')[0]
comment = ul.xpath('./div[@class="WB_text"]/text()')[1]
# 去除中文冒号:
comment = comment.split(':',maxsplit=1)[-1]
tim = ul.xpath('./div[contains(@class,"WB_func")]/div[contains(@class,"WB_from")]/text()')[0]
user_url = 'https:'+ul.xpath('./div[@class="WB_text"]/a/@href')[0]
try:
self.writer.writerow([user, comment, tim,user_url])
except Exception as e:
print(e)
# print(user, comment, tim,user_url)
主要流程就是如此,此次最花时间的就是下一页评论url的获取,新浪微博采用瀑布流,鼠标下滑或点击查看更多服务器才会返回数据,通过上一页的数据,推算出下一页url的参数~
以前新浪微博,通过page参数就能得到下一页,现在升级,后浪在推前浪,而我还想再浪,加油