微软“最强自动化工具”playwright实战项目


近期微软推出了一款号称“最强”的自动化测试工具,网上便出现了大批讲解文章,一看全是github上的reademe.md。估计是来赚那点文章费的。。。

“只要和网络扯上关系,就能用来做爬虫。”
playwright相比selenium支持异步,相比pyppeteer背后有微软在维护,并且支持录制操作。唯一一点国内好像没有详细的文档,只有官方的英文文档(这让不会英文的我苦比了T。T)。但是“我爱学习”,用一个以前的工作项目来熟悉这个工具吧 ^。*

  • github
  • 官方文档
  • 项目地址,在master分支下

项目介绍

有谱么
爬取有谱么吉他和有尤克里里的每首谱子并保存至png格式,但谱子是渲染上去的所以需使用自动化工具



playwright安装

# 安装python库
pip install playwright -i https://pypi.douban.com/simple
# 安装驱动
python -m playwright install

实现步骤

  1. 获取每首谱子的url
    有谱么这个网站没什么反爬措施,全是api传输数据,稍微分析下就能拿到想要的数据。直接上代码吧。
import requests
from faker import Faker
import asyncio
import os
import json
import aiohttpfake = Faker()YOOPU_TYPE = ["guitar", "ukulele"]# 获得榜单用户id
def userCodeList(t):url = f"https://yoopu.me/users/ranking/totalscore?instrument={t}"headers = {"User-Agent": fake.user_agent()}resp = requests.get(url, headers=headers)data = resp.json()codeList = [user["userCode"] for user in data["userRanks"]]return codeList# 获得用户的曲子id,title
async def songInfo(userCode):headers = {"User-Agent": fake.user_agent(),"Referer": "https://yoopu.me/view-user"}song_list = []async with aiohttp.ClientSession() as session:for i in range(0, 10000, 20):url = f"https://yoopu.me/api/user/sheets?code={userCode}&start={i}&sort=views"print(url, "start")async with session.get(url, headers=headers) as resp:# 边界值data = await resp.text()if data == "[]":breakdata = await resp.json()for song in data:item = {"type": song['type'], "title": song['title'],"artist": song['artist'],"id": song['id']}song_list.append(item)# breakreturn song_list# 回调函数,保存数据
def callback_songInfo(future):song_list = future.result()songs = os.path.join(os.path.abspath(os.path.dirname(__file__)), "songs.txt")with open(songs, "a+", encoding='utf-8') as f:for song in song_list:f.write(json.dumps(song, ensure_ascii=False) + "\n")print(song)def async_run():loop = asyncio.get_event_loop()tasks = []for t in YOOPU_TYPE:code_list = userCodeList(t)for code in code_list:print(code, "start")task = asyncio.ensure_future(songInfo(code))task.add_done_callback(callback_songInfo)   # 回调tasks.append(task)loop.run_until_complete(asyncio.wait(tasks))if __name__ == "__main__":async_run()

运行完数据保存在本地txt文件里,当然也可以redis或其他什么里做个增量爬取。

  1. 获取谱子图片
    单首谱子url格式为: f"https://yoopu.me/view/{id}"
    这里使用playwright的异步操作,先来个简单实例
# 导入包
from playwright import async_playwright
import asyncioasync def func(url):async with async_playwright() as asp:# chrome驱动, 有头模式browser = await asp.chromium.launch(headless=False)# 新建窗口page = await browser.newPage()# 进入网页await page.goto(url)# 截屏await page.screenshot()input()# 关闭浏览器await browser.close()if __name__ == "__main__":loop = asyncio.get_event_loop()url = "https://yoopu.me/view/b19eym10"    loop.run_until_complete(func(url))

