Tip:本文仅供学习与交流,切勿用于非法用途!!!

前言

前段时间微博上关于某日记的评论出现了严重的两极分化,出于好奇的我想对其中的评论以及相关用户做一个简单的分析,于是我在网上找了相关的代码,简单的修改了cookies等参数就Run起来了。
既然没报错!! 我很震惊,从未若此畅快过~
随后便分析起来,过了一会就发现:事情并不简单,数据是重复的!!
没错,重复率如此之高,令人发指。于是,我开始了我的探索之路~
说明:整体代码还是借鉴了之前大佬的,主要是解决了数据重复的问题,如有侵权还请联系!

一、整体思路

思路也比较清楚,我画了一个极其简单的流程图:

至于这里为什么主评论和子评论要分开获取,这也是解决重复问题的关键,通过测试可以知道直接按照规律修改页面参数或者通过.cn的页面爬取,在到达一定的数量后(大概是几百)就无法得到数据或是出现数据重复。

二、获取微博地址

访问微博用户页面时的请求URL为:

https://weibo.com/xxxxxxxxx?is_search=0&visible=0&is_all=1&is_tag=0&profile_ftype=1&page=1
  • 1

其中通过修改page参数,即可控制页码,但是细心的小伙伴应该发现了,一页的数据除了直接加载的HTML之外,还有两次是通过ajax动态获取的:

start_ajax_url1 = 'https://weibo.com/p/aj/v6/mblog/mbloglist?ajwvr=6&domain=%s&is_all=1&page={0}&pagebar=0&pl_name=Pl_Official_MyProfileFeed__20&id=%s&script_uri=/%s&pre_page={0}'%(domain,page_id,user)
start_ajax_url2 = 'https://weibo.com/p/aj/v6/mblog/mbloglist?ajwvr=6&domain=%s&is_all=1&page={0}&pagebar=1&pl_name=Pl_Official_MyProfileFeed__20&id=%s&script_uri=/%s&pre_page={0}'%(domain,page_id,user)
  • 1
  • 2

也就是说,每页数据有三部分组成:

1、获取ajax地址

通过主界面,获取相应的ajax请求地址:

def get_ajax_url(user):
url = 'https://weibo.com/%s?page=1&is_all=1'%user
res = requests.get(url, headers=headers,cookies=cookies)
html = res.text
page_id = re.findall("CONFIG\['page_id'\]='(.*?)'",html)[0]
domain = re.findall("CONFIG\['domain'\]='(.*?)'",html)[0]
start_ajax_url1 = 'https://weibo.com/p/aj/v6/mblog/mbloglist?ajwvr=6&domain=%s&is_all=1&page={0}&pagebar=0&pl_name=Pl_Official_MyProfileFeed__20&id=%s&script_uri=/%s&pre_page={0}'%(domain,page_id,user)
start_ajax_url2 = 'https://weibo.com/p/aj/v6/mblog/mbloglist?ajwvr=6&domain=%s&is_all=1&page={0}&pagebar=1&pl_name=Pl_Official_MyProfileFeed__20&id=%s&script_uri=/%s&pre_page={0}'%(domain,page_id,user)
return start_ajax_url1,start_ajax_url2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2、解析页面中的微博地址

发送请求后,解析页面中的微博地址(主页面请求或AJAX请求相同):

def parse_home_url(url):
res = requests.get(url, headers=headers,cookies=cookies)
response = res.content.decode().replace("\\", "")
every_id = re.compile('name=(\d+)', re.S).findall(response) # 获取次级页面需要的id
home_url = []
for id in every_id:
base_url = 'https://weibo.com/aj/v6/comment/big?ajwvr=6&id={}&from=singleWeiBo'
url = base_url.format(id)
home_url.append(url)
return home_url
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3、获取指定用户微博地址

将上面两个函数整合,得到:

def get_home_url(user,page):
start_url = 'https://weibo.com/%s?page={}&is_all=1'%user
start_ajax_url1,start_ajax_url2 = get_ajax_url(user)
for i in range(page):
home_url = parse_home_url(start_url.format(i + 1)) # 获取每一页的微博
ajax_url1 = parse_home_url(start_ajax_url1.format(i + 1)) # ajax加载页面的微博
ajax_url2 = parse_home_url(start_ajax_url2.format(i + 1)) # ajax第二页加载页面的微博
all_url = home_url + ajax_url1 + ajax_url2
print('第%d页解析完成'%(i+1))
return all_url
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

