Python爬虫爬取微信朋友圈


Posted in Python onAugust 06, 2020

接下来,我们将实现微信朋友圈的爬取。
如果直接用 Charles 或 mitmproxy 来监听微信朋友圈的接口数据,这是无法实现爬取的,因为数据都是被加密的。而 Appium 不同,Appium 作为一个自动化测试工具可以直接模拟 App 的操作并可以获取当前所见的内容。所以只要 App 显示了内容,我们就可以用 Appium 抓取下来。

1. 本节目标

本节我们以 Android 平台为例,实现抓取微信朋友圈的动态信息。动态信息包括好友昵称、正文、发布日期。其中发布日期还需要进行转换,如日期显示为 1 小时前,则时间转换为今天,最后动态信息保存到 MongoDB。

2. 准备工作

请确保 PC 已经安装好 Appium、Android 开发环境和 Python 版本的 Appium API。Android 手机安装好微信 App、PyMongo 库,安装 MongoDB 并运行其服务,安装方法可以参考第 1 章。

3. 初始化

首先新建一个 Moments 类,进行一些初始化配置,如下所示:

PLATFORM = 'Android'
DEVICE_NAME = 'MI_NOTE_Pro'
APP_PACKAGE = 'com.tencent.mm'
APP_ACTIVITY = '.ui.LauncherUI'
DRIVER_SERVER = 'http://localhost:4723/wd/hub'
TIMEOUT = 300
MONGO_URL = 'localhost'
MONGO_DB = 'moments'
MONGO_COLLECTION = 'moments'
 
class Moments():
  def __init__(self):
    """初始化"""
    # 驱动配置
    self.desired_caps = {
      'platformName': PLATFORM,
      'deviceName': DEVICE_NAME,
      'appPackage': APP_PACKAGE,
      'appActivity': APP_ACTIVITY
    }
    self.driver = webdriver.Remote(DRIVER_SERVER, self.desired_caps)
    self.wait = WebDriverWait(self.driver, TIMEOUT)
    self.client = MongoClient(MONGO_URL)
    self.db = self.client[MONGO_DB]
    self.collection = self.db[MONGO_COLLECTION]

这里实现了一些初始化配置,如驱动的配置、延时等待配置、MongoDB 连接配置等。

4. 模拟登录

接下来要做的就是登录微信。点击登录按钮,输入用户名、密码,提交登录即可。实现样例如下所示:

def login(self):
  # 登录按钮
  login = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/cjk')))
  login.click()
  # 手机输入
  phone = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/h2')))
  phone.set_text(USERNAME)
  # 下一步
  next = self.wait.until(EC.element_to_be_clickable((By.ID, 'com.tencent.mm:id/adj')))
  next.click()
  # 密码
  password = self.wait.until(EC.presence_of_element_located((By.XPATH, '//*[@resource-id="com.tencent.mm:id/h2"][1]')))
  password.set_text(PASSWORD)
  # 提交
  submit = self.wait.until(EC.element_to_be_clickable((By.ID, 'com.tencent.mm:id/adj')))
  submit.click()

这里依次实现了一些点击和输入操作,思路比较简单。对于不同的平台和版本来说,流程可能不太一致,这里仅作参考。

登录完成之后,进入朋友圈的页面。选中朋友圈所在的选项卡,点击朋友圈按钮,即可进入朋友圈,代码实现如下所示:

def enter(self):
  # 选项卡
  tab = self.wait.until(EC.presence_of_element_located((By.XPATH, '//*[@resource-id="com.tencent.mm:id/bw3"][3]')))
  tab.click()
  # 朋友圈
  moments = self.wait.until(EC.presence_of_element_located((By.ID, 'com.tencent.mm:id/atz')))
  moments.click()

抓取工作正式开始。

5. 抓取动态

我们知道朋友圈可以一直拖动、不断刷新,所以这里需要模拟一个无限拖动的操作,如下所示:

# 滑动点
FLICK_START_X = 300
FLICK_START_Y = 300
FLICK_DISTANCE = 700
 
def crawl(self):
  while True:
    # 上滑
    self.driver.swipe(FLICK_START_X, FLICK_START_Y + FLICK_DISTANCE, FLICK_START_X, FLICK_START_Y)

