更多>>关于我们

西安鲲之鹏网络信息技术有限公司从2010年开始专注于Web(网站)数据抓取领域。致力于为广大中国客户提供准确、快捷的数据采集相关服务。我们采用分布式系统架构,日采集网页数千万。我们拥有海量稳定高匿HTTP代理IP地址池,可以有效获取互联网任何公开可见信息。

您只需告诉我们您想抓取的网站是什么,您感兴趣的字段有哪些,你需要的数据是哪种格式,我们将为您做所有的工作,最后把数据(或程序)交付给你。

数据的格式可以是CSV、JSON、XML、ACCESS、SQLITE、MSSQL、MYSQL等等。

更多>>官方微博

西安鲲之鹏
陕西 西安

加关注

  • 【经验分享】关于彻底关闭Nox模拟器虚拟机
    背景: 命令行关闭Nox虚拟机可以使用"NoxConsole.exe quit <-name:nox_name | -index:nox_index>", 但是有时候会失败。
    这里采用一种保险的思路,先调用"NoxConsole.exe quit"进行安全关闭,若干秒后检测虚拟机对应虚拟机的Nox.exe进程(考虑到多开的情况,根据"-clone:"参数判断是否属于当前虚拟机实例)和NoxVMHandle.exe进程(考虑到多开的情况,根据"--comment"参数判断是否属于当前虚拟机实例)是否还存在,如果存在就强制终止这两个进程,达到彻底关闭的目的。

    完整实现如下图所示。
    发布时间:2021-11-02 10:16:20
  • 【经验分享】如何计算阿里系Ajax请求中的sign签名?
    有过阿里系采集经验的开发者都应该知道,某宝(天猫)H5版、1688、某宝司法拍卖H5版等阿里系网站,在Ajax请求中都会有一个sign签名参数(如下图1、2、3所示),要是值不正确将无法获取到有效的数据(例如返回“非法请求”提示)。如果我们无法构造出有效的sign,就只能通过“模拟浏览器操作”的方式来绕过签名验证,再结合"mitmproxy动态抓包脚本"来提取返回数据,这种方案效率太低,而且很不灵活。本文将介绍如何计算这个sign值以及给出对应的Python实现,这样就能实现通过直接HTTP交互抓取数据。点击链接查看详情>>> http://www.site-digger.com/html/articles/20211018/851.html

    •  
    发布时间:2021-10-18 14:59:23
  • 【经验分享】mysqldump时的两点技巧
    (1)如何避免锁表?
    加上--single-transaction=TRUE参数即可。
    来源:https://stackoverflow.com/questions/104612/run-mysqldump-without-locking-tables
    (2)如何排除某张表?例如 避免导出尺寸太大的日志表。
    使用--ignore-table=dbname.tablename指定即可,如果要排除多个,加上多个--ignore-table=dbname.tablename参数。
    来源:https://www.cnblogs.com/rxbook/p/7735485.html
    发布时间:2021-10-09 11:40:29
  • 【经验分析】urllib2使用"User-Agent"设置UA会无效原因的分析

    (1)如下代码,尝试使用"User-Agent"设置UA为"test",会失败:
    服务端接收到的UA信息为"Python-urllib/2.7",而不是"test"。
    urllib2.build_opener().open(urllib2.Request(url='http://192.168.1.200:1234', data=None, headers={'User-Agent': 'test'}))
    (2)将headers修改为{'User-agent': 'test'},成功。
    如附图1所示。原因是什么呢?

    可以在urllib2.py中找到答案:
    (1)首先urllib2默认会在headers列表中添加一个“User-agent”,其值为"Python-urllib/%s" % __version__,如附图2所示。
    (2)在do_open()中对headers进行了规范化处理(.title()),代码如下。
    headers = dict((name.title(), val) for name, val in headers.items())
    如图3所示,我们在该句前后分别打印headers,处理之前为:
    {'Host': '192.168.1.200:1234', 'User-Agent': 'test', 'Connection': 'close', 'User-agent': 'Python-urllib/2.7'},里面有我们设置的'User-Agent': 'test'
    处理之后就变成下面了:
    {'Host': '192.168.1.200:1234', 'Connection': 'close', 'User-Agent': 'Python-urllib/2.7'}
    原因是后面的'User-agent'经过.title()后也会变'User-Agent',在字典中覆盖掉了我们自定义的值。

    以后使用urllib2要设置UA时,一定要用“User-agent”,而不能用“User-Agent”!
    发布时间:2021-08-15 13:48:07
  • 【经验分享】在分析别人代码的时候,通过打印调用栈可以帮助我们快速掌握调用函数的上下文调用逻辑。Python中如何打印调用栈呢?如下。
    import traceback
    traceback.print_stack()
    参考>>> https://stackoverflow.com/questions/1156023/print-current-call-stack-from-a-method-in-python-code
    例如,我们想知道httplib.py中_send_request()函数的调用上下文,在_send_request()中加入上述代码,当代码执行的时候就会在控制台打印出调用栈信息,如附图所示。
    发布时间:2021-08-15 13:17:33
  • 【经验分享】如何给python函数增加一个timeout功能?
    想要实现的效果:
    在调用一个函数(不固定)的时候如果在指定的N秒内没有返回,就强制停止。

    在github上找到了一些方案例如func_timeout,timeout_decorator。经过测试对比,func_timeout比较理想,项目主页:github.com/kata198/func_timeout,“Python module to support running any existing function with a given timeout.”。它提供了一个装饰器func_set_timeout,非常方便。

    timeout_decorator原理:
    它是利用子线程StoppableThread来执行目标函数,当指定时间到达,子线程还未结束,就强制结束子线程,然后抛出FunctionTimedOut异常,详见github.com/kata198/func_timeout/blob/master/func_timeout/dafunc.py。
    发布时间:2021-06-24 14:11:26
  • 【演示】得物APP商品"最近购买列表"采集演示
    (1)根据商品的spuid从APP端采集。
    (2)得物的“最近购买”列表展示了最近3个月的销售记录,可以借以分析商品的价格和销量波动情况。包括字段“昵称、日期、规格、价格”,采集好的示例数据见:http://db.site-digger.com/csv/646577755f6170705f70726f647563745f6c6173745f736f6c645f6c6973745f73616d706c65/ 点击查看西安鲲之鹏的微博视频 ​​​
    发布时间:2021-06-24 13:17:22
  • 【演示】某小红书APP笔记详情及评论数据采集最新(20210618)演示
    近日某小红书的网页版不再展示笔记的“点赞数、评论数、收藏数和分享数”了,为了能拿到这些信息只能从APP端入手。本采集方案可以拿到笔记的所有属性值以及前10条评论数据。

    (1)笔记详情包含的字段
    'note_id'(笔记ID), 'note_type'(笔记类型,是普通的还是视频), 'user_red_id'(用户ID), 'user_name'(用户名), 'liked_count'(点赞数), 'shared_count'(分享数), 'collected_count'(收藏数), 'comments_count'(评论数), 'pubtime'(笔记发布时间), 'hash_tags'(标签), 'images_list'(原图列表), 'video'(视频链接,如果是视频笔记), 'desc'(笔记内容)
    示例数据链接:点击查看"小红书笔记示例数据"

    (2)笔记评论包含的字段
    'comment_id'(评论), 'note_id'(关联的笔记ID), 'user_name'(用户名), 'pubtime'(评论发布时间), 'like_count'(评论点赞数), 'score'(评分), 'content'(评论内容), 'sub_comment_count'(评论回复数), 'sub_comments'(评论回复内容)
    示例数据链接:点击查看小红书评论示例数据

    某小红书APP笔记详情及评论数据采集最新(20210618)演示

    发布时间:2021-06-18 14:30:34
  • 【经验分享】某特来电APP采集方案
    我们分析的是V5.11.0版本,加了360的壳,用了ssl证书固定。
    (1)经过ssl unpinning之后,Fiddler成功抓到包,如图1-3所示,分别对应“充电站搜索返回的列表”,“充电站详情”和“充电站的终端列表”。可以看到请求头中有很多陌生的参数,例如AVER,它是怎么构造出来的?
    (2)脱壳,反编译找到了实现过程代码,如图4所示。可以看出AVER是通过对时间戳参数ATS,经过"DES/CBC/PKCS7Padding"加密而成。其它参数在代码中也都能找到实现过程。其中"X-Token"的产生过程比较复杂,后面再分享产生过程。
    发布时间:2021-06-08 12:10:34
  • 【经验分享】com.google.gson.Gson的toJson()方法在插桩分析的时候太有用了,赞赞赞。
    function toJson(javaObject) {
        return Java.use("com.google.gson.Gson").$new().toJson(javaObject);
    };
    通过toJson(javaObject)可以将Java对象(数据结构)转换成JSON格式,非常方便。想想之前都是通过字符串拼接各个字段(熟悉)值,太费劲儿了。

    关于com.google.gson.Gson的toJson()的更多示例可以看这篇文章:https://www.cnblogs.com/reboost/p/9521603.html
    发布时间:2021-06-04 10:57:34