参数为用户的ID,以及爬取的页数,返回结果则为每条微博的地址。

三、获取主评论

简单分析请求数据可以知道,获取微博评论的接口为:

https://weibo.com/aj/v6/comment/big?ajwvr=6&id=4498052401861557&root_comment_max_id=185022621492535&root_comment_max_id_type=0&root_comment_ext_param=&page=1&from=singleWeiBo
  • 1

一个很耀眼的page参数映入眼帘,而且似乎其他参数去掉之后请求也正常,也许你第一反应是写个循环直接获取不就OK了,emmmm,然后你就将陷入数据重复的恐怖原点。似乎root_comment_max_id这个参数也很重要,得想法获得。通过进一步分析可以发现,其实在请求返回的数据中,已经包含了下一步请求的地址,只需要提取出来再继续往下即可:

代码如下:

def parse_comment_info(data_json):
html = etree.HTML(data_json['data']['html'])
name = html.xpath("//div[@class='list_li S_line1 clearfix']/div[@class='WB_face W_fl']/a/img/@alt")
info = html.xpath("//div[@node-type='replywrap']/div[@class='WB_text']/text()")
info = "".join(info).replace(" ", "").split("\n")
info.pop(0)
comment_time = html.xpath("//div[@class='WB_from S_txt2']/text()") # 评论时间
name_url = html.xpath("//div[@class='WB_face W_fl']/a/@href")
name_url = ["https:" + i for i in name_url]
ids = html.xpath("//div[@node-type='root_comment']/@comment_id")
try:
next_url = 'https://weibo.com/aj/v6/comment/big?ajwvr=6&from=singleWeiBo&'+html.xpath('/html/body/div/div/div[%d]/@action-data'%(len(name)+1))[0]+'&__rnd='+str(int(time.time()*1000))
except:
try:
next_url = 'https://weibo.com/aj/v6/comment/big?ajwvr=6&from=singleWeiBo&'+html.xpath('/html/body/div/div/a/@action-data')[0]+'&__rnd='+str(int(time.time()*1000))
except:
next_url = ''
comment_info_list = []
for i in range(len(name)):
item = {}
item["id"] = ids[i]
item["name"] = name[i] # 存储评论人的网名
item["comment_info"] = info[i][1:] # 存储评论的信息
item["comment_time"] = comment_time[i] # 存储评论时间
item["comment_url"] = name_url[i] # 存储评论人的相关主页
try:
action_data = html.xpath("/html/body/div/div/div[%d]//a[@action-type='click_more_child_comment_big']/@action-data"%(i+1))[0]
child_url = 'https://weibo.com/aj/v6/comment/big?ajwvr=6&from=singleWeiBo&' + action_data
item["child_url"] = child_url
except:
item["child_url"] = ''
comment_info_list.append(item)
return comment_info_list,next_url
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

参数为请求的json数据,返回解析后的数据,以及下一个地址,数据格式如下:

其中child_url即为相对应子评论地址,进一步获取子评论。

四、获取子评论

获取子评论的思路和获取主评论的思路是一致的,当我们获取完所有主评论之后,便遍历结果,当child_url不为空时(即有子评论),则进行请求获取子评论。

1、解析子评论

def parse_comment_info_child(data_json):
html = etree.HTML(data_json['data']['html'])
name = html.xpath("//div[@class='list_li S_line1 clearfix']/div/div[1]/a[1]/text()")
info=html.xpath("//div[@class='list_li S_line1 clearfix']/div/div[1]/text()")
info = "".join(info).replace(" ", "").split("\n")
info.pop(0)
comment_time = html.xpath("//div[@class='WB_from S_txt2']/text()") # 评论时间
name_url = html.xpath("//div[@class='list_li S_line1 clearfix']/div/div[1]/a[1]/@href")
name_url = ["https:" + i for i in name_url]
ids = html.xpath("//div[@class='list_li S_line1 clearfix']/@comment_id")
try:
next_url = 'https://weibo.com/aj/v6/comment/big?ajwvr=6&from=singleWeiBo&'+html.xpath('/html/body/div[%d]/div/a/@action-data'%(len(name)+1))[0]+'&__rnd='+str(int(time.time()*1000))
except:
next_url = ''
comment_info_list = []
for i in range(len(name)):
item = {}
item["id"] = ids[i]
item["name"] = name[i] # 存储评论人的网名
item["comment_info"] = info[i][1:] # 存储评论的信息
item["comment_time"] = comment_time[i] # 存储评论时间
item["comment_url"] = name_url[i] # 存储评论人的相关主页
comment_info_list.append(item)
return comment_info_list,next_url
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

