赞
踩
爬虫课题描述可见:
课题解决方法:
微博移动版爬虫
微博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
本专题内对源码粘贴和分析已经比较全面和清楚了,可以满足读者基本的学习要求。源码资源为抛砖引玉,也只是多了配置文件和一些工具方法而已,仅为赶时间速成的同学提供完整的项目案例。大家按需选择
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。