技术标签: 爬虫 python 微博 数据采集 爬取微博内容(学习用)
爬虫课题描述可见:
课题解决方法:
微博移动版爬虫
微博PC网页版爬虫
Python爬虫【二】爬取PC网页版“微博辟谣”账号内容(selenium同步单线程)
Python爬虫【三】爬取PC网页版“微博辟谣”账号内容(selenium单页面内多线程爬取内容)
Python爬虫【四】爬取PC网页版“微博辟谣”账号内容(selenium多线程异步处理多页面)
本文我们针对3G4G移动版微博网站(URL: https://m.weibo.cn),爬取"微博辟谣"的数据
https://m.weibo.cn/u/1866405545?uid=1866405545&t=0&luicode=10000011&lfid=100103
首先对微博辟谣网页访问和翻页时进行数据抓包,发现调用了如下接口:
接口返回数据集:
因此总结特点如下:
- 移动版网站页面是通过js访问API接口、再渲染到html元素的方式来加载内容的。因此我们可以不管html页面展示如何,而采用直接调用后台API接口来提取数据
- API接口中通过添加入参page=?的方式来实现不同页面数据内容的获取;新版API(2021年11月)换用了since_id=?作为入参,但也保留了page参数,两者都可实现分批按页查询的功能。since_id参数说明如下:每次API请求,返回的结果中都会保留下一页查询需要的since_id值;当翻页时,下一次API调用传入的since_id即为此since_id。所以我们可以通过更换此参数不断的实现分页爬取
整个微博移动版的爬取流程,可总结为以下四步:
1. 创建用来保存数据的DataFrame对象:excel_df
2. 从第一页开始,访问当前页面的API接口,获取微博数据并提取相关字段,存入df中
3. 不断的向后请求每一页的API接口,重复上面的提取数据和存df操作,直到最后一页
4. 将整个excel_df数据写入excel中
Python爬虫工程使用requests模块
请求API接口。因为整体功能比较简单,所以使用面向过程的设计模式,用函数调用串联整个业务。
工程结构如下:
- 创建
m_crawler.py
模块,定义爬取流程所需要的几个函数以上处理模块,放在名为
m
包中
- 因为DataFrame数据写excel比较基础,所以我们将它设计为一个工具方法。定义一个
util.py
模块,将工具方法都写入此模块中- 项目中URL、写入地址、表头等配置变量比较多,因此将他们写入
property.py
文件以上两个公共模块,放在名为
common
的包中
- 在项目下创建一个
名为excel的文件夹
,将最终的导出结果文件存于其中main.py
作为整个项目的启动入口
最终项目结构如下图:
main.py为程序入口,启动工程时首先从这里开始执行。因为我们的串联类为CrawlHandle,并且需要Crawler做为入参,因此main.py中代码设计如下:
if __name__ == '__main__':
# 移动版微博爬取
m_crawler.crawler_m_weibo_write_excel()
def crawler_m_weibo_write_excel()
为爬取处理主函数,根据上面的设计,功能是串联整个爬取的流程,设计如下:
def crawler_m_weibo_write_excel():
"""
主方法:从m移动端微博中读取数据,整理并存入指定excel
:return:
"""
# 定义空df,以装载处理完的数据
excel_df = DataFrame(columns=EXCEL_COLUMNS)
try:
# 无线循环不断向后翻页查询,直到查至微博最后一页
# 发现问题:移动版微博设置了只能拉取2000条数据,超过200条数据,since_id不会再返回;入参用page传参也是如此。此问题已在页面上尝试,m微博确实只能下拉2000条数据
page = 1 # 页面计数
while True:
# 1. 爬取微博数据
json_cards = get_weibo_info_by_page(page)
# 爬不到时退出爬取循环
if len(json_cards) == 0:
print("已爬取到微博最后一页,退出爬虫循环...")
break
# 2. 解析页面返回的json数据 整理为特定格式的list并返回
parse_json_list = list(parse_result_2_list(json_cards))
print("爬取第 %s 页,爬取有效微博数: %s" % (page, len(parse_json_list)))
# 3. 将此轮爬取的微博数据添加到df数据表末端
excel_df = excel_df.append(parse_json_list)
# 页数加1
page += 1
# 最好让程序睡眠一段时间,防止微博服务器认为在恶意爬取,封禁IP
time.sleep(0.5)
except Exception as e:
print("爬虫程序报错,可能出现问题,请检查!", e)
# 4. 爬取完所有数据后,进行写文档操作
util.write_excel(excel_df, M_WB_EXCEL_PATH)
那么接下来的重点就是补充完这五部分的方法代码细节,定义相关的类或者方法
上面调用的"分页抓取微博数据"功能,封装了函数def get_weibo_info_by_page()
,编写如下:
def get_weibo_info_by_page(page):
"""
分页抓取微博数据
:return:
"""
# 拼接接口入参
m_wb_request_params['page'] = page
# 拼装完整的请求URL
url = M_WB_URL + urlencode(m_wb_request_params)
# print("请求URL: %s" % url)
try:
# get请求接口,返回结果为json结构数据
response = requests.get(url, headers=headers)
if response.status_code == 200:
# 打印返回值,用于调试
# print("请求得到的response==%s" % response.text)
# 转json
response_json = response.json()
# 取json中的cards, 为array_list
cards = response_json.get('data').get('cards')
# print("提取到的cards属性:%s" % cards)
return cards
except requests.ConnectionError as e:
print('爬取错误', e.args)
将API请求的结果json提取必要的字段,封装入df的逻辑,封装为函数def parse_result_2_list()
:
def parse_result_2_list(json_cards):
"""
解析页面返回的json数据 整理为特定格式的list并返回
:return:
"""
# 循环所有cards,依次处理
for card in json_cards:
# card_type == 9 为微博正文内容,取出分析
if card.get('card_type') == 9:
# 取mblog
mblog = card.get('mblog')
# “微博辟谣”账号发微博时输入的文字内容
# wb_text = mblog.get('raw_text') # 老方法,2021年已不可用
# 新方法
bs = BeautifulSoup(mblog.get('text'), "html.parser")
wb_text = bs.get_text()
# 剔除月度工作报告信息
if '月度工作报告' in wb_text:
# 剔除月度工作报告,可打印日志分析剔除结果,以防有误判删除掉有用信息
print("剔除月度报告: %s" % mblog.get('raw_text'))
continue
# card转换整理后的json结果
etl_json = {
}
# "微博辟谣"此条微博的id
wb_id = mblog.get("id")
# 微博名,这里为“微博辟谣”
wb_name = mblog.get('user').get("screen_name")
# “微博辟谣”账号发微博时微博时间
wb_time = util.parse_time(mblog.get('created_at'))
# 本微博转发数,若为文章“转发”,则说明还没人转,设为0
wb_repost_count = mblog.get('reposts_count')
# set值
etl_json['WB_id'] = wb_id
etl_json['WB_name'] = wb_name
etl_json['WB_text'] = wb_text
etl_json['WB_time'] = wb_time
etl_json['WB_repost_count'] = wb_repost_count
# mblog中若有属性 retweeted_status 说明是转发
if mblog.get('retweeted_status'):
weibo_info = mblog.get('retweeted_status')
""" 当出现特殊情况无法爬取内容时的处理办法,目前处理办法是:忽略此条微博,跳过 """
# 被设为不可见,因此无法爬取(包括原作者设为自己可见、半年内可见等情况)
if weibo_info['visible']['type'] != 0 or weibo_info['visible']['list_id'] != 0:
print("以下博客已被设为不可见,不可爬取:%s" % weibo_info)
continue
# 微博账号被删除z包括自己注销、被查封注销等情况)
if 'deleted' in weibo_info:
print("以下博客已被删除,不可爬取:%s" % weibo_info)
continue
# 原微博的id
wb_id_org = weibo_info.get("id")
# 原微博号名称
wb_name_org = weibo_info.get('user').get("screen_name")
# 原账号发微博时微博时间
wb_time_org = util.parse_time(weibo_info.get('created_at'))
# 原微博转发数
wb_repost_count_org = weibo_info.get('reposts_count')
etl_json['WB_id_org'] = wb_id_org
etl_json['WB_name_org'] = wb_name_org
etl_json['WB_time_org'] = wb_time_org
etl_json['WB_repost_count_org'] = wb_repost_count_org
etl_json['type'] = "转发"
etl_json['weibo_name'] = wb_name_org
etl_json['time'] = wb_time_org
etl_json['repost_count'] = wb_repost_count_org
# 原微博发微时输入的文字内容
wb_text_org = weibo_info.get('raw_text')
# 如果文章没有全部展开,则再次提取微博数据
if ">全文<" in weibo_info.get("text"):
wb_long_text_org = util.get_weibo_long_text(wb_id_org)
# etl_json['wb_long_text_org'] = wb_long_text_org
# 替换文本
if wb_long_text_org is not None or wb_long_text_org != "":
wb_text_org = wb_long_text_org
etl_json['WB_text_org'] = wb_text_org
etl_json['text'] = wb_text_org
else:
etl_json['type'] = "原创"
etl_json['weibo_name'] = wb_name
etl_json['text'] = wb_text
etl_json['time'] = wb_time
etl_json['repost_count'] = wb_repost_count
yield etl_json
因为每个API接口会请求多条微博数据,因此入参是个list; 函数中用yield关键字返回一个迭代,但我们需要的是类型为list的结果,所以调用时,直接用list()做转换,就可得到此函数迭代执行后的结果list。
此模块为工具模块,用到的方法有
def get_weibo_long_text(id):
"""
通过id获取微博长文本
:return:
"""
# 拼装完整的请求URL
url = M_WB_LONG_TEXT_URL % id
# print("长文本请求URL: %s" % url)
try:
# get请求接口,返回结果为json结构数据
response = requests.get(url, headers=headers)
if response.status_code == 200:
# 打印返回值,用于调试
# print("长文本请求得到的response: %s" % response.text)
# 转json
response_json = response.json()
# 提取长文本
long_text = response_json.get('data').get('longTextContent')
# print("提取到的long_text:%s" % long_text)
# 将长文本用bs4转换为纯文字
html_soup = BeautifulSoup(long_text)
text = html_soup.get_text()
if text:
pass
else:
print("!调用接口查询长文本出错! URL = %s ,response= %s" % (url, response.text))
return text
except Exception as e:
print('爬取长文本错误')
traceback.print_exc()
def write_excel(excel_df, excel_path):
"""
将结果写入Excel
:param excel_df:
:param excel_path:
:return:
"""
print("开始写入Excel文档:文档名称 %s" % excel_path)
excel_df.to_excel(excel_path, index=False)
# 如果只想要前四列,则用下面的语句:
# excel_df[excel_columns[:4]].to_excel(excel_path, index=False)
print("写入Excel文档成功!")
主要是用来请求全文,还有些Excel的工具方法
以上整个爬虫项目编写完毕,只有配置变量和用到的工具方法没有贴出,具体实现可见源代码
当程序执行后,可以看到依次爬取每页微博数据,一页中有25条数据,会空过已删除、不可见、月报告的微博
最后爬取存入excel的结果如下,前四列即为课题要求的结果。
日期笔者在2020年12月时为yyyy-MM-dd的格式,2021年3月19日重试时,返回结果改为了日期字符串,这里可以用日期转换工具类做转换,format日期值,博主此处省略
项目工程编译了windows版本执行程序:微博数据采集python+selenium执行程序:WBCrawler.exe
执行项目前,需要下载selenium对应的浏览器驱动程序(driver.exe),并放在本机环境变量路径中,否则会报错。安装操作具体可见博客专题中的指导【二】
执行程序时,会在系统用户默认路径下,创建一个虚拟的python环境(我的路径是C:\Users\Albert\AppData\Local\Temp_MEI124882\),因此启动项目所需时间较长(约20秒后屏幕才有反应,打出提示),请耐心等待;也正因如此,执行电脑本身环境是可以无需安装python和selenium依赖包的;同时最后爬取保存的excel也在此文件夹下。
本项目采用cmd交互方式执行,因此等到屏幕显示:
选择爬取方式:
1. 移动版微博爬取
2. PC网页版微博爬取(单线程)
3. PC网页版微博爬取(页面内多线程)
4. PC网页版微博爬取(多线程异步处理多页面)
后,用键盘输入1~4,敲回车执行
工程参见:微博数据采集python+selenium工程:WBCrawler.zip
本专题内对源码粘贴和分析已经比较全面和清楚了,可以满足读者基本的学习要求。源码资源为抛砖引玉,也只是多了配置文件和一些工具方法而已,仅为赶时间速成的同学提供完整的项目案例。大家按需选择
文章浏览阅读15次。空化气泡的大小和相应的空化能量可以通过调整完全标度的振幅水平来操纵和数字控制。通过强调超声技术中的更高通量处理和防止样品污染,Epigentek EpiSonic超声仪可以轻松集成到现有的实验室工作流程中,并且特别适合与表观遗传学和下一代应用的兼容性。Epigentek的EpiSonic已成为一种有效的剪切设备,用于在染色质免疫沉淀技术中制备染色质样品,以及用于下一代测序平台的DNA文库制备。该装置的经济性及其多重样品的能力使其成为每个实验室拥有的经济高效的工具,而不仅仅是核心设施。
文章浏览阅读4.2k次,点赞3次,收藏14次。目录点击这里查看所有博文 本系列博客,理论上适用于合宙的Air202、Air268、Air720x、Air720S以及最近发布的Air720U(我还没拿到样机,应该也能支持)。 先不管支不支持,如果你用的是合宙的模块,那都不妨一试,也许会有意外收获。 我使用的是Air720SL模块,如果在其他模块上不能用,那就是底层core固件暂时还没有支持,这里的代码是没有问题的。例程仅供参考!..._合宙获取天气
文章浏览阅读7.7k次,点赞2次,收藏41次。1 关于meshMesh的意思是网状物,以前读书的时候,在自动化领域有传感器自组网,zigbee、蓝牙等无线方式实现各个网络节点消息通信,通过各种算法,保证整个网络中所有节点信息能经过多跳最终传递到目的地,用于数据采集。十多年过去了,在无线路由器领域又把这个mesh概念翻炒了一下,各大品牌都推出了mesh路由器,大多数是3个为一组,实现在面积较大的住宅里,增强wifi覆盖范围,智能在多热点之间切换,提升上网体验。因为节点基本上在3个以内,所以mesh的算法不必太复杂,组网形式比较简单。各厂家都自定义了组_802.11s
文章浏览阅读5.2k次,点赞8次,收藏21次。线程的几种状态_线程状态
文章浏览阅读4.2w次,点赞124次,收藏688次。stack翻译为栈,是STL中实现的一个后进先出的容器。要使用 stack,应先添加头文件include<stack>,并在头文件下面加上“ using namespacestd;"1. stack的定义其定义的写法和其他STL容器相同, typename可以任意基本数据类型或容器:stack<typename> name;2. stack容器内元素的访问..._stack函数用法
文章浏览阅读71次。<li> <a href = "“#”>-</a></li><li>子节点:文本节点(回车),元素节点,文本节点。不同节点树: 节点(各种类型节点)childNodes:返回子节点的所有子节点的集合,包含任何类型、元素节点(元素类型节点):child。node.getAttribute(at...
文章浏览阅读3.4k次。//config的设置是全局的layui.config({ base: '/res/js/' //假设这是你存放拓展模块的根目录}).extend({ //设定模块别名 mymod: 'mymod' //如果 mymod.js 是在根目录,也可以不用设定别名 ,mod1: 'admin/mod1' //相对于上述 base 目录的子目录}); //你也可以忽略 base 设定的根目录,直接在 extend 指定路径(主要:该功能为 layui 2.2.0 新增)layui.exten_layui extend
文章浏览阅读3.2k次,点赞6次,收藏13次。分层思想分层思想分层思想-1分层思想-2分层思想-2OSI七层参考模型物理层和数据链路层物理层数据链路层网络层传输层会话层表示层应用层OSI七层模型的分层结构TCP/IP协议族的组成数据封装过程数据解封装过程PDU设备与层的对应关系各层通信分层思想分层思想-1在现实生活种,我们在喝牛奶时,未必了解他的生产过程,我们所接触的或许只是从超时购买牛奶。分层思想-2平时我们在网络时也未必知道数据的传输过程我们的所考虑的就是可以传就可以,不用管他时怎么传输的分层思想-2将复杂的流程分解为几个功能_5g分层结构
文章浏览阅读191次。在激光雕刻中,单向扫描(Unidirectional Scanning)是一种雕刻技术,其中激光头只在一个方向上移动,而不是来回移动。这种移动方式主要应用于通过激光逐行扫描图像表面的过程。具体而言,单向扫描的过程通常包括以下步骤:横向移动(X轴): 激光头沿X轴方向移动到图像的一侧。纵向移动(Y轴): 激光头沿Y轴方向开始逐行移动,刻蚀图像表面。这一过程是单向的,即在每一行上激光头只在一个方向上移动。返回横向移动: 一旦一行完成,激光头返回到图像的一侧,准备进行下一行的刻蚀。
文章浏览阅读577次。强连通:在有向图G中,如果两个点u和v是互相可达的,即从u出发可以到达v,从v出发也可以到达u,则成u和v是强连通的。强连通分量:如果一个有向图G不是强连通图,那么可以把它分成躲个子图,其中每个子图的内部是强连通的,而且这些子图已经扩展到最大,不能与子图外的任一点强连通,成这样的一个“极大连通”子图是G的一个强连通分量(SCC)。强连通分量的一些性质:(1)一个点必须有出度和入度,才会与其他点强连通。(2)把一个SCC从图中挖掉,不影响其他点的强连通性。_强连通分量
文章浏览阅读3.9k次,点赞5次,收藏18次。在做web开发,要给用户提供一个页面,页面包括静态页面+数据,两者结合起来就是完整的可视化的页面,django的模板系统支持这种功能,首先需要写一个静态页面,然后通过python的模板语法将数据渲染上去。1.创建一个templates目录2.配置。_django templates
文章浏览阅读1.7k次。Ubuntu等Linux系统显卡性能测试软件 Unigine 3DUbuntu Intel显卡驱动安装,请参考:ATI和NVIDIA显卡请在软件和更新中的附加驱动中安装。 这里推荐: 运行后,F9就可评分,已测试显卡有K2000 2GB 900+分,GT330m 1GB 340+ 分,GT620 1GB 340+ 分,四代i5核显340+ 分,还有写博客的小盒子100+ 分。relaybot@re...