Python练习项目

  • 目标
  • 编写Python微博爬虫
    • 数据来源
    • 微博列表请求分析
    • 应答报文分析
    • 获取微博正文
    • 微博正文文本提取
    • 获取多页微博
    • 反爬虫机制应对处理
    • 爬虫完整代码
  • 词云图生成
  • 成果展示

目标

用爬虫程序抓取目标用户人民日报的微博文本,通过分析词频,生成直观的词云图。

编写Python微博爬虫

所谓爬虫程序,其实就是通过计算机运行程序,模拟用户浏览网页的行为,向特定网站发送连接请求,获取网站返回的数据。

  • 所以首先需要确定发送请求的对象,也就是请求的目标网址
  • 其次需要确定发送请求的url中所需要携带的请求参数,从而实现检索特定网页信息
  • 最后则是如何从返回的网页数据中,定位出自己需要的有效信息

注意:微博的接口可能会发生变化,所以请不要盲目照抄,建议按照下述流程独立分析。

数据来源

微博移动版网页(点此跳转)
内容简洁,便于分析,因此选用移动版网页作为爬取对象。

微博列表请求分析

打开目标用户的移动版微博主页:人民日报

注意:此处需要退出微博登录来保证请求内容的普适性。

F12打开开发者工具,这里使用的是谷歌浏览器。选中最上方的Network标签页,刷新页面来监测网络连接请求。

通过分析preview和response两个标签页的内容,可以确定获取微博列表的链接请求为:
https://m.weibo.cn/api/container/getIndex?uid=2803301701&t=0&luicode=10000011&lfid=100103type%3D1%26q%3D%E4%BA%BA%E6%B0%91%E6%97%A5%E6%8A%A5&type=uid&value=2803301701&containerid=1076032803301701

在开发者工具中查看该请求的头部信息,下拉到最后查看请求参数:

分析到请求参数一共有七个:

  1. uid: 2803301701
  2. t: 0
  3. luicode: 10000011
  4. lfid: 100103type=1&q=人民日报
  5. type: uid
  6. value: 2803301701
  7. containerid: 1076032803301701
    其中uidvalue都是用来唯一标识用户的,内容相同,lifd是用来标识微博用户名,containerid用来标识不同范围的微博,107603+uid\rm107603+uid107603+uid表示公开的所有微博,其他参数则都是默认无需变化。
    去掉参数后的请求地址为
    https://m.weibo.cn/api/container/getIndex?
    请求地址+特定参数即可访问特定用户的微博列表。

应答报文分析

通过开发者工具可以发现微博服务器回应请求的是一个较为复杂的json格式文件。
不要慌,一步步分析。
首先,通过request库的get方法,向上述分析出的url地址发送请求,获得回应的字符串文件,代码如下:

import requests
import json
url = 'https://m.weibo.cn/api/container/getIndex?'#headers信息防止触发反爬虫机制
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36'}parameter = {'uid': '2803301701','t': '0','luicode': '10000011','lfid': '100103type=1&q=人民日报','type': 'uid','value': '2803301701','containerid': '1076032803301701','page_type': '60','page': '1'
}
respones = requests.get(url + urlencode(parameter), headers=headers)
#通过json库的loads方法将返回的字符串转换为字典数据格式
data = json.loads(respones.text)

通过使用代码循环遍历字典中的键值对,对每个值输出其类型,是字典则迭代遍历,可以很快搞清楚该json文件的结构,并定位到我们所需用的信息。

该json文件最外层有两个键,一个是ok,值为0或1,代表查询成功或失败。
一个是data,值是一个字典,我们需要的信息在该字典的键cards对应的值中,而cards对应的值则是一个列表。在这里可以推测,因为移动版微博的每条微博都是卡片样式,所以cards中的每一个元素,对应主页的一条微博。

获取微博正文

通过访问微博正文,可以发现url格式为:https://m.weibo.cn/detail/+标识数字\boxed {\rm https://m.weibo.cn/detail/+标识数字}https://m.weibo.cn/detail/+标识数字​每条微博对应一个独一无二的标识数字,而我们需要获取这个标识来访问每条微博。

cards对应的列表中,每个元素都是一个字典,对应了一条微博的主要内容和各种信息,其中我们需要的标识数字,在mblog键对应的值当中。mblog对应的值又是一个字典,而我们需要的数字,则是该字典中的idstrmid键值对
将获取到的微博标识数字与url请求部分结合,可以获取到微博正文。

循环遍历cards列表,即可获取所有微博的正文

对应的代码如下:

#接上述得到的data字典
if data['ok'] == 0:print("爬取完成~")break
if data['ok'] == 1:for item in data['data']['cards']:if 'mblog' not in item:continueblog = requests.get('https://m.weibo.cn/detail/' + item['mblog']['idstr'], headers=headers)

微博正文文本提取

获取到微博正文页面的html代码后,需要从中提取出正文内容。
检查html代码,可以很容易定位到微博正文部分,特征为"text":后面的内容,正文内容被双引号包裹,且不存在换行字符,据此可以正则匹配出正文内容,正则表达式如下:
"text":.∗"\tt"text":.*""text":.∗"
其中.代表任一不是换行符的字符,*表示匹配任意多次,即采用贪婪匹配模式,尽可能多的匹配字符,因为正文对应的代码中可能含有"。而正文结束后会有换行符,所以不担心过度匹配。

匹配完成后,会发现正文中依然含有html代码和首部的"text":多余字符,需要再次匹配除去。采用re库的sub函数,利用正则匹配表达式去除匹配到的字符串。

  1. 去除html代码:html代码都在<>中,非贪婪模式匹配尖括号,来防止正文被除去,这里需要注意,并不是所有微博正文都会含有代码,没有时会抛出异常,需要进行异常处理<.∗?>\tt<.*?><.∗?>
  2. 去除首部的"text":,\s用来匹配空格 :"text":\s"?\tt"text":\backslash s"?"text":\s"?
  3. 去除尾部的:因为引号也会出现在正文中,故这里采用字符串转列表,直接操作列表后再改字符串的方法去多多余的引号

正文处理部分的代码如下:

     #接上部分的for循环for item in data['data']['cards']:if 'mblog' not in item:continueblog = requests.get('https://m.weibo.cn/detail/' + item['mblog']['idstr'], headers=headers)res = re.search('"text":.*"', blog.text)try:blog_text = re.sub('<.*?>', '', res.group())#正文纯文字无代码时捕获异常,继续后续流程except AttributeError:blog_text = res.group()#去除首尾用来定位匹配的字符串final_text = list(re.sub(r'"text":\s"?', '', blog_text))final_text[-1] = ''final_text = ''.join(final_text)

获取多页微博

使用上述请求参数爬取微博时存在一个问题,即微博服务器只会返回10条微博数据。通过向下滑动页面,使用开发者工具检测请求,可以看到下拉页面加载时,多出了page_typepage参数。


首先猜测page参数代表页码,在请求参数字典中加入这两个参数,遍历完response报文中的cards列表后,对page参数执行递增操作,发现可以获取到新的页面微博列表。

实际运行一下程序,发现最多只能抓取2000条左右的微博数据,之后返回的json数据中的ok键值对的值为0,没有微博列表数据。

猜测需要增大page_type,对page_type参数进行测试,当page不变时,改变page_type参数,对结果无影响,但是增大’page_type’之后,page参数可以继续获取微博数据,故page_type决定了你能获取的最大微博数目。

所以当返回的json文件ok值为0,对page_typepage值进行递增,实现抓取大规模微博数据。
当目标微博数量在2000以下时,则只需要递增page值。

反爬虫机制应对处理

实际测试中发现微博对爬虫有着很多限制:

  • 首先,如果无限制爬取网页,很快会被微博服务器发现,会拒绝返回有效数据,提示无内容。所以需要在循环中加入睡眠时间,降低爬取速度。经过测试,1-4秒内随机请求一次即可避坑。
  • 其次,需要在get请求中设置headers参数中的user-agent,通过观察开发者工具中的headers即可获取该参数。否在会触发418状态码错误,即被识别出爬虫程序。
  • 还有,有些情况下,刷新页码后会获取到相同的微博,需要对比微博id,及时跳过
  • 最后,对于每个ip地址,微博似乎限制了其每小时最多能获取的微博数量,会直接拒绝访问,抛出拒绝访问错误。这种情况下只能更换ip地址或者等待一段时间。

爬虫完整代码

将捕获的文本存入文本文档,代码如下:

import random
import re
import traceback
from urllib.parse import urlencode
import json
import time
import requests# 自定义异常类,用于跳出多重循环
class Getoutofloop(Exception):pass# 获取微博列表的url地址
url = 'https://m.weibo.cn/api/container/getIndex?'
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36'}parameter = {'uid': '2803301701','t': '0','luicode': '10000011','lfid': '100103type=1&q=人民日报','type': 'uid','value': '2803301701','containerid': '1076032803301701','page_type': '03','page': '1'
}
txt = open("spider.txt", 'w', encoding='utf-8')
i = 1
j = 1
former = ''
while True:
# 人为设定循环最大次数if j >= 5000:print("爬取结束,i = {},j = {}".format(i, j))# 用于捕获各种异常,保证读取到的数据能正常存入文件try:respones = requests.get(url + urlencode(parameter), headers=headers)time.sleep(random.uniform(1, 4))# 运行状态查看,可以省略print('status:', respones.status_code)if respones.status_code != 200:print('爬虫暴露了!')print('status:', respones.status_code)breakdata = json.loads(respones.text)# 返回ok值为0,改变page_type值if data['ok'] == 0:parameter['page_type'] = str(int(parameter['page_type']) + 1)parameter['page'] = str(int(parameter['page']) + 1)i = i + 1j = j + 1print("i=", i)print("j=", j)continueif data['ok'] == 1:# 循环当前微博列表for item in data['data']['cards']:if 'mblog' not in item:continue# 打印微博id,可以忽略print(item['mblog']['mid'])if former == item['mblog']['mid']:breakblog = requests.get('https://m.weibo.cn/detail/' + item['mblog']['mid'], headers=headers)# 请求状态码不正常,则直接结束程序if blog.status_code != 200:print("爬虫被限制了")raise Getoutofloop()res = re.search('"text":.*"', blog.text)# 当前页面没找到文本的异常处理try:blog_text = re.sub('<.*?>', '', res.group())except AttributeError:print('找不到文本')print(blog.text)continue# 正则表达式提前文字final_text = list(re.sub(r'"text":"?', '', blog_text))final_text[-1] = ''final_text = ''.join(final_text)txt.write(final_text + '\n')former = item['mblog']['mid']else:print('failed')breakj = j + 1parameter['page'] = str(int(parameter['page']) + 1)except BaseException as err:print(type(err))traceback.print_exc()print('no response')break
txt.close()

词云图生成

词云图生成部分比较简单,用到第三方中文分词库jieba分词,再用worldcloud库生成词云即可,需要事先准备好背景图片,生成词云字体颜色从图片背景中获取自动生成,代码如下:

import jieba
import matplotlib
import matplotlib.pyplot as plt
from wordcloud import WordCloud, STOPWORDS, ImageColorGeneratorfin1 = open("文本.txt", "r", encoding='UTF-8')def split_word(fin):text = ''# 读取文件,去除换行符for line in fin.readlines():line = line.strip('\n')# 分词并存入text,注意用空格隔开text += " ".join(jieba.cut(line))# 设置背景图background_picture = "背景图片.jpg"background_Image = plt.imread(background_picture)wc = WordCloud(background_color='white',mask=background_Image,# 设置字体font_path=r'C:\Windows\Fonts\simhei.ttf',# 设置词云中的词语数量max_words=int(100),# 分词用到的停止词,默认即可,也可以自定义stopwords=STOPWORDS,# 最大字号max_font_size=400,# 字体颜色种类random_state=10)wc.generate_from_text(text)wc.recolor(color_func=ImageColorGenerator(background_Image))plt.imshow(wc)plt.axis("off")plt.show()split_word(fin1)

成果展示

应用上述程序分析了2020年3月底到5月初的人民日报微博,生成词云图如下:

可以非常直观的看到,整个四月份,新馆肺炎依然牢牢占据人民日报的关注,并且关注点主要放在了境外输入的确诊病例上。
我们修改程序,爬取2017年12月至2020年5月份的两万多条微博进行分析,结果如下:

可以看到时间跨度拉长后,境外输入占比大大降低,而新冠肺炎确诊病例成为主要高频词,说明了新冠疫情可以算得上是近三年以来中国社会发生的最占据公共注意力的大事件。

