关于腾讯微视频采集解决方案

Github 地址 点这里

前段时间看到一个关于微信数据采集的小项目,我感觉非常不错, 修改它做一些好玩的事,用来获取短视频平台的一些数据,

它很容易理解,也很容易进行修改,可以为以后其他爬虫提供思路。

首先确保你有一台或多台安卓 手机,使用adb 对手机进行模拟操作,我们可以使用代理拦截的方式进行获取数据 当然我们也可以使用其他的方式,多种多样的玩法或许很有成就感

关于adb 使用 可以  点这里

控制手机

from os import system
import random
import time
from threading import Thread


def connect_phone(func):
    """
    装饰器负责每次命令前连接手机
    :param func:
    :return:
    """

    def wrapper(*args, **kwargs):
        system('adb connect ' + args[0].phone)
        return func(*args, **kwargs)

    return wrapper


class PhoneControl():
    def __init__(self, phone):
        """
        :param phone:adb 操作手机所需要的端口信息
        """
        self.phone = phone

    def get_phone(self):
        """
        获取实例手机端口信息
        """
        return self.phone

    @connect_phone
    def get_screen_cap(self, file_name='screen_cap'):
        """
        获取截图
        """
        system('adb -s ' + str(self.phone) + ' shell screencap -p /sdcard/' + file_name + '.png')
        system('adb -s ' + str(self.phone) + ' pull /sdcard/' + file_name + '.png' + ' ./' + file_name + '.png')
        return file_name + '.png'

    @connect_phone
    def input_tap(self, pos):
        """
        点击屏幕
        pos为一个区域的左上与右下坐标
        如果事先已经随机化了pos可以只是一个点 随机点击一个位置是为防止被被认为是机器人
        返回实际点击位置
        """
        if len(pos) == 2:
            _pos = pos
        else:
            _pos = (random.randint(pos[0], pos[2]), random.randint(pos[1], pos[3]))
        command = r'adb -s ' + str(self.phone) + ' shell input tap {} {}'.format(_pos[0], _pos[1])
        system(command)
        return _pos

    @connect_phone
    def input_swipe(self, x1, x2):
        """
        从一个位置滑动到另一位置
        :param x1:起始坐标,屏幕左上角为原点
        :param x2:终点坐标,屏幕左上角为原点
        :return:[x1,x2]
        """
        command = r'adb -s ' + str(self.phone) + ' shell input swipe {} {} {} {}'.format(x1[0], x1[1], x2[0], x2[1])
        system(command)
        return [x1, x2]

    @connect_phone
    def input_roll(self, dx=500, dy=0):
        """
        拉动屏幕 向上拉动屏幕
        :param dx:x方向速度
        :param dy:y方向速度
        :return:[dx, dy]
        """
        command = r'adb -s ' + str(self.phone) + ' shell input roll {} {}'.format(dx, dy)
        system(command)
        return [dx, dy]

    @connect_phone
    def input_key_event(self, event_cmd):
        """
        按键事件 比如home menue back volum_up volum_down等等 具体定义在配置文件中
        :param event_cmd:事件ID
        :return:event_cmd
        """
        command = r'adb -s ' + str(self.phone) + ' shell input keyevent ' + event_cmd
        system(command)
        return event_cmd

    @connect_phone
    def input_text(self, text):
        """
        输入文本信息 可能不支持中文输入
        :param text:待输入的文本信息
        :return:文本信息
        """
        command = r'adb -s ' + str(self.phone) + ' shell input text {}'.format(text)
        system(command)
        return text

    @connect_phone
    def input_chn(self, text):
        """
        支持中文 需要事先将ADB键盘设置为默认输入法而且打开软键盘
        :param text:待输入的文本信息
        :return:文本信息
        """
        # command = r'adb -s '+str(self.phone)+' shell am broadcast -a ADB_INPUT_TEXT --es msg {}'.format(text)
        command = r'adb -s ' + str(self.phone) + ' shell am broadcast -a ADB_INPUT_TEXT --es msg {}'.format(text)
        system(command)
        return text


class OperateAllPhone():
    """
    同时控制给定abd端口的所有手机 请确保手机初始界面一致
    """

    def __init__(self, phone_list):
        """
        :param phone_list:
        """
        self.phone_list = phone_list
        self.pcs = []
        for ap in self.phone_list:
            self.pcs.append(PhoneControl(ap))

    def key(self, event):
        self.operate_all("input_key_event", (event,))

    def text(self, str_data):
        self.operate_all("input_chn", (str_data,))

    def swap(self, x1, x2):
        self.operate_all("input_swipe", (x1, x2))

    def roll(self, dx, dy):
        self.operate_all("input_roll", (dx, dy))

    def tap(self, pos):
        self.operate_all("input_tap", (pos,))


    def operate_all(self, operation, args):
        """
        :param operation: PhoneControl实例方法字符串名称
        :param args:tuple格式参数
        :return:
        """
        _tasks = []
        for pc in self.pcs:
            _tasks.append(Thread(target=pc.__getattribute__(operation), args=args))
        for t in _tasks:
            t.start()
        for t in _tasks:
            t.join()
        return operation


if __name__ == '__main__':
    p = PhoneControl('xxx')
    for i in range(20):
        p.input_swipe([761, 1390], [806, 999])
        time.sleep(0.8)

2 进行某些必须操作, 比如模拟点击加载, 下拉刷新,左右滑动 等

3 如果使用代理拦截 我们这里使用mitmproxy, 可以将拦截到的代理发送到redis或者其地方,也可以使用粘贴剪切板方式,明显代理拦截要更为快捷。

import mitmproxy.http
from mitmproxy import ctx


class Counter(object):
    def __init__(self):
        self.num = 0
        self.url_list = list()

    def request(self, flow: mitmproxy.http.HTTPFlow):
        if 'http://v.weishi.qq.com/shg' in flow.request.pretty_url:
            print('+'*20)
            u = flow.request.pretty_url
            print('+'*20)
            with open('u_list.txt', 'a') as f:
                f.write(str(u) + '\n')


addons = [
    Counter()
]

4 这样就可以拦截到需要的请求,保存到了本地文件

python爬微信视频号 微信视频爬虫_剪切板

5 另外 还有一种方式可以使用 因为代理拦截方式有时候并不那么完美,可以使用复制剪切板的方式