我们利用 swipe() 方法,传入起始和终止点实现拖动,加入无限循环实现无限拖动。

获取当前显示的朋友圈的每条状态对应的区块元素,遍历每个区块元素,再获取内部显示的用户名、正文和发布时间,代码实现如下所示:

# 当前页面显示的所有状态
items = self.wait.until(
  EC.presence_of_all_elements_located((By.XPATH, '//*[@resource-id="com.tencent.mm:id/cve"]//android.widget.FrameLayout')))
# 遍历每条状态
for item in items:
  try:
    # 昵称
    nickname = item.find_element_by_id('com.tencent.mm:id/aig').get_attribute('text')
    # 正文
    content = item.find_element_by_id('com.tencent.mm:id/cwm').get_attribute('text')
    # 日期
    date = item.find_element_by_id('com.tencent.mm:id/crh').get_attribute('text')
    # 处理日期
    date = self.processor.date(date)
    print(nickname, content, date)
    data = {
      'nickname': nickname,
      'content': content,
      'date': date,
    }  
  except NoSuchElementException:
    pass

这里遍历每条状态,再调用 find_element_by_id() 方法获取昵称、正文、发布日期对应的元素,然后通过 get_attribute() 方法获取内容。这样我们就成功获取到朋友圈的每条动态信息。

针对日期的处理,我们调用了一个 Processor 类的 date() 处理方法,该方法实现如下所示:

def date(self, datetime):
  """
  处理时间
  :param datetime: 原始时间
  :return: 处理后时间
  """
  if re.match('d + 分钟前 ', datetime):
    minute = re.match('(d+)', datetime).group(1)
    datetime = time.strftime('% Y-% m-% d', time.localtime(time.time() - float(minute) * 60))
  if re.match('d + 小时前 ', datetime):
    hour = re.match('(d+)', datetime).group(1)
    datetime = time.strftime('% Y-% m-% d', time.localtime(time.time() - float(hour) * 60 * 60))
  if re.match(' 昨天 ', datetime):
    datetime = time.strftime('% Y-% m-% d', time.localtime(time.time() - 24 * 60 * 60))
  if re.match('d + 天前 ', datetime):
    day = re.match('(d+)', datetime).group(1)
    datetime = time.strftime('% Y-% m-% d', time.localtime(time.time()) - float(day) * 24 * 60 * 60)
  return datetime

这个方法使用了正则匹配的方法来提取时间中的具体数值,再利用时间转换函数实现时间的转换。例如时间是 5 分钟前,这个方法先将 5 提取出来,用当前时间戳减去 300 即可得到发布时间的时间戳,然后再转化为标准时间即可。

最后调用 MongoDB 的 API 来实现爬取结果的存储。为了去除重复,这里调用了 update() 方法,实现如下所示:

self.collection.update({'nickname': nickname, 'content': content}, {'$set': data}, True)

首先根据昵称和正文来查询信息,如果信息不存在,则插入数据,否则更新数据。这个操作的关键点是第三个参数 True,此参数设置为 True,这可以实现存在即更新、不存在则插入的操作。

最后实现一个入口方法调用以上的几个方法。调用此方法即可开始爬取,代码实现如下所示:

def main(self):
  # 登录
  self.login()
  # 进入朋友圈
  self.enter()
  # 爬取
  self.crawl()

这样我们就完成了整个朋友圈的爬虫。代码运行之后,手机微信便会启动,并且可以成功进入到朋友圈然后一直不断执行拖动过程。控制台输出相应的爬取结果,结果被成功保存到 MongoDB 数据库中。

6. 结果查看

我们到 MongoDB 中查看爬取结果,如图所示。

Python爬虫爬取微信朋友圈

可以看到朋友圈的数据就成功保存到了数据库。

7. 本节代码

8. 结语

以上内容是利用 Appium 爬取微信朋友圈的过程。利用 Appium,我们可以做到 App 的可见即可爬,也可以实现自动化驱动和数据爬取。但是实际运行之后,Appium 的解析比较烦琐,而且容易发生重复和中断。如果我们可以用前文所说的 mitmdump 来监听 App 数据实时处理,而 Appium 只负责自动化驱动,它们各负其责,那么整个爬取效率和解析效率就会高很多。