一步一步教你做微博用户画像分析:Python微博爬虫+词云生成相关推荐

  1. python名片制作代码_教你做一张能运行 Python 的名片,成本只要20元!

    原标题:教你做一张能运行 Python 的名片,成本只要20元! 作为一名普普通通的开发者,改变世界估计是办不到了,但可以让世界更 Geek 一些. 极客名片的诞生 如标题所说,表面上看这是一个名片, ...

  2. 手把手教您搞定用户画像(战略解读|建模体系|维度分解|应用流程)

    备注:此博客为转载,链接地址请点击此处 手把手教您搞定用户画像 下面,我们就来手把手地教你,如何用敏捷可视化分析搞定用户画像. 什么是用户画像? 在互联网逐渐步入大数据时代后,不可避免的给企业及消费者 ...

  3. python爬虫能干什么-爬虫可以做的事情非常多,Python的爬虫你又了解多少?

    原标题:爬虫可以做的事情非常多,Python的爬虫你又了解多少? 现在爬虫可以做的事情非常多,数据.信息.电影.资料等,你技术过关了,爱咋爬咋爬,仔细观察,加上你如果认真观察,就会发现学习爬虫的人越来 ...

  4. 如何做用户画像分析?

    我们经常在淘宝网购物,作为淘宝方,他们想知道用户是什么样的,年龄.性别.城市.收入.购物品牌偏好.购物类型.平时的活跃程度等,这样的一个用户描述就是用户画像分析. 在实际工作中,用户画像分析是一个重要 ...

  5. 【毕业设计_课程设计】基于Django和vue的微博用户情感分析系统

    文章目录 0 项目说明 1 系统介绍 2 系统功能介绍 3 系统启动注意事项 4 项目工程 0 项目说明 基于Django和vue的微博用户情感分析系统 提示:适合用于课程设计或毕业设计,工作量达标, ...

  6. 公众号用户运营|公众号用户画像分析怎么做?

    想要做好公众号用户运营的前提就是需要我们充分了解用户画像: 什么是用户画像? 用户画像,即用户信息标签化,就是企业通过收集与分析消费者社会属性.生活习惯.消费行为等主要信息的数据之后,完美地抽象出一个 ...

  7. 最近在做的用户留存分析,和几种方法。

    来源:数据分析不是个事儿 我是小z 之前跟大家讲过同期群分析: 都说经典,同同同期群分析到底是个啥? Python数据分析实战 | 经典的同期群分析(附实战数据和代码) 同期群本质上是留存分析的一种实 ...

  8. 微博 用户画像_分析用户画像?从微博数据采集开始!

    想设计一款新的产品,你了解未来的用户是什么样吗?想提升服务质量,用户调研应该怎么做?想评估新功能上线效果,用户情感偏好你了解吗?有了用户画像,这些问题的答案就变得逐渐清晰起来. 那么关键问题来了,用户 ...

  9. 家用电器用户行为分析与事件识别_8个步骤,教你如何开始用户行为分析

    以客户为中心的团队: •全面了解用户的挑战 •了解客户使用什么功能以及不使用什么功能 •了解客户如何从产品中获得最大价值 真正了解客户及其问题是产品经理承担的最重要角色. 当发布新功能后,最紧迫的问题 ...

  10. 微博 用户画像_微博/抖音/快手/小红书/B站内容营销和粉丝画像研究

    用户画像数据在市面上最难获得的一批数据,因为就算大部分咨询公司会对外公开趋势数据和大的宏观数据,一般不会公开用户画像数据,因为这部分的含金量最大,可以用于广告的投放,新媒体的预算分配,个人创业者平台的 ...

最新文章

  1. ubuntu su进入root权限
  2. python 矩形补正方形
  3. 大学python用什么教材-数据结构 Python语言描述 大学教材
  4. java 用来查找输出的函数_Solr复杂查询一:函数查询
  5. Serverless五大优势,成本和规模不是最重要的,这点才是架构
  6. 第5章-Vue.js交互及生命周期练习
  7. VTK:图片之ImageDilateErode3D
  8. asp.net viewstate 数据过大 导致错误
  9. keepass2Android密码,Keepass2Android密码管理(快速解锁密码)
  10. Javascript——进阶(事件、数组操作、字符串操作、定时器)
  11. Objective-C 一些很基础的总结
  12. 8客户端安装后无法启动_QQ下载几天后无法启动、无法运行——可能与火绒有关...
  13. Windows10 关闭自动更新
  14. beego框架开发投票网站(1) beego基础之运行逻辑
  15. 全网疯传!传智播客java基础入门第二版pdf
  16. 简单OCX控件的开发
  17. CAD2010安装→注册
  18. 8位可控加减法电路设计
  19. java web简单线上游戏_手把手教你用Java实现一个简易联网坦克对战小游戏 !
  20. 也谈谈我对腾讯“抄袭”的认识

热门文章

  1. ps渐变怎么用和渐变工具技巧
  2. 对应阻尼下的开环增益matlab,自动控制原理实验指导书MATLAB版解析.doc
  3. HDU 6080 度度熊保护村庄(计算几何+floyd)
  4. 如何在 Android 中减小 APK 大小?
  5. h5页面 请在微信客户端打开链接_如何看到“请在微信客户端打开链接”页面的源码?...
  6. EXCEL表格-绝对引用符$详解
  7. 苹果手机HTML页面滑动卡顿
  8. bootstrap3.x popover报错Cannot read property 'off' of null
  9. 1.7-工控上位机软件开发平台介绍
  10. ACCESS sql语句数据类型转换 文本类型转换为数字类型