2、获取子评论

整合调用上一个函数,获得相应的子评论:

def get_childcomment(url_child):
print('开始获取子评论...')
comment_info_list = []
res = requests.get(url_child, headers=headers, cookies=cookies)
data_json = res.json()
count = data_json['data']['count']
comment_info,next_url = parse_comment_info_child(data_json)
comment_info_list.extend(comment_info)
print('已经获取%d条'%len(comment_info_list))
while len(comment_info_list) < count:
if next_url == '':
break
res = requests.get(next_url,headers=headers,cookies=cookies)
data_json = res.json()
comment_info,next_url = parse_comment_info_child(data_json)
comment_info_list.extend(comment_info)
print('已经获取%d条'%len(comment_info_list))
return comment_info_list
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

参数为child_url,返回为对应的子评论。

五、主函数调用

1、导入相关库

import re
import time
import json
import urllib
import requests
from lxml import etree
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2、主函数执行

if "__main__" == __name__:
# 设置相应参数
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0',
'Accept': '*/*',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Content-Type': 'application/x-www-form-urlencoded',
'X-Requested-With': 'XMLHttpRequest',
'Connection': 'keep-alive',
}
cookies = {} # 微博cookies(需要自己获取请求得到)
userid = '' # 需要爬取的微博用户ID
page = 1 # 爬取的页数
# 开始爬取
all_urls = get_home_url(userid,page)
for index in range(len(all_urls)):
url = all_urls[index]
print('开始获取第%d个微博主评论...'%(index+1))
comment_info_list = []
res = requests.get(url, headers=headers, cookies=cookies)
data_json = res.json()
count = data_json['data']['count']
comment_info,next_url = parse_comment_info(data_json)
comment_info_list.extend(comment_info)
print('已经获取%d条'%len(comment_info_list))
while True:
if next_url == '':
break
res = requests.get(next_url,headers=headers,cookies=cookies)
data_json = res.json()
comment_info,next_url = parse_comment_info(data_json)
comment_info_list.extend(comment_info)
print('已经获取%d条'%len(comment_info_list))
for i in range(len(comment_info_list)):
child_url = comment_info_list[i]['child_url']
if child_url != '':
comment_info_list[i]['child'] = get_childcomment(child_url)
else:
comment_info_list[i]['child'] = []
with open('第%d条微博评论.txt'%(index+1),'w') as f:
f.write(json.dumps(comment_info_list))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

3、结果

获取了10条微博数据,如下:

写在最后

当然,还有很多不足之处,比如速度不太理想,结构比较混乱。考虑过加入多线程等办法加快速度,不过由于需要登陆,所以有封号风险哦,大家慎重~

最后,需要python自学资料的可以私信我哦~

