最新要闻
- 什么八字的人长得好看?什么八字的人长得丑?
- 今日快讯:官宣:F1中国站无缘2023赛季 已连续4年缺席
- 世界热文:开发预算突破7亿!网易400人团队“超级项目”《逆水寒》手游获批版号
- 天天快报!2023全球品牌价值500强出炉:苹果跌至第2 抖音杀入前10
- 全球观察:买一赠一:腾讯视频京东Plus会员联名年卡148元大促
- 《魔兽世界》电子骨灰盒出现bug:下载存档错误 账号被锁定
- 排面!小米登上《人民日报》头版
- 情侣拍照时戒指不慎掉入洱海 男生立即跳湖寻找:官方回应太危险不可取
- 天天热文:吃播账号“浪胃仙”被判属原公司:创始人涉职务侵占被捕
- 10辆特斯拉新车高速上集体燃烧 只剩一堆废铁架子
- 美国科技巨头依然寒气逼人 微软即将裁员:1万多人丢了工作
- 每日消息!《最后生还者》热播 收视率仅次于《龙之家族》
- 全球百事通!别人“借钱不还”怎么办?网友公布微信妙招 赶紧学起来
- 全球快资讯丨1月23日国服停!双方谈崩 网友吐槽暴雪绿茶:网易后到底谁来接盘?
- 世界热资讯!冬天加油枪都被冻住了 网友吐槽:国六B汽油里水加多了
- 当前关注:彻底闹掰!网易:暴雪提议蛮横 不符合商业逻辑
广告
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
Python使用pyppeteer搭建网页截图api
(相关资料图)
因为跨语言需要,打算把pyppeteer、图片压缩、aws S3封装成一个api来调用。
首先自然是要安装依赖 pip3 install pillow boto3 pyppeteer
运行一次脚本,pyppeteer会自动下载最新的浏览器到本地
实测windows启动Chrome时需要设置headless=False 阅读几篇关于pyppeteer的文章你应该知道我在说什么。
请求用GET/POST均可,GET将参数放进query,POST将参数放进消息体,使用application/x-www-urluncoded
# 使用GETcurl -i "http://127.0.0.1:9871/?token=xry1029&uri=https://www.baidu.com"# 使用POSTcurl -X POST -i "http://127.0.0.1:9871" --data "token=xry1029" --data "uri=https://www.baidu.com"
切记uri需要加http://,不需要的功能就注释掉吧...
#!/usr/bin/python3# author: 如雨yu# date: 2023/1/18# httpd importimport http.server as BaseHTTPServerimport socketserver as SocketServerimport urllib.parse as urlparseimport threadingimport reimport argparseimport json# chrome importfrom pyppeteer import launch, connectimport asyncioimport tracebackimport timeimport sys# 图片压缩 importimport osfrom PIL import Imagefrom PIL import ImageFile# aws S3 importimport osimport boto3import base64def compress_image(outfile, savefile, kb=1536, quality=75, k=0.75): # tx服务器貌似只要1.5m以内的图 """ :param outfile: 要压缩的文件 :param savefile: 导出文件 :param kb: 压缩目标,KB :param k: 每次调整的压缩比率 :param quality: 初始压缩比率 :return: 压缩文件地址,压缩文件大小 outfile => savefile """ o_size = os.path.getsize(outfile) // 1024 # 函数返回为字节,除1024转为kb(1kb = 1024 bit) if o_size <= kb: os.rename(outfile,savefile) print("[compress] 无需压缩") return savefile ImageFile.LOAD_TRUNCATED_IMAGES = True # 防止图像被截断而报错 while o_size > kb: im = Image.open(outfile) x, y = im.size out = im.resize((int(x*k), int(y*k)), Image.Resampling.LANCZOS) try: out.save(outfile, quality=quality) # quality 质量 except Exception as e: print(e) break o_size = os.path.getsize(outfile) // 1024 os.rename(outfile,savefile) print("[compress] 压缩完成") return savefileasync def launchchrome(): # 全局化变量 global WsToken #global browser browser = await launch(autoClose=False,userDataDir="/home/user/user-data/",args=["--disable-infobars","--disable-gpu"]) WsToken = browser.wsEndpoint print ("[browser] 浏览器启动完成!Ws: "+str(WsToken)) await browser.disconnect()loop = asyncio.new_event_loop()asyncio.set_event_loop(loop)loop.run_until_complete(launchchrome())loop.close()async def screenshot(Width, Height, Uri, Output, JSexec, SleepTime, FullPage): # 连接浏览器 browser = await connect(browserWSEndpoint=WsToken) page = await browser.newPage() # 设置页面视图大小 await page.setViewport(viewport={"width":Width,"height":Height}) # 是否启用JS,enabled设为False,则无渲染效果 await page.setJavaScriptEnabled(enabled=True) # 跳到地址 page.setDefaultNavigationTimeout(15000) #渲染时间15s 多了就是出现未知的bug # 屏蔽webdriver await page.evaluateOnNewDocument("Object.defineProperty(navigator, "webdriver", {get: () => false})") await page.goto(Uri) # 运行js await page.evaluate(JSexec) # 等待时间 time.sleep(SleepTime) # 截图 Saveput = Output Output = Saveput+"-cache.png" # 长截图... if FullPage == "true": await page.screenshot({"path": Output, "fullPage": True}) else: await page.screenshot({"path": Output}) # 压缩一下 compress_image(Output, Saveput) # 断开浏览器 print("[browser] 生成图片完成") await page.close() #关掉标签防止内存太多ww await browser.disconnect()async def StopChrome(): # 连接浏览器 browser = await connect(browserWSEndpoint=WsToken) # 关掉! await browser.close() await browser.disconnect()def upload_files(path_local, path_s3): """ 上传(重复上传会覆盖同名文件) :param path_local: 本地路径 :param path_s3: s3路径 """ print(f"[aws S3] Start upload files.") if not upload_single_file(path_local, path_s3): raise Exception(f"[aws S3] Upload files failed.") print(f"[aws S3] Upload files successful.") def upload_single_file(src_local_path, dest_s3_path): """ 上传单个文件 用upload_files方法 :param src_local_path: :param dest_s3_path: :return: """ try: with open(src_local_path, "rb") as f: s3.upload_fileobj(f, BUCKET_NAME, dest_s3_path) except Exception as e: print(f"[aws S3] Upload data failed. | src: {src_local_path} | dest: {dest_s3_path} | Exception: {e}") return False #print(f"[aws S3] Uploading file successful. | src: {src_local_path} | dest: {dest_s3_path}") return True# 开始操作s3BUCKET_NAME = "" # 存储桶名称# aws_access_key_id和aws_secret_access_key# 使用base64加密一下S3_AKI = b""S3_SAK = b""# str类型CN_S3_AKI = base64.b64decode(S3_AKI).decode("utf-8") CN_S3_SAK = base64.b64decode(S3_SAK).decode("utf-8")CN_S3_AKI = CN_S3_AKI.replace(""", "")CN_S3_SAK = CN_S3_SAK.replace(""", "")CN_REGION_NAME = "" #前缀域名ENDPOINT_URL = "" # endpoint 端点域名 一定看给的那个啊!!!# 打开实例s3_session = boto3.Session(region_name=CN_REGION_NAME, aws_access_key_id=CN_S3_AKI, aws_secret_access_key=CN_S3_SAK)s3 = s3_session.client("s3", endpoint_url=ENDPOINT_URL)print("[aws S3] 初始化完成")class apiHandler(BaseHTTPServer.BaseHTTPRequestHandler): def do_GET(self): path,args=urlparse.splitquery(self.path) self._response(path, args) def do_POST(self): args = self.rfile.read(int(self.headers["content-length"])).decode("utf-8") self._response(self.path, args) def _response(self, path, args): self.send_response(200) self.send_header("Content-type","text/plain") self.end_headers() # 开始call chrome # 参数处理 if args: args=urlparse.parse_qs(args).items() args=dict([(k,v[0]) for k,v in args]) else: args={} Uri=args.get("uri","https://www.baidu.com") ScreenShotPath=args.get("path","test.jpg") Height=int(args.get("h","1080")) Token=args.get("token","null") Width=int(args.get("w","1920")) FullPage=args.get("fullpage","false") JSexec=args.get("jsexec", "void(0);") SleepTime=int(args.get("sleeptime", "0")) err_str = None try: # 在这里可以指定一个token 没有token不生成截图 if Token != "": print("[httpd] 无token访问...") # 抛出无token raise Exception("Bad Token") else: loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) loop.run_until_complete(screenshot(Width, Height, Uri, "<输出图片地址>"+ScreenShotPath, JSexec, SleepTime, FullPage)) loop.close() except Exception as e: err_str = "服务器错误: "+str(e) #+"\n"+traceback.format_exc() if err_str is not None: response_str = err_str else: # 上传s3 upload_files("<输出图片地址>"+ScreenShotPath, "data/"+ScreenShotPath) response_str = "https:///data/"+ScreenShotPath self.wfile.write(response_str.encode("UTF-8"))class ThreadedServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer): def __init__(self, *args,**kwargs): self.screen_lock = threading.Lock() BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs) def safe_print(self,*args,**kwargs): try: self.screen_lock.acquire() print(*args,**kwargs) finally: self.screen_lock.release()if __name__=="__main__": parser=argparse.ArgumentParser() parser.add_argument("-ip","--address",required=False,help="IP address to listen. Default is 127.0.0.1",default="127.0.0.1") parser.add_argument("-p","--port",type=int,help="port to bind",default=9087) args = parser.parse_args() server = ThreadedServer((args.address, args.port), apiHandler) #start the server print("[httpd] Server is Ready. %s:%s" % (args.address, args.port)) while True: try: server.handle_request() except KeyboardInterrupt: break server.safe_print("[main] Control-C hit: Exiting server...") loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) loop.run_until_complete(StopChrome()) loop.close()
如果要关掉chrome呢?当然不是手动ctrl+c然后kill
用这个,重新连接chrome浏览器并且关闭
#!/usr/bin/python3from pyppeteer import connectimport asyncioglobal WsTokenf = open("/home/user/user-data/DevToolsActivePort", "r",encoding="utf-8")Port = f.readline() # 第一行 有\nPort = Port.replace("\n", "")Token = f.readline() # 第二行 没有WsToken = "ws://127.0.0.1:"+Port+Tokenasync def StopChrome(): browser = await connect(browserWSEndpoint=WsToken) await browser.close() await browser.disconnect() # 离谱 关浏览器还要断连...loop = asyncio.new_event_loop()asyncio.set_event_loop(loop)loop.run_until_complete(StopChrome())loop.close()
大概1.5s一张图,还是不太稳定..希望有大佬能帮忙提提意见
Python使用pyppeteer搭建网页截图api
三星移动硬盘质量怎么样?三星移动硬盘打不开怎么解决?
帅康燃气灶怎么样?帅康燃气灶打火后松手就熄灭怎么解决?
qq服务器拒绝发送离线文件是什么意思?qq服务器拒绝发送离线文件怎么办?
什么八字的人长得好看?什么八字的人长得丑?
光波炉的危害有哪些?光波炉与微波炉的区别
移动硬盘目录损坏如何恢复?移动硬盘目录损坏解决方法
今日快讯:官宣:F1中国站无缘2023赛季 已连续4年缺席
世界热文:开发预算突破7亿!网易400人团队“超级项目”《逆水寒》手游获批版号
天天快报!2023全球品牌价值500强出炉:苹果跌至第2 抖音杀入前10
全球观察:买一赠一:腾讯视频京东Plus会员联名年卡148元大促
《魔兽世界》电子骨灰盒出现bug:下载存档错误 账号被锁定
全球快消息!APM vs NPM
排面!小米登上《人民日报》头版
情侣拍照时戒指不慎掉入洱海 男生立即跳湖寻找:官方回应太危险不可取
天天热文:吃播账号“浪胃仙”被判属原公司:创始人涉职务侵占被捕
全球今日讯!学习笔记——Spring中组件扫描(包含扫描、排除扫描)、Spring中完全注解开发;Spring整合Junit4步骤
10辆特斯拉新车高速上集体燃烧 只剩一堆废铁架子
美国科技巨头依然寒气逼人 微软即将裁员:1万多人丢了工作
每日消息!《最后生还者》热播 收视率仅次于《龙之家族》
全球百事通!别人“借钱不还”怎么办?网友公布微信妙招 赶紧学起来
全球快资讯丨1月23日国服停!双方谈崩 网友吐槽暴雪绿茶:网易后到底谁来接盘?
世界热资讯!冬天加油枪都被冻住了 网友吐槽:国六B汽油里水加多了
当前关注:彻底闹掰!网易:暴雪提议蛮横 不符合商业逻辑
天天速看:价值10万土壤被快递弄丢 或致无法毕业 中通:找不到愿赔1000元 后续来了
央视点赞比亚迪:不惧冰雪 用技术发力高端
【天天时快讯】网友抱怨小米13前摄开孔太大!雷军回应:升级了3200万像素 无短板
数据类型的内置方法 可变类型与不可变类型
每日视讯:P1352 没有上司的舞会+P1122 最大子树和(树形DP入门)
丰收1年 挥霍掉400年的积累!东北黑土地不“黑”了
世界视讯!小米13打破安卓手机亮度纪录!雷军:显示效果特别好
环球微资讯!《魔兽世界》电子骨灰盒今日上线 网易提醒:小心安全隐患
三星旗下SEMES前员工窃密:卖给中国企业 获利6.5亿元
世界视讯!强制使用自家支付系统!苹果摊上事:俄罗斯开出12亿卢布罚单
设计模式之模板方法模式和策略模式
语音助手-智能家居
今日要闻!4K分辨率能否吼得住 RTX 3070Ti版戴尔游匣G16游戏挑战
世界最新:索尼独大的CMOS市场:;两匹中企黑马杀出来了
当前快看:年轻人第一辆车来了!传雷军亲自试驾小米汽车 标准三厢
当前关注:学习笔记——Servlet底层源码分析;Servlet接口;ServletConfig接口;
当前观点:宋浩老师专升本
全球速看:MySQL笔记01: MySQL入门_1.1 MySQL概述
速递!笔记本也不行了 将交出10年最差成绩单
天天快资讯:官方揭秘中国空间站:13年前秘密启动!至少用10年
【新要闻】专利暴露苹果野心!未来苹果全家桶将支持卫星通信
世界播报:漫威宇宙归来!两部大片《黑豹2》《蚁人3》国内定档:预告看个爽
全球最新:学习笔记——Spring中的注解;Spring中装配对象的注解;使用注解配置对象中属性
当前头条:node-sass安装问题
焦点热讯:2023首批版号出炉!米哈游双端大作《崩坏:星穹铁道》获批
讯息:特斯拉股价大跌!马斯克甩锅:未来可能跌得更狠
新动态:靠“车震”充电?宝马最新专利整活
世界观点:官方回应2000元帐篷房像45元招待所属实:游客称被气晕过去 双方已谅解
全球热讯:宏碁推出Nitro XV5 4K显示器:可超频至200Hz高刷
今日最新!LeetCode刷题:343. 整数拆分的完全背包写法解析
焦点报道:AcWing. 1073 树的中心
环球热点!湖北襄阳五分钟内连发两次地震:无破坏性 官方科普地震如何逃生
每日资讯:VScode和cmake的组合拳
手机硬件光追 为啥一直是PPT?
世界快资讯丨骁龙888站起来了!小米11系列MIUI 14稳定版就绪:流畅度暴增
简讯:《三体》电视剧第五集惊现《GTA》地图 网友:梦幻联动、双厨狂喜
滴滴回来了!但没有掌声
车主注意!今日起 全国严查酒驾醉驾
【天天报资讯】计算机基础 数据类型 流程控制 字符编码 文件操作
【新视野】ARP欺骗攻击:利用driftnet截获图片数据流
环球新消息丨剧毒蓝环章鱼再现 网友:山姆超市买的、没吃直接给扔了
今晚油价下调!春节出行加满一箱油将少花八元
全球今热点:日系车后期果然省钱!丰田10年维保花费4万元 美国车最低
环球短讯!25年经验资深机长驾驶 3D还原尼泊尔客机坠机过程:让人后怕
当前报道:三星Galaxy Book 3 Pro 360笔记本来了:12核i7 还有3K触摸屏
环球焦点!portswigger 靶场之 XSS 篇(上)
每日快播:JavaWeb开发中在服务器常用命令集锦
今日热文:学习笔记——Spring管理第三方bean;Spring中Bean的作用域;Spring中Bean的生命周期;Spring中bean的后置处理器;Sp
程序员同事每天准点下班,原来是用了这6个开发工具
【NAS使用心得】使用Synology Photos管理照片
【环球新要闻】70多岁大爷开40万极氪001买菜 直言此前开的奔驰真是垃圾
万茜吐槽自己游戏手法不行!网友发现本人玩的是《剑网3》
网易云音乐TV版发福利!150小时电视端会员听歌时长免费领
赵长江:腾势D9时速120km轮胎被磕到、车身稳如泰山
iPhone印度造:要跟我们平分蛋糕!
个人使用 sudo 方法
环球视讯!iOS APP上架流程(详细)
焦点观察:聚焦技术与体验极致提升,阿里云视频云连续5年领跑!
还在用 Excel 和 SQL?火山引擎 VeDI 这款产品帮你更快处理数据
AI偷偷写70多篇新闻:数月后才被人发现
今日报丨与网易延长半年合作失败 内幕曝光:分手一事错怪暴雪了?
贾跃亭下周回国?FF发春节“回家”海报:或暗示将落地中国
【新要闻】不到3年狂揽1.41万亿元?抖音怎么做到的?
【天天热闻】特效太炸!《王者荣耀》李信新皮肤公布 碾压一念神魔
JNPF流程审批加签功能讲解
14年合作结束!暴雪网易彻底谈崩 玩家不满:暴雪甩锅被识破
紧跟特斯拉、问界!小鹏汽车宣布降价:14.89万元起
3000次超长寿命国产闪存 致态512GB SSD到手229元
资讯:印度男子得子后去尼泊尔还愿:不幸遭遇空难
马斯克曾大赞中国工人能工作到凌晨3点 特斯拉德国工人不干了
世界快讯:温州一奔驰越线撞击比亚迪 比亚迪调转180度 车头都快没了
环球速读:魔兽国服1月24日关服!暴雪称已提议延期半年 网易拒绝
图书管理系统
天天最新:Educational Codeforces Round 14
每日速读!“抠书式”还原的剧版《三体》获赞!动画版已扑街:豆瓣跌至4.6分
领券还送牙刷!三金西瓜霜牙膏狂促:6支到手23.7元