上面是一个简单的例子,单项目却要复杂一点,所以我先写一下我的思路:

  • 所有谱子一共有1.3w+,就是说要打开一个浏览器,1.3w+个网页。
  • 一个网页完成任务就关闭,使用信号量 asyncio.Semaphore() 控制并发量,防止网页打开太多电脑卡死,尤其是虚拟机。
  • 有些谱子长度过长为了可以截取长图
    1. 要先获取具体谱子在网页中的位置
    2. 然后修改网页大小至可以完全展示全部谱子长度
    3. 然后浏览器截图和再次记录谱子在网页中的位置
    4. 最后通过 pillow的 crop()方法截取最终图片并保存至本地
  • 网页上还有些小功能键会影响最终效果,所以在打开网页加载完所有网站js后,注入自己写的js,清除掉这个功能键。
  • 保存日志文件方便观察

上代码:

from playwright import async_playwright
import asyncio
import os
import json
from PIL import Image
from loguru import logger
import time
import re# 异常处理
def errPro(func):'''一个装饰器,用来记录异常'''def inner(*args, **kwargs):try: res = func(*args, **kwargs)return resexcept Exception as e:raise Exception(f"{func.__name__} - {str(e)}")return inner#去除特殊字符
def isSpec(l):'''有些 title 或 artist 会包含 "/",会导致异常,这里正则处理一下只有中文,英文字符,-,数字才会保留下来 '''l = json.loads(l)comp = re.compile("[\u4e00-\u9fa5a-zA-Z\-0-9]{0,}")for k,v in l.items():words = comp.findall(v)words = "".join(words)l[k] = wordsreturn l# 读取songs.txt
@errPro
def readSong():'''读取 songs.txt 返回要爬取的歌曲信息'''songs = os.path.join(os.path.abspath(os.path.dirname(__file__)), "songs.txt")with open(songs, "r", encoding='utf-8') as f:song_list = [isSpec(l) for l in f.readlines()]return song_list# 剪切图片
@errPro
def cropPic(pic_path, box):'''使用 crop 剪切出最终图片'''img = Image.open(pic_path)img = img.convert("RGB")img = img.crop(box)img.save(pic_path)logger.info(f"{pic_path} ok")@errPro
async def screenshotPic(songInfo, browser, semaphore):'''处理单个网页'''# 网页urlurl = f"https://yoopu.me/view/{songInfo['id']}"# 图片保存路径pic_path = os.path.join(os.path.join(os.path.abspath(os.path.dirname(__file__)), "pus"), f"{songInfo['title']}-{songInfo['artist']}-{songInfo['type']}.png")async with semaphore:page = await browser.newPage()await page.goto(url)logger.info(f"{songInfo['title']} start")# 等待直到 selector出现,相当于selenium的隐式等待await page.waitForSelector("//hexi-sheet")# 注入js,这个api是在加载网站js后再运行,实现清除两个功能键await page.addScriptTag(content='''document.getElementsByTagName("yp-slider-play")[0].style.display = "none";document.getElementsByClassName("fullscreen-button yoopu3-icon")[0].style.display = "none";''')# 获得谱子对象pu = await page.querySelector("//hexi-sheet")# 获得谱子的边界框值: {左上点xy坐标和宽高} -> dictlocation = await pu.boundingBox()# 调整页面大小await page.setViewportSize(width=int(location['width']*1.2), height=int(location['height']*1.2))# 重新获取谱子的大小location = await pu.boundingBox()# 截屏await page.screenshot(path=pic_path)# 剪切图片box = [location["x"], location["y"], location["x"]+location["width"], location["y"]+location["height"]]cropPic(pic_path, box)# 关闭单个页面await page.close()logger.info(f"{songInfo['title']} end")async def main():async with async_playwright() as asp:# 打开浏览器browser = await asp.chromium.launch(headless=True)# 信号量,限制并发数,这里我用的是虚拟机怕崩掉,所以限制6个并发,依据自己运行环境设置并发量semaphore = asyncio.Semaphore(6)song_list = readSong()#  创建任务列表,这时任务状态还是 Pendingtasks = [asyncio.ensure_future(screenshotPic(song, browser, semaphore)) for song in song_list]# 实现异步嵌套dones, pendings = await asyncio.wait(tasks)for t in dones:t.result()# 关闭浏览器await browser.close()if __name__ == "__main__":# 记录一下时间start = time.time()# 日志保存位置log = logger.add(os.path.join(os.path.abspath(os.path.dirname(__file__)), "yoopu.log"))loop = asyncio.get_event_loop()loop.run_until_complete(main())end = time.time()logger.info(f"共耗时:{end - start}s")