以上就是Python爬虫爬取微信朋友圈的详细内容,更多关于Python爬取微信朋友圈的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
python打开网页和暂停实例
Sep 30 Python
基于Python实现一个简单的银行转账操作
Mar 06 Python
Scrapy框架爬取Boss直聘网Python职位信息的源码
Feb 22 Python
如何通过50行Python代码获取公众号全部文章
Jul 12 Python
Python中的延迟绑定原理详解
Oct 11 Python
Python3.7安装keras和TensorFlow的教程图解
Jun 18 Python
Django框架ORM数据库操作实例详解
Nov 07 Python
OpenCV+Python--RGB转HSI的实现
Nov 27 Python
python异常处理、自定义异常、断言原理与用法分析
Mar 23 Python
keras 实现轻量级网络ShuffleNet教程
Jun 19 Python
Python生成pdf目录书签的实例方法
Oct 29 Python
Matlab如何实现矩阵复制扩充
Jun 02 Python
Python变量及数据类型用法原理汇总
Aug 06 #Python
基于logstash实现日志文件同步elasticsearch
Aug 06 #Python
如何解决pycharm调试报错的问题
Aug 06 #Python
Python用来做Web开发的优势有哪些
Aug 05 #Python
Python结合Window计划任务监测邮件的示例代码
Aug 05 #Python
python爬虫使用requests发送post请求示例详解
Aug 05 #Python
8种常用的Python工具
Aug 05 #Python
You might like
在mysql数据库原有字段后增加新内容
2009/11/26 PHP
php实例分享之mysql数据备份
2014/05/19 PHP
PHP实现的英文名字全拼随机排号脚本
2014/07/04 PHP
golang与PHP输出excel示例
2016/07/22 PHP
PHP多种序列化/反序列化的方法详解
2017/06/23 PHP
PHP实现基于状态的责任链审批模式详解
2019/05/31 PHP
IE8 引入跨站数据获取功能说明
2008/07/22 Javascript
javascript 出生日期和身份证判断大全
2008/11/13 Javascript
Jquery替换已存在于element上的event的方法
2010/03/09 Javascript
判断用户的在线状态 onbeforeunload事件
2011/03/05 Javascript
jQuery获得页面元素的绝对/相对位置即绝对X,Y坐标
2014/03/06 Javascript
JQuery判断checkbox是否选中及其它复选框操作方法合集
2015/06/01 Javascript
JavaScript中的this机制
2016/01/30 Javascript
详解jquery validate实现表单验证 (正则表达式)
2017/01/18 Javascript
vue + socket.io实现一个简易聊天室示例代码
2017/03/06 Javascript
详解nodejs微信公众号开发——6.自定义菜单
2017/04/13 NodeJs
基于匀速运动的实例讲解(侧边栏,淡入淡出)
2017/10/17 Javascript
VeeValidate在vue项目里表单校验应用案例
2018/05/09 Javascript
解决vue动态为数据添加新属性遇到的问题
2018/09/18 Javascript
使用jQuery动态设置单选框的选中效果
2018/12/06 jQuery
JS回调函数 callback的理解与使用案例分析
2019/09/09 Javascript
vue 实现走马灯效果
2019/10/28 Javascript
vue 获取url里参数的两种方法小结
2020/11/12 Javascript
element-ui点击查看大图的方法示例
2020/12/14 Javascript
Python2.5/2.6实用教程 入门基础篇
2009/11/29 Python
深入理解NumPy简明教程---数组1
2016/12/17 Python
python写一个md5解密器示例
2018/02/23 Python
python基础 range的用法解析
2019/08/23 Python
python manage.py runserver流程解析
2019/11/08 Python
python opencv图片编码为h264文件的实例
2019/12/12 Python
CSS3中使用RGBa来调节透明度的教程
2016/05/09 HTML / CSS
颁奖晚会主持词
2014/03/25 职场文书
汉语言文学专业求职信
2014/06/19 职场文书
超市采购员岗位职责
2015/04/07 职场文书
《从现在开始》教学反思
2016/02/16 职场文书
python保存图片的四个常用方法
2022/02/28 Python