—用python写图片格式批量处理工具的更多相关文章

  1. 用python写图片格式批量处理工具

    一.思路分析 其实,照片处理要求很简单,主要是两个方面:一个是调整图片尺寸(即宽x高),另一个是调整图片的大小(即压缩).为了实现这两个功能,利用python中的PIL库即可,其安装方法如下: pip ...

  2. Python 写了一个批量生成文件夹和批量重命名的工具

    Python 写了一个批量生成文件夹和批量重命名的工具 目录 Python 写了一个批量生成文件夹和批量重命名的工具 演示 功能 1. 可以读取excel内容,使用excel单元格内容进行新建文件夹, ...

  3. 【我的Android进阶之旅】推荐一款视频转换GIF图片格式的转换工具(Video to GIF)

    一.背景 最近想把一些Android Demo的运行效果图获取下来,但是一直使用真机进行调试,在电脑上不好截取一段gif动画.而之前使用模拟器的时候可以使用 GifCam 工具进行屏幕动画截取.Gif ...

  4. 利用 Python 写一个颜值测试小工具

    我们知道现在有一些利用照片来测试颜值的网站或软件,其实使用 Python 就可以实现这一功能,本文我们使用 Python 来写一个颜值测试小工具. 很多人学习python,不知道从何学起.很多人学习p ...

  5. python之简单主机批量管理工具

    今天做了一个很简单的小项目,感受到paramiko模块的强大. 一.需求 二.简单需求分析及流程图 需求很少,我就简单地说下: 1. 主机分组可以配置文件实现(我用字典存数据的). 2. 登陆功能不做 ...

  6. [ Python - 10 ] 练习:批量管理主机工具

    需求: 主机分组 登录后显示主机分组,选择分组后查看主机列表 可批量执行命令.发送文件,结果实时返回 主机用户名密码可以不同 流程图: 说明: ## 需求: 主机分组 登录后显示主机分组,选择分组后查 ...

  7. 基于pyqt5的图片素材批量处理工具

    功能 分辨率的批量转换,文件夹递归查找 像素偏移量批量调整,文件夹单层查找 画布的大小的批量进行调整,不进行缩放,文件夹单层查找 界面 通过PyUIC生成的代码 # -*- coding: utf-8 ...

  8. python转换图片格式

    在图片所在的路径下,打开命令窗口 bmeps -c picturename.png picturename.eps

  9. 2017.11.7 Python 制作EFM32/ AVR批量烧录工具

    Customer need program quickly asap. ok,I need to set up a table for test. 1 reference data http://ww ...

随机推荐

  1. Oracle函数使用1

    一.字符串处理函数 1.ascii(x):返回字符的ASCII. SQL语句:select ascii('a') from dual; dual:空表,每创建一个用户都会生成这样一个dual表,表中只 ...

  2. baby sqli 联合查询加入数据 手工注入

    0x00 BabySQli 原题目描述:刚学完sqli,我才知道万能口令这么危险,还好我进行了防护,还用md5哈希了密码! 登陆页面,查看源码后点进search.php看到一段可疑的句子MMZFM42 ...

  3. 《深入理解计算机系统》(CSAPP)读书笔记 —— 第一章 计算机系统漫游

    本章通过跟踪hello程序的生命周期来开始对计算机系统进行学习.一个源程序从它被程序员创建开始,到在系统上运行,输出简单的消息,然后终止.我们将沿着这个程序的生命周期,简要地介绍一些逐步出现的关键概念 ...

  4. Vue 的父组件和子组件生命周期钩子执行顺序是什么

    加载渲染过程父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount-&g ...

  5. 【ZJOI2019】线段树(线段树 & dp)

    Link UOJ LOJ Luogu Solution 很玄妙的一道题,考察了对线段树较本质的理解 然而我并不会这个所谓最可做的题 首先,虽然题目很复杂,好像每个点的标记变化都很玄学,但是我们可以深入 ...

  6. 实战演习:mysqlbinlog恢复bin-log数据

    mysqlbinlog恢复bin-log数据 Binlog日志即binary log,是二进制日志文件,有两个作用,一个是增量备份,另一个是主从复制,即从节点同步主节点数据时获取的即是bin-log, ...

  7. mysql扩展百分位函数(类似SUM)

    mysql扩展百分位函数(类似SUM) 参考:https://my.oschina.net/waterbear/blog/1186744 百度搜索:mysql percentile

  8. 教你30秒解开手机的密码 适用于高通CPU

    教程简介 先将手机进入9008模式.进入方法请自己百度. 进入9008方法如下:   先将手机关机,然后按住音量加和音量减不松手. 使用教程: 将数据线拆入电脑.会出现一个端口   出现端口后可以松开 ...

  9. 【MySQL】Novicat 连接mysql 报错1251的问题处理,Novicat12 破解方法

    1.远程连接时,报错 是因为我们的navicat版本太低 在网上查的是,出现这个原因是mysql8之前的版本中加密规则是mysql_native_password,而在mysql8之后,加密规则是ca ...

  10. postgresql修改postgres用户密码

    [postgres@pg01 ~]$ psql -Upostgres -dpostgres postgres=# alter user postgres with password 'postgres ...