总结一下

1.3w+个任务,每个任务生成3行log,log中也没有出现DEBUG。同一时间存在6个任务,总共耗时9318秒 约等于 2.5个小时,增加并发量还能更快一点。相比selenium同步实现,还算可以吧。。。

playwright常用API
playwright一个比较坑人的地方就是文档是 api名与实际不符合吧,实际api用的是驼峰命名法,还有就是没有案例,不过可以直接看源码,还有参数详解也挺好的。

  • waitForSelector [等待]

  • addInitScript & addScriptTag [注入js,前者是在任何脚本之前执行,后者是在任何脚本之后执行,一般用后者]


  • querySelector & querySelectorAll [查找对象,前者返回一个对象,后者返回一个列表]



  • boundingBox [获得对象的边框值]


  • getAttribute [获得属性值]



  • innerHTML & innerText [前者获得对象html,后者获得对象内所有文本]


  • fill & click [前者填充文本,后者点击,可以组合实现点击搜索功能]



    代码块

from playwright import async_playwright
import asyncioasync def example():async with async_playwright() as asp:browser = await asp.chromium.launch(headless=False)url = "https://yoopu.me/view/b19eym10"page = await browser.newPage()await page.goto(url)await page.waitForSelector("//hexi-sheet")await page.addInitScript(source="alert('hello, world')")await page.addScriptTag(content='''document.getElementsByTagName("yp-slider-play")[0].style.display = "none";document.getElementsByClassName("fullscreen-button yoopu3-icon")[0].style.display = "none";''')pu = await page.querySelector("//hexi-sheet")pu_all = await page.querySelectorAll("//hexi-sheet")print("pu:", pu)print("pu_all:", pu_all)pu_location = await pu.boundingBox()print("pu_location:", pu_location)instrument = await pu.getAttribute("instrument")print("instrument:", instrument)innerhtml = await pu.innerHTML()innertext = await pu.innerText()print("innerHtml:", innerhtml[:50])print("innerText:", innertext[:50])q = await page.querySelector("//form[@class='searchContainer']/input")yoopu3_icon = await page.querySelector("//form[@class='searchContainer']/a")await q.fill("再见")await yoopu3_icon.click()# 阻塞input()await browser.close()if __name__ == "__main__":asyncio.get_event_loop().run_until_complete(example())

我先写这些吧,一些情况也够用了,如果想实现一些复杂的动作链,可以看看文档,如果和我一样看不懂英文可以在这个网站下个百度翻译插件。如果代码有错误请一定要提出来。

看文档不易,点个赞吧 :)