当前位置: 首页 > 技术文章 >
基于CDP实现微信小程序UI自动化
发布时间:2022-04-14

        CDP是Chrome DevTools Protocol的简称,即Chrome远程调试协议。基于这个协议可以实现对Chrome浏览器或者使用WebView组件的应用进行调试、分析。简而言之,我们可以通过这个协议来操作Chrome或者WebView。CDP的官方文档是https://chromedevtools.github.io/devtools-protocol/。WebView的应用非常广泛,它常被用在原始APP中展示H5页面,微信小程序使用的X5和XWeb内核也是基于WebView实现的。

        在众多Web自动化测试和爬虫解决方案中,基于CDP操作PC Chrome浏览器已经不是什么新鲜事儿了,例如微软的Playwright(https://playwright.dev/python/)库,就是基于CDP来控制Chrome浏览器的。本文主要介绍的是通过CDP来操作安卓设备上WebView/X5/XWeb,特别是微信小程序。老版本微信的使用X5内核作为小程序的UI,新版微信使用XWeb内核作为小程序的UI,前面提到过它们都是基于WebView的,因此也支持CDP。

        CDP采用C/S模式工作,客户端和服务端基于HTTP和WebSoket协议交互。要使用CDP我们需要先开启Chrome浏览器或者WebView/X5/XWeb的CDP服务端。对于Chrome浏览器可以通过指定"--remote-debugging-port=9222"参数来暴露出CDP接口(开启DevTools服务端,监听TCP:9222端口),那么对于安卓上的WebView/X5/XWeb该如何操作才能开启CDP服务端呢?

        操作步骤如下:

        1. 首先要打开安卓设备的USB调试。将设备连接至测试机,并确保"adb devices"能找到设备。为了避免不同品牌真机上操作路径不一致的麻烦,建议使用模拟器来测试。例如MuMu模拟器,启动后执行"adb connect 127.0.0.1:7555",即可建立adb连接。

        2. 启动WebView/X5/XWeb的远程调试。

            (1)  对于使用XWeb内核的新版微信,可以通过在聊天窗口发送并访问“http://debugxweb.qq.com/?inspector=true”链接来启用远程调试。注意:访问这个链接会自动跳转到微信官方首页,没有任何提示。PS:这个方法是在微信开放社区里看到的。

            (2)  对于使用X5内核的老版微信,打开远程调试的方法网上可以找到很多,加之现在的微信已经换用XWeb内核,因此这里不再赘述。

            (3)  对于自己开发的原生APP,要使用WebView的远程调试,需要安卓开发同学在程序中调用"WebView.setWebContentsDebuggingEnabled(true)"来开启。对于线上它厂的APP,一般远程调试是关闭的,可以借助第三方的Hook工具(例如 WebViewDebugHook这个Xposed插件),强制其打开调试开关。

        3. 验证是否成功启动了CDP服务端。

        在这里要插一个知识点unix domain socket(Unix域套接字,下简称UDS)。在Linux系统中套接字家族有两兄弟:老大是大名鼎鼎的TCP/IP套接字,老二叫做Unix域套接字,它主要用于同一机器上不同进程间的通信,用法基本上和TCP/IP的套接字一样,不过它不依赖于IP和端口,而是依赖于文件描述符。在同一台机器上进程间通信UDS要比TCP/IP套接字效率更高,因为它不需要网络协议栈,不需要打包、拆包、计算校验和、维护序号等过程。

        安卓系统的WebView CDP服务端正是使用的UDS,因此我们可以通过检查系统的UDS来判断CDP服务是否启动起来了。

        我们先在安卓系统里启动一个微信小程序,例如58同城。然后在本地机器上执行如下命令(二选一):

# 查看安卓设备的Unix域套接字列表,过滤出含有"devtools_remote"字样的条目
adb shell netstat -lx|grep "devtools_remote"
# 或者执行如下命令也可以
adb shell cat /proc/net/unix |grep -a "devtools_remote"

        如下图所示,显示了多个"devtools_remote"相关的条目,但是具体是哪一个对应当前运行的微信小程序呢?

查看安卓系统的Unix域套接字列表

        我们用"adb shell dumpsys activity top|head"查看一下当前顶层窗口所属的进程ID,如下图所示,pid=1926。因此上图中的“xweb_devtools_remote_1926”是当前运行小程序的WebView启动的CDP服务端对应的Unix域套接字。

adb shell dumpsys activity top结果截图

        如何能在本机访问安卓系统里的这个Unix域套接字呢?需要借助"adb forward"命令把“xweb_devtools_remote_1926”映射为本地的一个TCP端口。完整命令如下:

adb forward tcp:9222 localabstract:xweb_devtools_remote_1926

        然后在本机浏览器里尝试访问"http://127.0.0.1:9222/json",如果能够看到正常的JSON数据(如下图所示),则说明一切就绪。

CDP HTTP JSON接口

        这里如果我们直接点击“devtoolsFrontendUrl”对应的链接,会自动打开Google DevTools工具(如下图所示),这里的操作就和F12打开开发人员工具调试本机浏览器页面一样。注意:如果打开是空白页或者提示404,是因为appspot.com这个域名被Q了,需要FQ才能正常使用。

CDP DevTools调试微信小程序示例

        现在CDP服务端已经就绪了,使用Google DevTools我们也可以直接查看和控制小程序的UI了,但是如何编写自动化脚本呢?下面给出一个完整的Python示例。

        pychrome是github上一个Google Chrome Dev Protocol Client的Python实现。我们下面使用pychrome来连接服务端"http://127.0.0.1:9222/",并操作小程序UI实现多次上划的操作。完整代码如下所示。

# coding: utf-8
# android_webview_automate.py
# 基于CDP自动化操作安卓WebView(包括微信小程序)

import time
import pychrome

def test():
    # 连接CDP Server
    browser = pychrome.Browser(url='http://127.0.0.1:9222')
    
    # 找到可见tab
    tab = None
    for _tab in browser.list_tab():
        if ':VISIBLE' in _tab._kwargs['title']:
            print('找到可见TAB', '"{}".'.format(_tab._kwargs['title']))
            tab = _tab
            # 必须要先调用start(),否则pychrome不允许后续接口调用
            tab.start()
            break
    if not tab:
        print('未发现可见TAB.')
    else:
        # 通过执行js实现获取当前可见窗口大小
        # https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-evaluate
        jsresult = tab.Runtime.evaluate(expression="'' + window.screen.availWidth + ',' + window.screen.availHeight")['result']['value']
        print('当前可见窗口大小(宽,高):', jsresult)
        width, height = jsresult.split(',')
        width, height = int(width), int(height)
        # 通过调用CDP的Input.synthesizeScrollGesture()方法模拟向上滑屏操作
        # https://chromedevtools.github.io/devtools-protocol/tot/Input/#method-synthesizeScrollGesture
        # 起始位置:x坐标定为屏幕宽度的一半, y坐标定为屏幕高度的四分之三
        x = int(width * 0.5)
        y = int(height * 0.75)
        # 向上滑动的偏移:xDistance,yDistance为"-height * 0.5",表示上划总屏幕一半的高度(负值表示向上)
        yoffset = int(height * 0.5)
        # 上划10次
        for i in range(10):
            tab.Input.synthesizeScrollGesture(x=x, y=y * 0.75, xDistance=0, yDistance=-yoffset)
            print(i + 1, '上划{}px.'.format(yoffset))
            time.sleep(1)
        
if __name__ == '__main__':
    test()

        运行效果如下GIF所示。本例只是抛砖引玉,CDP的功能非常强大,大家可以结合CDP的文档发掘更多的功能。

Python基于CDP控制微信小程序UI演示视频

特别说明:本文旨在技术交流,请勿将涉及的技术用于非法用途,否则一切后果自负。如果您觉得我们侵犯了您的合法权益,请联系我们予以处理。
☹ Disqus被Qiang了,之前所有的评论内容都看不到了。如果您有爬虫相关技术方面的问题,欢迎发到我们的问答平台:http://spider.site-digger.com/
QQ在线客服
欢迎咨询,点击这里给我发送消息。
欢迎咨询,点击这里给我发送消息。

加微信咨询