微软“最强自动化工具”playwright实战项目相关推荐

  1. 微软开源的浏览器自动化工具-Playwright

    软开源了一个 Python 项目:Playwright,从此又多了一个浏览器自动化工具.之前一直用 selenium 或 splinter. Playwright 可通过单个 API 自动执行 Chr ...

  2. python能不能自动写代码_微软最强 Python 自动化工具开源了!不用写一行代码

    1. 前言 最近,微软开源了一款非常强大的 Python 自动化依赖库:playwright-python,它支持主流的浏览器,包含:Chrome.Firefox.Safari.Microsoft E ...

  3. python是开源工具吗_微软最强 Python 自动化工具开源了!不用写一行代码

    1. 前言 最近,微软开源了一款非常强大的 Python 自动化依赖库:playwright-python,它支持主流的浏览器,包含:Chrome.Firefox.Safari.Microsoft E ...

  4. 阿里最强 Python 自动化工具开源了

    1. 前言 最近,阿里内部开源了一个 iOS 端由 Python 编写的自动化工具,即:tidevice 它是一款跨平台的自动化开源工具,不依赖 Xcode 就可以启动 WebDriverAgent( ...

  5. 阿里最强 Python 自动化工具开源了!

    1. 前言 最近,阿里内部开源了一个 iOS 端由 Python 编写的自动化工具,即:tidevice 它是一款跨平台的自动化开源工具,不依赖 Xcode 就可以启动 WebDriverAgent( ...

  6. Google 月球 X 大奖结束,无人获奖;微软宣布全新开发者工具项目 Windows Desktop...

    (点击上方蓝字,快速关注我们) 转自:开源中国.solidot.cnBeta.腾讯科技等 0.Google 月球 X 大奖结束,无人获奖 由 X 奖基金会发起,Google 赞助的太空竞赛" ...

  7. 自动化运维工具-Ansible实战指南

    Ansible实战 前言 一.Ansible简介 1.ansible是什么? 2.ansible特点 3.ansible架构 主要模块 工作流程 命令执行过程 二.Ansible 配置 1 安装ans ...

  8. 【Python入门】你值得拥有的这八个实用工具安装包与实战项目

    1.推荐3个画图工具:几个常用的画图工具,这类工具可以帮助理清楚思路,像学习框架也可以用这些做出来. 2.推荐5个实用的编辑工具:工欲善其事必先利其器. 3.推荐4个优秀的实战项目:内功修炼得好,写代 ...

  9. uniapp实战项目 (仿知识星球App) - - 配置开发工具和全局css样式

    实战项目名称:仿知识星球App 技术栈:前端 => uni-app ( 后端:Node.js + Mysql + Apollo + Graphql ) 已实现功能:微信登录,创建星球,内容管理, ...

最新文章

  1. 关于网络的自动协商属性
  2. mysql如何实现读提交锁_MySQL学习笔记(二)—MySQL事务及锁详解
  3. 对生信与计算生物的一点认识[转载]
  4. SpringMVC接收json数据转对象中的一些问题(415错误的解决)
  5. git 常用命令_Git基本常用命令
  6. Delphi编程 -- 如何实现一个支持Visual Basic的For Each调用的COM对象
  7. pyqt5 -——菜单和工具栏
  8. 使用纯生js实现图片轮换
  9. 从Word2Vec到Bert,聊聊词向量的前世今生(一)
  10. 如何使用VideoProc将MKV转换为MP4?
  11. MongoDB 复制集(Replica Set) 配置(Windows 版)
  12. 如何 珍惜自己和珍重别人。珍惜一切
  13. H3C题库HCNE的 最新
  14. VC 获取IE版本号
  15. 一款轻量级android图表组件SimpleChart-Kotlin
  16. eclipse 编译器 unhandled event loop exception 异常解决办法
  17. 计算机主机的拆卸步骤,电脑清灰教程:电脑主机怎么清理灰尘?台式电脑主机清理灰尘教学...
  18. 前端实习小白日记—1
  19. Flex 3 预览版目前已经上架 Cydia BigBoss 源
  20. java笔试题-六一儿童节

热门文章

  1. 固态硬盘装win7系统(win8、win10基本同理唯一不同就是程序用的安装镜像不同)
  2. 一瓶汽水1块钱,3个瓶盖换一瓶汽水,2个空瓶换一瓶汽水,假如手里有5块钱,能喝多少瓶汽水
  3. 为新员工分配部门 (Java经典编程案例)
  4. 索尼录音笔怎么导出录音内容_年轻人的第一台专业录音设备——索尼A10线性录音笔...
  5. Eclipse 加速
  6. 超全的C++开发工程师面经
  7. 微信小程序+OLAMI自然语言API接口制作智能查询工具--快递、聊天、日历等
  8. android开发qq分享图片,android qq分享图片_android qq分享sdk_android qq分享
  9. 小米耳机怎么连接手机(实用方法)
  10. RTP中的FUs打包与NAL的关系