• [热门活动] 直播回顾-CG行业上云,不烧钱也能做大片
    随着人们生活水平的提高,人们对娱乐的需求也越来越高,影视需求就是其中之一。近些年来,我国的影视动画产业发展势头迅猛,《西游记之大圣归来》、《哪吒之魔童降世》、《白蛇·缘起》、《捉妖记》等优秀影视作品比比皆是。然而这些影视作品精彩之一就是特效,具有美感的特效场景才能带给用户极致的观感体验。说到特效,那么渲染行业发展至今,是什么样的趋势呢?未来又将如何?影视动画行业已然进入发展快车道,由此,对软件、硬件要求更高,对公有云更依赖;数字多媒体行业随着大尺寸普及,8K-10K增多,对精度要求更高;其他行业也对云服务要求也逐步增多;用户定制化需求量逐渐递增, 需匹配用户本地环境及需求。挑战和机遇并存,渲云与华为云深入合作,laas层基于成熟的华为云公有云平台模式,基于公有云传输协议,提升传输效率,加强传输安全,保障硬核储存资源,适应企业云时代的发展变化。渲云云桌面渲染上云价值:渲云云桌面渲染方案上云价值:渲云客户案例:《唐人街探案2》累计票房突破33亿,整部影片一共500多个特技镜头,涉及复杂的三维镜头有200多个。渲云在电影后期制作过程中提供了高效的渲染解决方案与技术服务,为其调配了大规模高端渲染服务器,为电影的项目周期提供了充分保障。《捉妖记2》不仅是第一部在剧情上的延续,还有全面升级的特效制作,从第一部的1200个特效镜头升级到为1800个特效镜头。渲云配合制作方的要求,以专业化的服务团队、大规模高性能渲染数据中心来确保项目按期按质完成,高速的渲染能力应对后期庞大的渲染量,游刃有余。当然,渲云云渲染不仅可以运用在影视动画领域,还可以运用在建筑设计、VR/AR、互动游戏、工业设计等多种领域,帮助用户快速完成三维内容、动画及效果图的渲染计算。未来,期望渲云继续与华为云深耕云渲染领域,为大众提供更多优秀的渲染场景!直播回放:《CG行业上云,不烧钱也能做特效大片》渲云云桌面渲染服务:https://marketplace.huaweicloud.com/contents/4e9ec5b4-06c7-438b-8e79-725b92195674?marketplace_live_20210629 
  • [热门活动] “渲云”你的得力好助手
    关于“渲云”我的了解有如下:渲云客户端是一款面向三维设计师的自助式云渲染工具,轻松调动海量服务器资源,帮您快速完成渲染。渲云现已支持corona渲染器、vray渲染器,支持通道渲染、多镜头提交、光子渲染、分享结果文件等功能,可完全满足用户使用需求。渲云与多家全球知名公有云平台达成深度合作,实现海量节点拓展,提供全球性极速渲染服务, 用户遍及亚洲,欧美,中东等区域,为全球用户带来极致非凡的云渲染解决方案服务。下面有几个问题需要解答:渲云平台支持各种元素输出以及V-Ray Frame buffer校色吗?如何判断渲染文件是否在正常渲染?C4D渲染丢帧怎么办
  • [热门活动] 《CG行业上云,不烧钱也能做特效大片》——看直播,参与互动赢华为云好礼,100元渲染券&100;元呆猫桌面云体验券
    当提起《斗罗大陆》、《唐人街神探》、《金刚川》、《三生三世十里桃花》等影视作品时,你的脑海里是不是浮现出酷炫的特效和精彩绝伦的CG动画?▼                                                                                                                                                   *以上影视作品均由赞奇科技(渲云)参与渲染一部好的电影离不开好的导演、编剧、演员对于动画、科幻等特效要求高的电影作品来说,电影渲染技术尤为重要在建筑设计、VR/AR、互动游戏、影视动漫、工业设计等多领域三维内容制作高效渲染也是必不可少的环节▼ 本期直播邀请国内渲染行业领航者-赞奇科技CEO金伟为大家解锁如何基于华为云公有云,GET快速、高效、稳定的渲染新姿势!有奖直播:《CG行业上云,不烧钱也能做特效大片》(点我)直播嘉宾:赞奇科技CEO——金伟Kim直播时间:6月29日(周二)19:00-20:00推荐严选产品:渲云云桌面渲染服务(低至0.18元 /核/小时)渲云直播好礼1:问卷闯关,渲云速览点击直播页面向下拉,点击“问答闯关”按钮获得答卷(点我)填写华为云ID并且答题(5题),得分在75分以上且前500名提交的用户即可获得500码豆了解渲云,探索影视后期制作的秘密!(小云提示:所有问题的答案可在渲云官网(点我)找到答案哦!)了解什么是华为云码豆?(点我) (扫我加入社群)本次活动所有奖励领取,必须加入社群并收看直播,否则无效!群内添加群主小云微信——还可获得渲云独家[3dmax]资料包一份!渲云直播好礼2:畅谈渲染,赢华为云码豆&无门槛渲染券作为“设计狮”,渲云的资深用户又或者是首次接触渲云,对CG动画、三维设计感兴趣在这其中,对渲云的使用心得,对渲云的了解,“CG狗”的心路历程在本帖回复#渲云渲染#+一切关于“渲云”的议题且不少于50字便可获300码豆,活动结束后,将评选出4条最佳畅谈,赠送5000码豆+100元无门槛渲染券(一个id仅为一个畅谈可获码豆,但可提多个畅谈参与评选,限前500名)。例:(1.使用心得:#渲云渲染#偶然从朋友口中得知渲云这款云渲染软件,我就使用了一段时间,感受是真的很不错,无论是软件界面、安装程序大小、操作等方面都十分贴合设计师的需求,简单流畅,体验nice。2.对渲云的了解:#渲云渲染#赞奇科技成立于2010年,致力于三维视觉计算云技术研发与应用,目前完全自主研发并运营云渲染平台,为影视、动漫、建筑、室内室外、游戏、工业设计等用户提供云渲染服务以及其他相关的云PaaS、SaaS服务。)(扫我加入社群)本次活动所有奖励领取,必须加入社群并收看直播,否则无效!群内添加群主小云微信——还可获得渲云独家[3dmax]资料包一份!渲云直播好礼3:直播提问,四轮抽奖,“幸运鹅”就是你直播观看地址,点击沾好运!!1.直播间提问:直播期间在“问答区”向赞奇科技CEO金董提问,将抽取问题进行答疑,被选中第1-3个问题获得100元“渲云”无门槛渲染券,第4-5个问题获得“渲云”超大定制限量鼠标垫,第6-7个问题获得“呆猫”桌面云100元代金券,第8-10个问题获得5000码豆!!!           (问题问得好,福利少不了!)(提问格式:手机号码后四位+问题,例:8898+哪里有海量渲染优惠体验券让我**?如何花低价钱制作出高大上的CG成图?)一定要来看直播,锁定6/29日晚上19:00!!!(点我)2.直播间抽奖:渲云直播好礼4:转发朋友圈,GET 渲云独家“3dmax模型包”向上拉获得本次直播宣传海报,转发至朋友圈(不少于2小时),并附上文字:【锁定6月29日19:00渲云&华为云直播,CEO亲临直播间为你讲解渲云渲染的100种姿势,海量100元渲染券、呆猫桌面云100元体验券、华为云精美好礼送送送!”直播地址:http://t.cn/A6VsA73Q】添加小云微信(huaweiyun1120),备注“直播”凭借转发截图,即可获得由渲云独家提供的“3dmax模型包”一份大量素材,绝对是你成为大神路上的好帮手!(扫我加入社群)本次活动所有奖励领取,必须加入社群并收看直播,否则无效!群内添加群主小云微信——还可获得渲云独家[3dmax]资料包一份!6月29日(周二)19:00,关于CG行业的所有秘密,都在直播间为你揭晓!>>>好礼直播传送门华为云码豆怎么用?会员中心入口:https://devcloud.huaweicloud.com/bonususer/home码豆奖励活动规则: 1)码豆可在码豆会员中心兑换实物礼品; 2)码豆奖励将于活动结束后的3个工作日内充值到账,请到会员中心的“查看明细”中查看到账情况; 3)码豆只能用于会员中心的礼品兑换,不得转让,具体规则请到会员中心阅读“码豆规则”; 4)为保证码豆成功发放,如果修改过账号名还请向工作人员提供修改前后的账号名。本期活动已结束,现将获奖用户公布如下:注:1、所有中奖用户在申诉时间结束前必须完成实名认证并确定华为云账号是否正确,否则码豆无法发放,视为放弃领奖。2、本次活动申诉时间:2021年7月6日16:00前1、最佳畅谈&直播间提问最佳畅谈直播提问楼层华为云账号码豆序号华为云账号码豆12#chenlingjun50008 lenghanzz500029#DouDou_50009hw21012767500036#canfen500010hw30141920500045#hw9454516150002、问答闯关&畅谈问答闯关论坛畅谈您的华为云ID是?码豆华为云账号码豆hw23597877500hw09974076300aahph1994500liuxifeng300hw02428460500a565855829300hwid_c7gbgw8wvl56bxs500zhengxx6688300无500hw_008617750345773_01300sunxiaobei500yizhangl300hw79284301500nukinsan300爹哥zzzdiego500hw46425134300zhienna500hw11326648300starry6500chenlingjun300violetevergarden123500hw86105405300亡魂陌路500hw55101971300zjj8008500kasson_300hw06979116500hw61299517300ljh752093278500hw46433192300xj120141121500hw26536804300hjh15003396976500hw78949957300HUAWEI_Yuan-Heng-Li-Zhen500lhw79459023-300liuyuanxiang500whw43454189_300hw17383096500meet00300sunshine_huawei500oznar119300shenhaodong500JaneConan300franco52576500DouDou_300hw36175659500hw63394802300awake_demon500xianuan300wuvita500hw36175659300hw68852324500canfen300xianuan500franco52576300skyfirecloud500hw75771608300Lisdon500hw73261881300cai_hy500hw94545161300wanggui001500qiushu2020300jockerxin500shenhaodong300hw96458811500sunshine_huawei300DouDou_500hw75435701300JaneConan500linghz666300oznar119500hw11078500hw10996515500hw61299517500linghz666500hw86105405500fuhaozhijia500whw43454189_500nukinsan500yizhangl500hw26536804500hw46433192500hw56223614500hw00922381500lhw79459023-500hw46425134500chenlingjun500suncker500hw79926743500hw_008617750345773_01500hw31711871500hw35544043500zhangpaodan500zhangyibaluobo500heiyouyoubaluobo500hw75771608500emptyBox500hw48382267500bigdata1500hw94545161500hw73261881500zhengxx6688500a565855829500qiushu2020500canfen500hw11326648500注:1、所有中奖用户在申诉时间结束前必须完成实名认证并确定华为云账号是否正确,否则码豆无法发放,视为放弃领奖。2、本次活动申诉时间:2021年7月6日16:00前3、请继续关注7月8日云市场直播最新一期直播《目睹一下,企业营销新模式》(点我),更多好礼,更多精彩!
  • 上手 Gatsby 入门教程
    Gatsby 项目结构建议使用 Starter 修改着理解 Gatsby,我用的是 Gatsby + Netlify CMS Starter。完整的 Gatsby 项目结构可以看文档,这里针对搭建博客用到的功能说明一下。    /src/pages 目录下的组件会被生成同名页面。    /src/templates 目录下放渲染数据的模板组件,如渲染 Markdown 文章,在其它博客系统中一般叫 layout。    /src/components 一般放其它共用的组件。    /static 放其它静态资源,会跳过 Webpack 直接复制过去。接下来是两个比较常用的配置文件,需要修改时参考 Starter 改即可。    /gatsby-config.js 基本用来配置两个东西:    siteMetadata 放一些全局信息,这些信息在每个页面都可以通过 GraphQL 获取到。    plugins 配置插件,这个按用到时按该插件文档说明弄即可。    /gatsby-node.js 可以调用 Gatsby node APIs干一些自动化的东西。一般有两个常用场景:        添加额外的配置,比如为 Markdown 文章生成自定义路径。        生成 /src/pages 以外的页面文件,如为每个 Markdown 文章生成页面文件。    此外还有两个不那么常用的配置文件。        /gatsby-browser.js 可以调用 Gatsby 浏览器 APIs,一般插件才会用到,如滚动到特定位置。        /gatsby-ssr.js 服务器渲染的配置,一般也是插件才用到。    这就是搭建 Gatsby 博客的基本结构了,可以看到非常简单,且因为其丰富的生态,其它底层接口基本不需要用到。但接下来还是会有一些小坑,第一个便是 GraphQL,我们将马上来分析。为什么用 GraphQL    在上一节介绍了选择 Gatsby 的原因,其中提到了 Gatsby 使用 GraphQL 。大家可能会有疑惑,不是建静态博客么,怎么会有 GraphQL?难道还要部署服务器?    其实这里 GraphQL 并不是作为服务器端部署,而是作为 Gatsby 在本地管理资源的一种方式。    通过 GraphQL 统一管理实际上非常方便,因为作为一个数据库查询语言,它有非常完备的查询语句,与 JSON 相似的描述结构,再结合 Relay 的 Connections 方式处理集合,管理资源不再需要自行引入其它项目,大大减轻了维护难度。带魔法的 GraphQL    这里也是 Gatsby 的第一个坑。在 Gatsby 中,根据 js 文件的位置不同,使用 GraphQL 有两种形式,且 Gatsby 对其做了魔法,在 src /pages 下的页面可以直接 export GraphQL 查询,在其它页面需要用 StaticQuery 组件或者 useStaticQuery hook。    这里面查询语句虽然写的是字符串,但其实这些查询语句不会出现在最终的代码中,Gatsby 会先对其抽取。    个人其实不太喜欢魔法,因为会增加初学者的理解难度。但不得不承认魔法确实很方便,就是用了魔法的项目应该在文档最显眼的地方说明一遍。快速上手 GraphQL    GraphQL 结构跟最终数据很相似,基本语法也非常简单,看看官方文档即可。一个快速上手的方式是访问项目开发时(默认 http://localhost:8000)的 /___graphql 页面,通过 GraphiQL 编辑器右侧可以浏览所有能够查询的资源。    另一个需要理解的是 Relay 的 Connections 概念,你会发现 Gatsby 里所有的数据集合都是以这种方式查询。推荐阅读 Apollo 团队分享的文章。    对 Connections 细致的理解往往是实现分页等底层需求时才需要,而这些均有插件完成。一般使用时只需要知道集合里每个项目的数据在 edges.node 中,同时通过 GraphiQL 浏览其它可以使用的数据。如对于 Markdown 文章,相应插件提供了字数统计以及阅读时长等数据,均可通过 GraphQL 直接获取。Debug GraphQL    Gatsby 魔法带来的另外一个坑是 GraphQL 报错信息不全,可能会默默被吞掉,也可能无法定位到最终文件。    我在修改 starter 时踩到一个坑是复制组件时忘了修改 static query 查询语句的名称,导致重名报错。    避免错误最好方式是在 GraphiQL 编辑器中写好运行无误再复制到组件中。Remark 插件坑    Gatsby 中处理 markdown 最常用也是默认的插件是 gatsby-transformer-remark。这个插件对 markdown 文件解析后会生成 MarkdownRemark GraphQL 节点,其中 front matters 数据也会被解析出来。同时 MarkdownRemark 的集合对应为 allMarkdownRemark connections。    对于 connections 节点我们一般可以用 sort 和 filter 来筛选处理数据(可在 GraphiQL 编辑器中浏览),这里有一个坑便是如果要处理 front matters 数据,它们必须存在所有查询的 markdown 文件上并且具有相同的类型,插件才会生成相应的 fields,否则可能会抛出异常或者更糟糕的,默默失败了。    避免方式同上,先在 GraphiQL 编辑器中运行一遍,看看筛选的结果是否正确。    另外一种处理方式是在 /gatsby-node.js 中通过 onCreateNode 钩子,在生成 markdown 相关节点时手工处理,确保节点存在。    这在实现草稿和上下篇的时候会用到,具体例子我会在后续章节中提到。为什么选择 Netlify CMS    搭建 Gatsby 博客其实不需要 CMS 都是可以的,编写 Markdown 然后 build 即可。但这么做还是略嫌不便,通过 CMS 一般可以在一个可视化的在线环境中编辑文章,然后一键即可发布。    Gatsby 主流的两个 CMS 是 Contentful 和 Netlify CMS。    对于 Contentful 来说,文章是放在 Contentful 的服务器上的,管理也是通过 Contentful 提供的工具。当然其质量还是不错的,喜欢的可以参照官方的教程搭建。    Netlify CMS 是跟项目一起发布的,默认是在 /admin 页面下。文章也是存在源项目中,就是原来默认的 Markdown 文件。Netlify CMS 借助 Oauth 把写好的 Markdown 文件推送到项目源码的仓库上,再配合 Netlify 检测仓库变动自动构建发布。当然后者也不是必须的,可以换其它方式自动构建。    Netlify CMS 的优点是开源免费,文章跟项目源码在一起,界面可以高度自定义,甚至可以自行扩充 React 组件,基本满足简单的博客编写需求。配置 Netlify CMS    如果用官方的 starter配置将会非常简单。此 starter 默认使用 Github 作为仓库,Netlify 作为自动构建服务器。配置 Widgets    默认的 /static/admin/config.yml 已经配置好了大部分,如果对文章 Markdown 添加了自定义的 front matters 则需要再做些细调。    Widgets 代表了在 CMS 中可输入的模块,官方为常见的类型都提供了默认的 widgets ,没有满足的也可以自定义。    如我的博客中每篇文章都有一个 quote 域放些引用文字,那么在配置中添加上 fields: - label: "Quote" name: "quote" widget: "object" fields: - {label: "Content", name: "content", widget: "text", default: "", required: false} - {label: "Author", name: "author", widget: "string", default: "", required: false} - {label: "Source", name: "source", widget: "string", default: "", required: false}    如此即可在 CMS 中填写相关信息。配置预览    CMS 中提供了文章预览界面,如果需要自定义只需修改 /src/cms/ 下相应的文件即可,就是简单的 React 组件。    以上便是 Netlify CMS 最常用的配置,只需简单的修改博客现在就能跑起来了。接下来我们会通过实现草稿模式和上下篇文章来深入理解 Gatsby 的机制。    迁移博客需要考虑的一个重要问题便是路径兼容。我们当然不希望迁移后原有的链接无法访问,这不仅影响到 SEO ,更带来了不好的用户访问体验。本文将聊聊怎么让 Gatsby 兼容 Jekyll 式路径。Gatsby 如何生成特定页面    一般来说,在 /src/pages/ 目录下的组件会自动生成相应路径的页面,但如果是其它类型的文件就不会了。我们可以通过 Gatsby 的 Node APIs 来生成特定页面。    在 /gatsby-node.js 中配置 Gatsby Node APIs,如果项目是基于 starter 的话你很可能会发现里面已经有相应的配置。    我们通过声明 exports.createPages 钩子来配置页面生成,在回调中通过调用 actions.createPage 来生成某个指定页面。这个方法接受一个配置参数,其中的 path 域代表了生成页面的路径。 exports.createPages = ({ actions, graphql }) => { actions.createPage({ path, // ... }) }指定博客页面    那么我们怎么知道该生成哪些页面呢?这里通过 exports.createPages 回调中的 graphql 来查询 Markdown 文件。 exports.createPages = ({ actions, graphql }) => { const { createPage } = actions return graphql(` { allMarkdownRemark(sort: { order: ASC, fields: [frontmatter___date] }) { edges { node { id fields { slug } frontmatter { path title layout } } } } } `).then(result => { if (result.errors) { result.errors.forEach(e => console.error(e.toString())) return Promise.reject(result.errors) } // ... }) }    Netlify CMS 会在 Markdown front matters 中的 path 域生成路径。根据默认的 /static/admin/config.yml 我们的路径应该是 /blog/{{year}}-{{month}}-{{day}}-{{slug}}/ ,这个可能跟旧博客不一样,如 Jekyll 是 /{{year}}/{{month}}/{{day}}/{{slug}}/ 。修改 Markdown 节点    在 Remark 插件生成的 Markdown 节点中,我们可以往 fields 域放一些自定义的变量。这里我们把自定义的路径存到 fields.slug 中。    通过 /gatsby-node.js 中的 exports.onCreateNode 钩子我们可以在生成节点的时候进行拦截处理。你可能发现文件里面已经有一些配置的代码了,我们这里只关注 Markdown 相关的。 exports.onCreateNode = ({ node, actions, getNode }) => { const { createNodeField } = actions if (node.internal.type === `MarkdownRemark`) { // Jeykll style post path const filepath = createFilePath({ node, getNode }) createNodeField({ node, name: 'slug', value: filepath.replace( /^\/blog\/([\d]{4})-([\d]{2})-([\d]{2})-/, '/$1/$2/$3/' ) }) } }    我们把原有的路径值换成了自定义值并存在了 fileds.slug 中。创建页面    回到我们前面的查询,得到需要的数据之后只需要对每个页面调用 actions.createPage 即可。  exports.createPages = ({ actions, graphql }) => { const { createPage } = actions return graphql(` { allMarkdownRemark(sort: { order: ASC, fields: [frontmatter___date] }) { edges { node { id fields { slug } frontmatter { path title layout } } } } } `).then(result => { if (result.errors) { result.errors.forEach(e => console.error(e.toString())) return Promise.reject(result.errors) } const { edges } = result.data.allMarkdownRemark const options = edges.map(edge => ({ path: edge.node.fields.slug, title: edge.node.frontmatter.title, component: path.resolve( `src/templates/${edge.node.frontmatter.layout}.js` ), // additional data can be passed via context context: { id: edge.node.id } })) options.forEach(option => createPage(option)) }) }     也许你会问为什么不在这里直接计算自定义路径而是要存到 fields.slug 中。这是因为这个路径我们可能还会在其它地方用到,存起来就不必多处计算了。    上面代码中可以注意到还有个 context 域,这个域中的数据会被传到 component 的 props 中。这样我们在模板组件中通过 pageContext.id 便可判断当前渲染的文件。    通过实现自定义路径基本上可以了解 Gatsby 页面生成的方式了。下节中我会继续谈谈其它个性化的配置,如草稿模式和显示上下篇博文。草稿模式    草稿模式即可以将文章保存为草稿而不被渲染出来。方式是在 front matters 中设置一个 draft 布尔域,以此域作为渲染参考。    坑    这里有一个地方需要注意,前面文章提过,Markdown 插件需要所有文章中都有 draft 域且都是布尔类型才会生成相应的 GraphQL 查询。如果是新的博客这个问题不大,如果是迁移过来的,有两个解决方式,第一个是手动写个脚本给文章都补上域,另一个是利用 Gatsby 的 Node APIs 在 fields 上生成特定域,鲁棒性更好些。自动生成域    观察 Remark 插件生成的 GraphQL 类型,我们可以发现,front matters 都被放在 frontmatter 域中,而与之同级的有一个前面文章提到过的 fields 域,用来放自定义生成的数据。    Gatsby 在生成 GraphQL 节点时提供了钩子 onCreateNode,我们利用这个钩子往 fields 中放自定义的数据。    编辑 /gatsby-node.js ,如果是用了 starter 的话这里很可能已经有其它的代码,已有的不需要动,添加我们需要的即可。 exports.onCreateNode = ({ node, actions, getNode }) => { const { createNodeField } = actions if (node.internal.type === `MarkdownRemark`) { createNodeField({ node, name: 'draft', value: Boolean(node.frontmatter.draft) }) } }    如此 fields 中就保证了会有 draft 这个域了。过滤草稿    有了标记之后,在生成页面的地方我们就需要过滤草稿。    首先是普通的文章页面生成,这个是在 createPages 钩子中,如果你的博客只有文章用到 Markdown 的话,可以在 GraphQL 查询中直接过滤,否则我们用前面文章的方法,先取所有 Markdown 文件再根据渲染的模板来分别处理各种类型的文章。    注意我把模板域的名字换成了自己更习惯的 layout,原来的 starter 中应该叫 templateKey。修改其实也很简单,搜索所有文件替换关键字即可。 options .filter( (_, i) => !( edges[i].node.frontmatter.layout === 'blog-post' && edges[i].node.fields.draft ) ) .forEach(option => createPage(option))    我在主页中也列举了最近的几篇文章,这里也需要过滤草稿,可以直接在 GraphQL 中过滤。 query IndexQuery { latestPosts: allMarkdownRemark( sort: { order: DESC, fields: [frontmatter___date] } filter: { fields: { draft: { ne: true } } frontmatter: { layout: { eq: "blog-post" } } } limit: 5 ) { edges { node { excerpt(pruneLength: 200) id fields { slug } frontmatter { title description layout date(formatString: "MMMM DD, YYYY") } } } } }    其它地方同理。上下篇    在文章页面中我们通常会加入上下篇来引导继续浏览。这里我们同样在 createPages 钩子中处理,但这回我们添加到 context 域中,这个域里的数据会作为 props 传到模板组件中。    在 createPage 生成文章页面前添加处理代码计算上下篇: options .filter( (_, i) => edges[i].node.frontmatter.layout === 'blog-post' && !edges[i].node.fields.draft ) .forEach((option, i, blogPostOptions) => { option.context.prev = i === 0 ? null : { title: blogPostOptions[i - 1].title, path: blogPostOptions[i - 1].path } option.context.next = i === blogPostOptions.length - 1 ? null : { title: blogPostOptions[i + 1].title, path: blogPostOptions[i + 1].path } })    然后在文章的 /src/templates/blog-post.js 组件里,接收 pageContext props,就可以使用上面传入的数据了。这是我的例子。    通过实现这几个功能我们了解了 Gatsby 页面生成的方式以及其 Node APIs 的基本使用。Gatsby 的功能远不止这些,官方文档写得非常详细,需要实现其它功能建议先去看看有无现有的例子。本系列到这里暂告一段落,谢谢你的阅读,希望能对你搭建 Gatsby 博客有所帮助。
  • [技术干货] 差点以为是本人!这个3D**生成模型厉害了,还能自己改POSE
    丰色 发自 凹非寺量子位 报道 | 公众号 QbitAI很多3D**模型都很强大,但总是难免“裸奔”。像要创造出真正的人类“化身”模型,衣服和头发不可或缺。但这些元素的精确3D数据非常稀少,还很难获得。来自三星AI中心(莫斯科)等团队的技术人员一直致力于此方面的研究,最终他们开发出这样一个模型:生成的3D人,穿着原本的衣服、发量发型也都毫无保留地呈现。乍一看,“跟真人似的”。更棒的是,无需模特示范,模型还可以“举一反三”,摆出各种POSE!效果是这样子的:该模型被命名为StylePeople。来看看具体怎么搞的吧!神经装扮模型(The neural dressing model)其实,不止是“裸奔”,很多三维**模型还很“死板”:模特摆什么姿势模型就跟着摆什么。就像此前,利用隐函数来生成的三维**模型能够高度还原模特的着装和发型了,但是人物姿势依然不够变通,只能从原模特的几个特定视角生成。ps.也是该团队的研究成果所以在为3D**模型还原衣服颜色、褶皱和发型的同时,也要保证人物的姿势可以“举一反三”。为此研究人员采用将多边形实体网格建模与神经纹理相结合的方法。多边形网格负责控制和建模粗糙的**几何姿势,而神经渲染负责添加衣服和头发。首先他们设计了一个神经装扮模型(The neural dressing model),该模型结合了可变形网格建模与神经渲染,如下图所示。最左列表示被可视化的前三个PCA组件。第2、3列为在用SMPL-X建模的**网格上叠加“人型化身”的纹理(texture)。第4、5列为使用渲染网络光栅化渲染出的结果。可以优雅地处理出宽松的衣服和长头发以及复杂的穿衣结构!接下来,基于上面的神经装扮模型,研究人员造出能生成“Fullbody”的3D**模型。最终的生成架构是StyleGANv2和神经装扮的结合。StyleGAN部分使用反向传播算法生成神经纹理,然后将其叠加在SMPL-X网格上,并使用神经渲染器进行渲染。在对抗性学习中,判别器将每一对图像看为同一个人。提高了视频和少量图像生成3D人类模型的技术水平在对神经装扮这一方法的效果验证中,研究人员首先评估了基于视频素材的3D模型生成结果。效果如文章开头所展示的图像,左边是示例源帧,其余的图像是左边视频人物的“化身”。在简单的增强现实程序做的背景下,呈现出了模特先前并没有摆过的各种姿势。接下来,对基于小样本图像素材的神经装扮效果进行评估。研究人员使用仅两个人的People Snapshot数据集将他们的神经装扮方法与其他各种方法(如360Degree等,见表)进行比较。衡量生成的模型质量的指标包括LPIPS(感知相似度)、SSIM(结构相似性)、FID(真实样本与生成样本在特征空间之间的距离)、和IS(清晰度与多样性得分)。结果显示了他们的方法在所有指标上都占有优势,除了IS以外,但影响不大,因为它与视觉质量的相关性最小。最后,该团队表示,他们这个模型的生成效果(如下图)仍然受到目前样本数据规模和质量的限制,今后工作重点是提高该模型的数据利用率。有兴趣的同学可以持续关注该团队的研究进展。参考链接:[1]https://arxiv.org/abs/2104.08363—完—@量子位 · 追踪AI技术和产品新动态深有感触的朋友,欢迎赞同、关注、分享三连վ'ᴗ' ի ❤——转自知乎/量子位
  • [问题求助] 【ABC产品】【路由功能】利用平台上的路由导航和路由视图,嵌套实现页面跳转,一级路由可正常跳转,二级路由页面不渲染
  • [其他] 学习笔记 - 通过可微分神经渲染进行目标检测的数据增广
    在缺少注释数据的情况下,训练鲁棒的目标检测器具有挑战性。解决该问题的现有方法包括:半监督学习,用于从未标记数据中插入标记数据;自监督学习,其通过pretext任务利用未标记数据中的信号。在不更改监督学习范式的情况下,我们引入了一种用于目标检测的离线数据增强方法,该方法在语义上以新颖的观点对训练数据进行插值。具体而言,我们提出的系统基于可微分的神经渲染以及不涉及人工干预的相应边界框注释生成训练图像的可控视图。首先,我们在估计深度图的同时将像素对齐的图像特征提取并投影到点云中。然后,我们使用目标相机姿势重新投影它们,并渲染一个新颖的2D图像。在点云中标记关键点形式的对象,以在新视图中恢复注释。它与仿射变换,图像混合等在线数据增强方法完全兼容。广泛的实验表明,作为一种免费的丰富图像和标签的工具,我们的方法可以显著提高目标检测系统的性能,而这种资源很少训练数据。论文:https://arxiv.org/abs/2103.02852
  • [技术干货] UE4 Python批量渲染Sequence
    """说明:使用UE4 Python批量渲染Sequence,输出格式为.avi作者:钟小二宝E-mail:1023462838@qq.com时间:2021年3月1日"""import unrealimport jsonimport osimport subprocessdef render_sequence_to_movie(i,output_directory,output_format): # instance of unreal classessystem_lib = unreal.SystemLibrary()editor_util = unreal.EditorUtilityLibrary()capture_settings = unreal.AutomatedLevelSequenceCapture()burn_in_options = unreal.LevelSequenceBurnInOptions()# 获取选择的所有assetsselected_assets = editor_util.get_selected_assets()sequence_list = [asset for asset in selected_assets if isinstance(asset,unreal.LevelSequence)]# 如果超出数组则退出执行if i >= len(sequence_list):# 输出完成 打开输出文件夹的路径subprocess.call(["start","",output_directory], creationflags=0x08000000,shell=True)return# 获取当前渲染序号下的 LevelSequencesequence = sequence_list[i]set_capture_settings(sequence, i,output_directory,output_format,system_lib,capture_settings)set_burn_in_options(output_directory,output_format,burn_in_options)capture_settings.burn_in_options = burn_in_optionsglobal on_finished_callbackon_finished_callback = unreal.OnRenderMovieStopped(lambda s:render_sequence_to_movie(i+1,output_directory,output_format))unreal.SequencerTools.render_movie(capture_settings, on_finished_callback)def set_capture_settings(sequence,i,output_directory,output_format,system_lib,capture_settings):# 在UMovieSceneCapture上设置所有POD设置capture_settings.settings.output_directory = unreal.DirectoryPath(output_directory)capture_settings.settings.game_mode_override = None# 输出视频的文件名(我使用了LevelSequence的名字)capture_settings.settings.output_format = "{}".format(system_lib.get_object_name(sequence))capture_settings.settings.overwrite_existing = Truecapture_settings.settings.use_relative_frame_numbers = Falsecapture_settings.settings.handle_frames = 0capture_settings.settings.zero_pad_frame_numbers = 4# 渲染帧率设置,默认使用帧率为渲染序列中的帧率# capture_settings.settings.use_custom_frame_rate = True# capture_settings.settings.custom_frame_rate = unreal.FrameRate(24,1)# 渲染视频输出分辨率设置capture_settings.settings.resolution.res_x = 1280capture_settings.settings.resolution.res_y = 720capture_settings.settings.enable_texture_streaming = Falsecapture_settings.settings.cinematic_engine_scalability = Truecapture_settings.settings.cinematic_mode = Truecapture_settings.settings.allow_movement = False # Requires cinematic_mode = Truecapture_settings.settings.allow_turning = False # Requires cinematic_mode = Truecapture_settings.settings.show_player = False # Requires cinematic_mode = Truecapture_settings.settings.show_hud = False # Requires cinematic_mode = Truecapture_settings.use_separate_process = Falsecapture_settings.close_editor_when_capture_starts = False # Requires use_separate_process = Truecapture_settings.additional_command_line_arguments = "-NOSCREENMESSAGES" # Requires use_separate_process = Truecapture_settings.inherited_command_line_arguments = "" # Requires use_separate_process = True# 在UAutomatedLevelSequenceCapture上设置所有POD设置capture_settings.use_custom_start_frame = False # If False, the system will automatically calculate the start based on sequence contentcapture_settings.use_custom_end_frame = False # If False, the system will automatically calculate the end based on sequence contentcapture_settings.custom_start_frame = unreal.FrameNumber(0) # Requires use_custom_start_frame = Truecapture_settings.custom_end_frame = unreal.FrameNumber(0) # Requires use_custom_end_frame = Truecapture_settings.warm_up_frame_count = 0.0capture_settings.delay_before_warm_up = 0capture_settings.delay_before_shot_warm_up = 0.0capture_settings.write_edit_decision_list = True# 选择需要渲染的LevelSequencecapture_settings.level_sequence_asset = unreal.SoftObjectPath(sequence.get_path_name())capture_settings.set_image_capture_protocol_type(unreal.load_class(None, "/Script/MovieSceneCapture.VideoCaptureProtocol"))def set_burn_in_options(output_directory,output_format,burn_in_options):# 是否启用视频刻录burn_in_options.use_burn_in = Falseburn_in_options.set_burn_in(unreal.SoftClassPath("/Engine/Sequencer/DefaultBurnIn.DefaultBurnIn_C"))burn_in_options.settings.set_editor_property('TopLeftText', "{FocalLength}mm,{Aperture},{FocusDistance}")burn_in_options.settings.set_editor_property('TopCenterText', "{MasterName} - {Date} - {EngineVersion}")burn_in_options.settings.set_editor_property('TopRightText', "{TranslationX} {TranslationY} {TranslationZ}, {RotationX} {RotationY} {RotationZ}")burn_in_options.settings.set_editor_property('BottomLeftText', "{ShotName}")burn_in_options.settings.set_editor_property('BottomCenterText', "{hh}:{mm}:{ss}:{ff} ({MasterFrame})")burn_in_options.settings.set_editor_property('BottomRightText', "{ShotFrame}")# 加载水印地址burn_in_options.settings.set_editor_property('Watermark', unreal.load_asset("/Engine/EngineResources/AICON-Green"))# 创建一个FLinearColor着色水印burn_in_options.settings.set_editor_property('WatermarkTint', unreal.LinearColor(1.0, 0.5, 0.5, 0.5))def batch_render_sequence_to_movie():# 渲染输出文件夹output_directory="C:/render"output_format="{sequence}"render_sequence_to_movie(0,output_directory,output_format)def main():batch_render_sequence_to_movie()if __name__ == "__main__":main()
  • [技术干货] 在响应应用中实施有条件渲染的 7 种方法
    介绍您可以构建动态且与响应高度交互的单页应用程序 (SPA)。允许这样做的一个功能是有条件渲染。有条件渲染是一个术语,用来描述在条件为真或假时呈现不同用户界面 (UI) 标记的能力。在"响应"中,它允许我们根据条件呈现不同的元素或组件。此概念通常应用于以下情景:从 API 渲染外部数据。显示或隐藏元素。令人窒求的应用程序功能。实施权限级别。处理身份验证和授权。在本文中,您将研究在响应应用中实施有条件渲染的七种方法。先决条件要完成此教程,您需要:了解 JavaScript 变量和功能。您可以查阅JavaScript 系列中的"如何编写代码",以了解更多。了解进口、导出和渲染响应组件。您可以查看我们的"如何在响应中编码.js系列。节点.js本地安装的,您可以通过以下方式安装节点.js并创建本地开发环境。此教程已通过节点 v15.6.0、npm v7.4.0 和 v17.0.1 进行验证。react设置示例项目考虑需要用户登录的应用程序。如果用户注销,它将显示登录按钮。如果用户已登录,它将显示一个注销按钮。从使用创建反应应用程序生成响应应用开始:npx create-react-app react-conditional-rendering-example更改为新的项目目录:cd react-conditional-rendering-example接下来,打开代码编辑器中的文件。并将内容替换为以下代码行:App.jssrc/应用程序.jsimport React, { Component } from "react"; import './App.css'; class App extends Component {   constructor(props) {     super(props);     this.state = {       isLoggedIn: true     };   }   render() {     return (       <div className="App">         <h1>           This is a Demo showing several ways to implement Conditional Rendering in React.         </h1>         <button>Login</button>         <button>Logout</button>       </div>     );   } } export default App;接下来,打开代码编辑器中的文件。并将内容替换为以下代码行:App.csssrc/应用程序.cssbody {   padding: 1em; } h1 {   font-size: 3em;   font-weight: 500;   text-align: center;   margin-bottom: 1em;   margin-right: 1em;   padding: 0; } button {   appearance: none;   background-color: #246bec;   border: 1px solid #246bec;   border-radius: 0;   box-sizing: border-box;   color: #ffffff;   display: block;   font-size: 2em;   font-weight: 500;   margin-bottom: 1em;   margin-top: 1em;   padding: .5em;   width: 100%;   transition: border-color, background-color 300ms ease-in-out; } button:focus {   border-color: #00006D; } button:hover {   background-color: #0B52D3; } button:active {   background-color: #00006D; }然后,从终端窗口运行应用程序:npm start并与浏览器中的应用程序进行交互:每个有条件的渲染方法都将基于此代码进行构建。您可能希望在此时创建一个回滚更改,以完成此教程。git commit此时,您将有一个响应应用程序,显示登录和注销按钮。您的目标是仅显示其中一个按钮。让我们来看看有条件的渲染方法来实现这一目标。1. 使用声明if…else当条件得到满足时,声明将执行块中包含的操作。否则,它将执行块中包含的操作。if…elseifelse在 JSX 中,您可以使用带有标记的 JavaScript 代码在应用程序中呈现动态值。JSX 使用卷曲括号 (和 ) 表示在渲染之前需要解释的表达式。然而,需要注意的是,在这种括号内可以做些什么是有限度的。{}让我们考虑一下,如果您尝试在该方法中使用语句:if…elserender()警告:这是一个无法正常工作的代码示例。它作为方法中解释的局限性的示例提出。render()// ... class App extends Component {   // ...   render() {    let {isLoggedIn} = this.state;     return (       <div className="App">         <h1>           This is a Demo showing several ways to implement Conditional Rendering in React.         </h1>        {           if(isLoggedIn){             return <button>Logout</button>           } else{             return <button>Login</button>           }         }       </div>     );   } } // ...此代码将产生错误。逻辑需要移出方法。Unexpected tokenrender()打开代码编辑器中的文件,向下滚动到该方法,并进行以下突出显示的代码更改:App.jsrender()src/应用程序.js// ... class App extends Component {   // ...   render() {    let {isLoggedIn} = this.state;     const renderAuthButton = () => {       if (isLoggedIn) {         return <button>Logout</button>;       } else {         return <button>Login</button>;       }     }     return (       <div className="App">         <h1>           This is a Demo showing several ways to implement Conditional Rendering in React.         </h1>        {renderAuthButton()}       </div>     );   } } // ...这是创建提取函数的过程。此代码将逻辑从 JSX 提取到函数中。该函数在 JSX 卷曲括号内执行。renderAuthButton在 Web 浏览器中打开您的应用程序。以前,已显示登录和注销按钮。现在,状态和条件逻辑只显示注销按钮。isLoggedIntrue现在,让我们考虑一下,如果您尝试在方法中使用多个s:returnrender()警告:这是应避免执行不良代码的一个例子。// ... class App extends Component {   // ...   render() {    let {isLoggedIn} = this.state;     if (isLoggedIn) {       return (         <div className="App">           <h1>             This is a Demo showing several ways to implement Conditional Rendering in React.           </h1>          <button>Logout</button>         </div>           );    } else {       return (         <div className="App">           <h1>             This is a Demo showing several ways to implement Conditional Rendering in React.           </h1>          <button>Login</button>         </div>           );    }   } } // ...上面的片段将实现相同的结果,但不必要地膨胀组件,同时由于不断重新渲染不变的组件而引入性能问题。最佳做法是保持组件尽可能简单,以避免浪费兄弟姐妹或父组件的重渲染。在代码编辑器中,创建一个新文件:AuthButton.js斯尔克/奥特布顿.jsimport React from "react"; const AuthButton = props => {   let { isLoggedIn } = props;   if (isLoggedIn) {     return <button>Logout</button>;   } else {     return <button>Login</button>;   } }; export default AuthButton;AuthButton返回各种元素或组件,具体取决于通过道具传递的状态值。isLoggedIn接下来,重新访问并修改它以使用新组件:App.jssrc/应用程序.jsimport React, { Component } from "react"; import './App.css';import AuthButton from "./AuthButton";class App extends Component {   constructor(props) {     super(props);     this.state = {       isLoggedIn: true     };   }   render() {     return (       <div className="App">         <h1>           This is a Demo showing several ways to implement Conditional Rendering in React.         </h1>        <AuthButton isLoggedIn={isLoggedIn} />       </div>     );   } } export default App;这是创建提取功能组件的过程。此代码产生与方法相同的结果。但具有将更改移动到单独组件的优点。renderAuthButton()2. 使用声明switch如前所示,您可以使用语句有条件地返回基于设置条件的组件的不同标记。通过一个语句也可以实现这一点,您可以指定各种条件的加价。if…elseswitch重述组件,并将语句替换为声明:AuthButtonif…elseswitch斯尔克/奥特布顿.jsimport React from "react"; const AuthButton = props => {   let { isLoggedIn } = props;  switch (isLoggedIn) {     case true:       return <button>Logout</button>;      break;     case false:       return <button>Login</button>;      break;     default:       return null;   }}; export default AuthButton;请注意,此代码如何根据 。isLoggedIn注意:当存在两个以上可能的值或结果时,应用语句方法将更为实用。switch此外,从组件返回会导致它隐藏自己(不显示任何内容)。这是切换组件可见性的好方法。null3. 使用元素变量元素变量类似于将有条件渲染提取为函数的方法。元素变量是包含 JSX 元素的变量。您可以有条件地将元素或组件分配到 JSX 以外的这些变量中,并且只能在 JSX 内呈现变量。申请可以这样重写:src/应用程序.jsimport React, { Component } from "react"; import './App.css'; class App extends Component {   constructor(props) {     super(props);     this.state = {       isLoggedIn: true     };   }   render() {    let { isLoggedIn } = this.state;     let AuthButton;     if (isLoggedIn) {       AuthButton = <button>Logout</button>;     } else {       AuthButton = <button>Login</button>;     }     return (       <div className="App">         <h1>           This is a Demo showing several ways to implement Conditional Rendering in React.         </h1>        {AuthButton}       </div>     );   } } export default App;请注意此代码如何有条件地将值 ( 组件 - 分配给 JSX,然后可以在 JSX 中稍后引用。AuthButton4. 使用三元操作员有条件的(三元)操作员是唯一需要三个操作的 JavaScript 操作员。此操作员经常用作语句的快捷方式。if申请可以这样重写:src/应用程序.jsimport React, { Component } from "react"; import './App.css'; class App extends Component {   constructor(props) {     super(props);     this.state = {       isLoggedIn: true     };   }   render() {    let { isLoggedIn } = this.state;     return (       <div className="App">         <h1>           This is a Demo showing several ways to implement Conditional Rendering in React.         </h1>        {isLoggedIn ? <button>Logout</button> : <button>Login</button>}       </div>     );   } } export default App;如果此方法使组件膨胀、笨重或可读性较低,则可以将条件封装在功能组件中:斯尔克/奥特布顿.jsimport React from "react"; const AuthButton = props => {   let { isLoggedIn } = props;  return isLoggedIn ? <button>Logout</button> : <button>Login</button>;}; export default AuthButton;三元法对于简单的评估是有用的。对于复杂的比较和组件,它可能会影响可读性,因为一个项目的增长。if…else5. 使用逻辑(短路评估)&&短路评价是一种技术,用于确保在评估一个表达的操作过程中没有副作用。逻辑帮助您指定只应在一个条件下执行操作,否则,它将被完全忽略。&&申请可以这样重写:src/应用程序.jsimport React, { Component } from "react"; import './App.css'; class App extends Component {   constructor(props) {     super(props);     this.state = {       isLoggedIn: true     };   }   render() {    let { isLoggedIn } = this.state;     return (       <div className="App">         <h1>           This is a Demo showing several ways to implement Conditional Rendering in React.         </h1>        {isLoggedIn && <button>Logout</button>}       </div>     );   } } export default App;如果是,此代码将显示"注销"按钮,否则它将不显示任何内容。isLoggedIntrue现在,让我们考虑为登录按钮使用第二个短路评估:警告:这是应避免执行不良代码的一个例子。{isLoggedIn && <button>Logout</button>} {!isLoggedIn && <button>Login</button>}此代码将根据 。但是,不建议这样做,因为有更好、更清洁的方法来达到相同的效果。随着应用的增长,这种短路评估的过度使用可能会变得繁琐和不直观。isLoggedIn6. 使用立即调用的功能表达式 (IIFEs)前面的部分提到,JSX的限制使它无法执行每种类型的JavaScript代码。可以立即调用函数表达式 (IFFEs) 绕过这些限制。IFFEs 是一个 JavaScript 功能,一旦定义,它就会运行:(function () {   // statements })();使用此技术,您可以直接在 JSX 内编写有条件的逻辑,但包裹在匿名函数中,该函数在评估该代码时立即被调用。申请可以这样重写:src/应用程序.jsimport React, { Component } from "react"; import './App.css'; class App extends Component {   constructor(props) {     super(props);     this.state = {       isLoggedIn: true     };   }   render() {    let { isLoggedIn } = this.state;     return (       <div className="App">         <h1>           This is a Demo showing several ways to implement Conditional Rendering in React.         </h1>        {(function() {           if (isLoggedIn) {             return <button>Logout</button>;           } else {             return <button>Login</button>;           }         })()}       </div>     );   } } export default App;这也可以使用箭头函数以稍微简洁的方式书写:{(() => {   if (isLoggedIn) {     return <button>Logout</button>;   } else {     return <button>Login</button>;   } })()}您可以从 MDN 中了解更多关于IIFE 的了解。7. 使用增强型 JSX 库某些库可扩展 JSX 的功能,从而能够直接通过 JSX 实现有条件渲染。其中一个图书馆是JSX控制声明。它是一种Babel插件,在转译过程中将类似组件的控制语句转换为 JavaScript 对应件。在安装包并修改您的 Babel 配置后,应用程序可以这样重写:babel-plugin-jsx-control-statementssrc/应用程序.jsimport React, { Component } from "react"; import './App.css'; class App extends Component {   constructor(props) {     super(props);     this.state = {       isLoggedIn: true     };   }   render() {    let { isLoggedIn } = this.state;     return (       <div className="App">         <h1>           This is a Demo showing several ways to implement Conditional Rendering in React.         </h1>        <Choose>           <When condition={isLoggedIn}>              <button>Logout</button>;           </When>           <When condition={!isLoggedIn}>              <button>Login</button>;           </When>         </Choose>       </div>     );   } } export default App;但是,不建议使用此方法,因为您编写的代码最终被转换为常规 JavaScript 条件。写 JavaScript 可能总是比在如此琐碎的事情上添加额外的依赖要好。选择有条件渲染方法一般来说,最好确保在实施有条件渲染时:不要为了防止组件不必要地卸载和重新安装而任意更改组件的位置。仅更改与有条件渲染有关的标记。不要在方法内不必要地膨胀组件,因为这会导致组件延迟渲染。render要考虑的事情包括:有条件地渲染的加价大小可能的结果数量这将是更直观和可读的一般来说,请记住以下建议:对于只有一个预期结果的情况,"短路评估"可能最适用。对于有两个预期结果的情况,可能最适用语句、元素变量、三元操作员或"立即调用的函数表达"。if…else对于有两个以上结果的情况,陈述、提取函数或提取功能组件可能最适用。switch请记住,这些是建议,而不是规则。项目的需求和惯例可能要求您采用不遵循这些建议的方法。结论在本文中,您检查了在响应应用程序中实施有条件渲染的七种方法。每种方法都有其自身的优势,使用方式的选择主要取决于使用案例。
  • [技术干货] 在PHP中处理字符串
    介绍字符串是一个或多个字符的序列,可能由字母、数字或符号组成。所有书面通信均由字符串组成。因此,它们对任何编程语言都是至关重要的。在本文中,您将学习如何创建和查看字符串的输出、如何使用逃生序列、如何串联字符串、如何将字符串存储在变量中,以及 PHP 字符串中使用引号、撇号和新线的规则。单字符串和双报价字符串您可以通过在单个或双引号中包围字符序列来创建 PHP 中的字符串。PHP 实际上会以不同的方式解释以下字符串:'This is a string in single quotes.'"This is a string in double quotes."在输出之前,双报价字符串将评估和解析字符串中的任何变量或逃生序列。单引用的字符串将按照指定的准确输出每个字符。单引用字符串的例外是单个报价(在需要时反冲)。如果您在PHP中处于此字符串:echo'Sammy says: "This string\'s in single quotes." It required a backslash (\) before the apostrophes (\\\'), but do not use (\") with the double quotes.'它将返回此输出:OutputSammy says: "This string's in single quotes." It required a backslash (\) before the apostrophes (\'), but do not use (\") with the double quotes.如果您在单引用字符串中的撇号之前没有包含反冲,PHP 将在那一点上结束字符串,这将导致错误。由于您使用单个引号创建我们的字符串,因此您可以在其中包含双引号,以成为 PHP 输出的最后字符串的一部分。如果你想渲染序列,你必须使用三个反冲()。首先渲染反冲本身,然后渲染撇号。序列完全按照指定进行渲染。\'\\\'\\\'\""Sammy says: \"This string's in double quotes.\" It requires a backslash (\) before the double quotes (\\\"), but you MUST NOT add a backslash before the apostrophe (\')."OutputSammy says: "This string's in double quotes." It requires a backslash (\) before the double quotes (\"), but you MUST NOT add a backslash before the apostrophe (\').与单引用字符串一样,如果在双引号字符串中的双引号之前不包括反冲,PHP 将在该点结束字符串,这将导致错误。由于双报价字符串不会以单个报价结束,因此您直接将撇号添加到双报价字符串中。双引号字符串将输出与撇号一起使用的单反冲或双反冲。\'要输出序列,您必须使用三个反冲。首先渲染反冲本身,然后渲染双引号。序列完全按照指定进行渲染。\"\\\"\'被称为逃生字符。结合次要字符,它构成了一个逃生序列。现在,您已经了解了字符串,让我们回顾一下逃生序列。\逃生序列逃生序列告诉程序停止正常操作程序,并以不同的方式评估以下字符。在PHP中,逃生序列以反冲开始。逃生序列适用于双报价字符串。单引用字符串仅使用单个报价或反冲的逃生序列。\以下是双引字符串的一些常见逃生序列:\"双报价\\反冲\$渲染美元符号而不是扩展变量\n一条新线\t用于选项卡以下是如何在字符串中使用这些序列的示例:"\"What type of \$ do sharks use?\"\n\tSand dollars!"Output"What type of $ do sharks use?"     Sand dollars!使用逃生序列使我们能够构建所需的任何字符串,同时包括这些特殊字符。创建和查看字符串输出双报价字符串最重要的特征是可变名称将扩展,从而为您提供变量值。您可以使用变量来代表字符串或直接使用字符串。您通过调用函数输出字符串:echo$my_name = "Sammy";echo 'Name is specified using the variable $my_name.';echo "\n"; // escape sequence for newline characterecho "Hello, my name is $my_name. It's stored in the variable \$my_name.";变量在第一行上创建。在第二行中,该函数用于在单个报价中输出字符串。使用此单引用字符串中的变量显示字符的编写方式,因此我们将看到可变名称而不是其值。$my_nameecho$my_name在第四行,我们再次使用该功能,但这次我们使用双报价。这一次,变量被扩展,以显示第一句中的值。在下一句中,有一个之前明确告诉字符串显示一个字符,而不是扩展变量。echo\$$OutputName is specified using the variable $my_name. Hello, my name is Sammy. It's stored in the variable $my_name.注意:当字符串评估不是问题时,您可以选择使用单个报价或双引号,但无论您决定使用哪一个报价,您都应该在程序内保持一致。单个报价可能会稍微快一些。了解如何创建和查看字符串的输出,让我们继续查看如何操作字符串。字符串串联连接意味着将字符串端到端连接在一起以构建新字符串。在 PHP 中,有两种主要方法来串联字符串。首先是在双报价字符串中包含字符串变量。这在前一步和以下步骤中显示:$answer = "Chews wisely."; echo "What do sharks do when they have a big choice to make? $answer";运行此代码将结合字符串和变量,该变量设置为:$answerChews wisely.OutputWhat do sharks do when they have a big choice to make? Chews wisely.串联字符串的第二种方法是使用操作员。.让我们通过一个语句将字符串和串联组合在一起:"Sammy""Shark"echoecho "Sammy" . "Shark";此代码使用操作员将字符串和字符串组合在一起,中间没有空格。."Sammy""Shark"OutputSammyShark如果您希望两个字符串之间的白空间,则必须在字符串中包括白空间,例如单词之后:Sammyecho "Sammy " . "Shark";OutputSammy Shark您不能使用串联将字符串与整数组合在一起:echo "Sammy" . 27;这将产生一个错误:OutputParse error: syntax error, unexpected '.27' (T_DNUMBER), expecting ';' or ',' in php shell code on line 1如果你把报价,它会评估为一个字符串。"27"PHP 是一种键入松散的语言,这意味着它将尝试转换根据请求提供的数据。如果您将变量设置为,当使用字符串串串时,PHP 将将变量解析为字符串:27$my_int = 27; echo "Sammy" . $my_int;OutputSammy27您涵盖了串联或组合字符串的两种主要方式。有时,您可能需要完全替换或添加到字符串。接下来,让我们来探索 PHP 如何允许您覆盖或更新字符串。更新字符串PHP 中的正常变量是可变的,这意味着它们可以更改或覆盖。让我们来探索当您更改变量值时会发生什么:$my_name$my_name = "Sammy"; echo $my_name . "\n"; $my_name = "Shark"; echo $my_name;OutputSammy Shark首先,该变量被设置为并显示使用。然后,它被设置为,覆盖变量,以便当被称为第二次,它显示了新的价值。"Sammy"echo"Shark"echo"Shark"您可以使用串联分配操作员来附加到字符串的末尾,而不是覆盖变量:.=$my_name = "Sammy"; $my_name .= " Shark"; echo $my_name;首先,将变量设置为,然后使用操作员添加到其末尾。新的价值是。$my_name"Sammy".=" Shark"$my_nameSammy SharkOutputSammy Shark要预先使用字符串的开头,您将在使用原始字符串时覆盖:$my_name = "Shark"; $my_name = "Sammy " . $my_name; echo $my_name;这一次,您首先将变量设置为,然后使用操作员用新字符串覆盖变量,与变量的前值相结合,在被覆盖之前。最终值是。$my_name"Shark"=$my_name"Sammy "$my_name"Shark"$my_nameSammy SharkOutputSammy Shark覆盖、附加和预支出使我们能够进行更改并构建应用程序所需的字符串。字符串中的白空间因为PHP不关心空白,你可以把尽可能多的空间或断线在你的报价,你想。echo "Sammy The           (silly) Shark";TEXT OutputSammy The           (silly) Shark请记住,HTML 以不同的方式呈现白空间。新行需要标记,因此即使您的源可能有新行,您也不会在网页上看到这些新行。同样,无论您的代码中有多少空间,字符之间只显示一个空格。<br>HTML OutputSammy The (silly) Shark清洁和一致地使用白空间是使代码更具可读性的最佳工具之一。由于PHP基本上忽略了空白,你有很大的灵活性,你可以利用你的优势。集成开发环境(IDE) 可以帮助您与代码保持一致,并使用白空间。结论能够控制字符串的呈现方式对于与应用程序的最终用户进行通信至关重要。通过更新和组合包含特殊字符的变量,您可以清楚地进行通信,同时将重复保持在最低限度。当您继续处理字符串时,请记住以下三个方面:特别注意字符串中的报价。使用串联组合您的字符串。使用变量使字符串可重复使用。介绍字符串是一个或多个字符的序列,可能由字母、数字或符号组成。所有书面通信均由字符串组成。因此,它们对任何编程语言都是至关重要的。在本文中,您将学习如何创建和查看字符串的输出、如何使用逃生序列、如何串联字符串、如何将字符串存储在变量中,以及 PHP 字符串中使用引号、撇号和新线的规则。单字符串和双报价字符串您可以通过在单个或双引号中包围字符序列来创建 PHP 中的字符串。PHP 实际上会以不同的方式解释以下字符串:'This is a string in single quotes.'"This is a string in double quotes."在输出之前,双报价字符串将评估和解析字符串中的任何变量或逃生序列。单引用的字符串将按照指定的准确输出每个字符。单引用字符串的例外是单个报价(在需要时反冲)。如果您在PHP中处于此字符串:echo'Sammy says: "This string\'s in single quotes." It required a backslash (\) before the apostrophes (\\\'), but do not use (\") with the double quotes.'它将返回此输出:OutputSammy says: "This string's in single quotes." It required a backslash (\) before the apostrophes (\'), but do not use (\") with the double quotes.如果您在单引用字符串中的撇号之前没有包含反冲,PHP 将在那一点上结束字符串,这将导致错误。由于您使用单个引号创建我们的字符串,因此您可以在其中包含双引号,以成为 PHP 输出的最后字符串的一部分。如果你想渲染序列,你必须使用三个反冲()。首先渲染反冲本身,然后渲染撇号。序列完全按照指定进行渲染。\'\\\'\\\'\""Sammy says: \"This string's in double quotes.\" It requires a backslash (\) before the double quotes (\\\"), but you MUST NOT add a backslash before the apostrophe (\')."OutputSammy says: "This string's in double quotes." It requires a backslash (\) before the double quotes (\"), but you MUST NOT add a backslash before the apostrophe (\').与单引用字符串一样,如果在双引号字符串中的双引号之前不包括反冲,PHP 将在该点结束字符串,这将导致错误。由于双报价字符串不会以单个报价结束,因此您直接将撇号添加到双报价字符串中。双引号字符串将输出与撇号一起使用的单反冲或双反冲。\'要输出序列,您必须使用三个反冲。首先渲染反冲本身,然后渲染双引号。序列完全按照指定进行渲染。\"\\\"\'被称为逃生字符。结合次要字符,它构成了一个逃生序列。现在,您已经了解了字符串,让我们回顾一下逃生序列。\逃生序列逃生序列告诉程序停止正常操作程序,并以不同的方式评估以下字符。在PHP中,逃生序列以反冲开始。逃生序列适用于双报价字符串。单引用字符串仅使用单个报价或反冲的逃生序列。\以下是双引字符串的一些常见逃生序列:以下是如何在字符串中使用这些序列的示例:"\"What type of \$ do sharks use?\"\n\tSand dollars!"Output"What type of $ do sharks use?"     Sand dollars!使用逃生序列使我们能够构建所需的任何字符串,同时包括这些特殊字符。创建和查看字符串输出双报价字符串最重要的特征是可变名称将扩展,从而为您提供变量值。您可以使用变量来代表字符串或直接使用字符串。您通过调用函数输出字符串:echo$my_name = "Sammy";echo 'Name is specified using the variable $my_name.';echo "\n"; // escape sequence for newline characterecho "Hello, my name is $my_name. It's stored in the variable \$my_name.";变量在第一行上创建。在第二行中,该函数用于在单个报价中输出字符串。使用此单引用字符串中的变量显示字符的编写方式,因此我们将看到可变名称而不是其值。$my_nameecho$my_name在第四行,我们再次使用该功能,但这次我们使用双报价。这一次,变量被扩展,以显示第一句中的值。在下一句中,有一个之前明确告诉字符串显示一个字符,而不是扩展变量。echo\$$OutputName is specified using the variable $my_name. Hello, my name is Sammy. It's stored in the variable $my_name.注意:当字符串评估不是问题时,您可以选择使用单个报价或双引号,但无论您决定使用哪一个报价,您都应该在程序内保持一致。单个报价可能会稍微快一些。了解如何创建和查看字符串的输出,让我们继续查看如何操作字符串。字符串串联连接意味着将字符串端到端连接在一起以构建新字符串。在 PHP 中,有两种主要方法来串联字符串。首先是在双报价字符串中包含字符串变量。这在前一步和以下步骤中显示:$answer = "Chews wisely."; echo "What do sharks do when they have a big choice to make? $answer";运行此代码将结合字符串和变量,该变量设置为:$answerChews wisely.OutputWhat do sharks do when they have a big choice to make? Chews wisely.串联字符串的第二种方法是使用操作员。.让我们通过一个语句将字符串和串联组合在一起:"Sammy""Shark"echoecho "Sammy" . "Shark";此代码使用操作员将字符串和字符串组合在一起,中间没有空格。."Sammy""Shark"OutputSammyShark如果您希望两个字符串之间的白空间,则必须在字符串中包括白空间,例如单词之后:Sammyecho "Sammy " . "Shark";OutputSammy Shark您不能使用串联将字符串与整数组合在一起:echo "Sammy" . 27;这将产生一个错误:OutputParse error: syntax error, unexpected '.27' (T_DNUMBER), expecting ';' or ',' in php shell code on line 1如果你把报价,它会评估为一个字符串。"27"PHP 是一种键入松散的语言,这意味着它将尝试转换根据请求提供的数据。如果您将变量设置为,当使用字符串串串时,PHP 将将变量解析为字符串:27$my_int = 27; echo "Sammy" . $my_int;OutputSammy27您涵盖了串联或组合字符串的两种主要方式。有时,您可能需要完全替换或添加到字符串。接下来,让我们来探索 PHP 如何允许您覆盖或更新字符串。更新字符串PHP 中的正常变量是可变的,这意味着它们可以更改或覆盖。让我们来探索当您更改变量值时会发生什么:$my_name$my_name = "Sammy"; echo $my_name . "\n"; $my_name = "Shark"; echo $my_name;OutputSammy Shark首先,该变量被设置为并显示使用。然后,它被设置为,覆盖变量,以便当被称为第二次,它显示了新的价值。"Sammy"echo"Shark"echo"Shark"您可以使用串联分配操作员来附加到字符串的末尾,而不是覆盖变量:.=$my_name = "Sammy"; $my_name .= " Shark"; echo $my_name;首先,将变量设置为,然后使用操作员添加到其末尾。新的价值是。$my_name"Sammy".=" Shark"$my_nameSammy SharkOutputSammy Shark要预先使用字符串的开头,您将在使用原始字符串时覆盖:$my_name = "Shark"; $my_name = "Sammy " . $my_name; echo $my_name;这一次,您首先将变量设置为,然后使用操作员用新字符串覆盖变量,与变量的前值相结合,在被覆盖之前。最终值是。$my_name"Shark"=$my_name"Sammy "$my_name"Shark"$my_nameSammy SharkOutputSammy Shark覆盖、附加和预支出使我们能够进行更改并构建应用程序所需的字符串。字符串中的白空间因为PHP不关心空白,你可以把尽可能多的空间或断线在你的报价,你想。echo "Sammy The           (silly) Shark";TEXT OutputSammy The           (silly) Shark请记住,HTML 以不同的方式呈现白空间。新行需要标记,因此即使您的源可能有新行,您也不会在网页上看到这些新行。同样,无论您的代码中有多少空间,字符之间只显示一个空格。<br>HTML OutputSammy The (silly) Shark清洁和一致地使用白空间是使代码更具可读性的最佳工具之一。由于PHP基本上忽略了空白,你有很大的灵活性,你可以利用你的优势。集成开发环境(IDE) 可以帮助您与代码保持一致,并使用白空间。结论能够控制字符串的呈现方式对于与应用程序的最终用户进行通信至关重要。通过更新和组合包含特殊字符的变量,您可以清楚地进行通信,同时将重复保持在最低限度。当您继续处理字符串时,请记住以下三个方面:\"双报价\\反冲\$渲染美元符号而不是扩展变量\n一条新线\t用于选项卡特别注意字符串中的报价。使用串联组合您的字符串。使用变量使字符串可重复使用。
  • [产品动态] 人人都可“钢铁侠”,XR五大核心技术拉近未来
    华为云 郑洛说起钢铁侠,让人想起漫威迷们总结的“漫威宇宙规律”——“土豪靠装备,穷人靠变异”,盘点钢铁侠斯塔克的装备:从“48 套钢铁战衣”,到铁打的AI助手“贾维斯”,“星期五”——懂技术懂装备懂老板,还有他研发装备时在空中随心勾勒,将设计原型旋转、修改、定稿、抛入制造系统的一系列骚操作,一顿玩儿就把活儿干了钱挣了,这无一不是大多数理工男的终极梦想。这个终极梦想,真的就那么遥不可及吗?我们今天就来聊聊,土豪背后的技术——XR(VR,AR,MR)技术,看完后,你就知道,未来已被拉近。                                   简单科普一下什么是XR,XR 就是VR、AR、MR 的统称。虚拟现实(Virtual Reality)、增强现实(Augmented Reality)和混合现实(Mixed Reality),正在给我们的生活方式带来一系列的变化。不远的未来,你还可以坐在虚拟的会议室跟你虚拟的小伙伴们开一场云上会议,在虚拟的智慧教室上一堂多人互动课程、做一场模拟实验,甚至进入虚拟世界进行真实的设计、制造,虚拟和真实之间,边界不再那么明显。这一切,都得益于XR(VR,AR,MR)技术的应用。XR 技术,究竟依赖哪些核心能力呢?华为云最新发布的XR 云服务,给了我们答案。它提供了五大核心技术和能力,打开未来新视界。1、  大规模3D地图构建能力3D地图是数字孪生世界的基础。传统制作3D地图的成本,是非常高昂的。图商需要采购价格高昂的测绘车队,常年雇佣规模在千人左右的采集团队,并组织人力对采集来的数据进行标注、补缺。随着AI算法、传感器的发展,技术上已能实现自动化程度更高的制图流程。华为云XR提供手机拍摄视频的众包建图方案,只要是通过手机拍摄慢速转动的视频,或者有重叠区域的图片,覆盖建图的区域,传至云端即可通过算法还原出3D模型。如果将3D模型中的3个点与数字世界的坐标对齐即可将局部的3D模型补充到数字世界中,大大降低了制图成本。华为云同时支持卫星制图,具备大面积快速覆盖能力,配合地图自动生成流水线,大规模构建3D地图。2、  厘米级全场景适应的空间计算高精度定位技术大体上可以分为三类:基于信号的定位、基于航迹推算的定位、基于环境特征匹配的定位。基于信号的定位:普通的多星(GPS、北斗、伽利略)定位精度大约能做到1-2米。新的RTK(Real - time kinematic,实时动态)载波相位差分技术,是能够在野外实时得到厘米级定位精度的测量方法。但它同时存在一些问题,比如基站布设成本高,易受电磁环境干扰、易受环境遮挡、信号多径效应、4G/5G/WIFI网络环境差的影响等等,从而影响定位精度和定位系统的可用性。基于航迹推算[1]的定位: IMU[2]是常用的航迹推算系统,优势在于没有外部依赖,可以提供短时高精度的定位结果,缺点在于在连续的位置和方向的测量中误差会不断累积,导致位置和姿态的测量结果偏离实际位置,因而无法做长时间的高精度定位。基于环境特征匹配的定位技术[3],华为云XR能力平台提供根据视觉的定位技术,通过定期上传照片,在云端进行特征点提取、匹配,能够做到厘米级定位与一度以内定姿(6DOF)。针对光照条件差,弱纹理的夜晚和室内场景,通过AI方式进行增强,实现夜晚也能找到图像匹配点,室内也能定位的全场景适应的空间定位算法。基于VPS和SLAM的紧耦合算法架构,可逐步实现长时间的室内外导航。3、  提升效果同时提升数倍效率的AI渲染算法3D 渲染管线也称为渲染流水线,可以将其理解为一个流程,就是我们准备一些数据,让GPU 对这些数据做一些处理,最后得出一张二维图像,渲染流程主要分为几个大的阶段:数据准备阶段,顶点处理阶段,光栅操作阶段,像素着色阶段。当前渲染优化有两个方向,一个是渲染画质好,一个是游戏实时性高。华为通过对渲染管线中算法的深入研究和调试,并通过端云结合的AI 算法,对渲染管线进行拆分,达到画质和实时性的均衡,实现数倍效率,画质相对于传统渲染方式有大幅的提升。4、  十万发丝级数字人渲染不久的将来,现实世界中的每一个地方和事物 —— 每一条街道、每一个灯柱、每一栋建筑物和每一个房间 —— 都会在镜像世界中拥有它的全尺寸“数字孪生兄弟”。——凯文·凯利华为在数字人的生成、建模、渲染阶段均有所涉猎,如全自动化90% 相似度高精细**模型生成。传统的建模通过离线工具制作,费时费力,华为考虑改造建模过程,实现快速复制、自动化生成、快速建模。渲染过程的开发,主要是从皮肤、牙齿、眼睛、毛发等等方面入手。数字人的表情肢体动作自然驱动和表达,唇音同步延时<100ms,使用者时延无感知 。皮肤是半透的,比较薄的地方有散射的效果,光散射过来会产生折射,加入后会多一些细节;眼睛是晶状体,有透射效果;头发有很多层,每层有不同效果。 华为实时高真实度数字人绘制,实现逼真的发丝、皮肤、眼睛渲染呈现,10 万级发丝,fps>30 帧,整体主观打分8 分以上。5、  360度VR视频TWS FOV技术(Tile-Wise-Streaming Field of View)360 度VR 视频领域,华为提出了TWS FOV 传输技术,可以解决4K 硬件解码能力终端,基于4K 码率观看8K 片源的问题。通过TWS 映射方式,从像素点上就可以相对ERP 映射,直降40% 码率。通过增加低资源消耗的背景流,解决黑边问题,加入单帧切换技术,提升转头高清体验。↓↓↓XR 技术的研究和应用,未来将有无限可能。虚拟和真实将相辅相成,实现真实世界与虚拟世界之间的无界沟通和协作。在不久的将来,人人都可像钢铁侠一样,通过XR 技术释放无穷的创造力和想象力,突破极限,共筑未来。[1]航迹推算:(dead reckoning)通过测量运动主体移动的距离和方位,与原位置叠加,从而推算出当前位置的方法。[2]IMU: Inertial measurement unit, 惯性测量单元。[3]环境特征匹配的定位技术:通过实时测量提取环境特征,并与预先采集的基准数据进行匹配,从而获取确定的当前位置。
  • 浅谈实时计算机图像渲染和建模
    虚拟现实(VR)与增强现实(AR)是能够彻底颠覆传统人 机交互内容的变革性技术。变革不仅体现在消费领域,更 体现在许多商业和企业市场中。 VR/AR需要大量的数据传输、存储和计算功能,这些数据 和计算密集型任务如果转移到云端,就能利用云端服务器 的数据存储和高速计算能力。云 VR/AR 将大大降低设备成本 – 提供人人都能负担 得起的价格。    2. 云市场以18%的速度快速增长。在未来的 10 年中,家 庭和办公室对桌面主机和笔记本电脑的需求将越来越 小,转而使用连接到云端的各种人机界面,并引入语 音和触摸等多种交互方式。5G 将显著改善这些云服务 的访问速度。
  • 云 VR:走入千家万户的普惠 VR
    VR 是超高清视频技术重要应用场景。云 VR 是将云计 算、云渲染的理念及技术引入到 VR 业务应用中,借助高速 稳定的网络,在云端进行 VR 超高清画面的渲染和处理,并 对显示输出和声音输出等经过编码压缩后传输到用户的终 端设备,实现 VR 业务内容上云、渲染上云。
  • [技术干货] 【转载】高价值干货:这可能是你见过最全的网络爬虫总结
    摘要:从抓取、解析、存储、反爬、加速五个方面介绍了利用 Python 进行网络爬虫开发的相关知识点和技巧,介绍了不同场景下如何采取不同措施高效地进行数据抓取的方法。前段时间参加了一场 Python 网络爬虫主题的分享活动,主要以直播的形式分享了我从事网络爬虫相关研究以来的一些经验总结。整个分享分为三个阶段,第一阶段先介绍了自己从大学以来从事编程开发以来的相关历程,第二阶段是正式的网络爬虫分享流程,详细总结了网络爬虫开发的一些要点,第三阶段是解答一些提问,并抽奖送出一些礼品。所以在这里我会对我昨天分享的主要内容做下总结,希望大家可以支持!总括分享的主题叫做《健壮高效的网络爬虫》,本次分享从抓取、解析、存储、反爬、加速五个方面介绍了利用 Python 进行网络爬虫开发的相关知识点和技巧,介绍了不同场景下如何采取不同措施高效地进行数据抓取的方法,包括 Web 抓取、App 抓取、数据存储、代理选购、验证码破解、分布式抓取及管理、智能解析等多方面的内容,另外还结合了不同场景介绍了常用的一些工具包,全部内容是我在从事网络爬虫研究过程以来的经验精华总结。爬取对于爬取来说,我们需要学会使用不同的方法来应对不同情景下的数据抓取任务。爬取的目标绝大多数情况下要么是网页,要么是 App,所以这里就分为这两个大类别来进行了介绍。对于网页来说,我又将其划分为了两种类别,即服务端渲染和客户端渲染,对于 App 来说,我又针对接口的形式进行了四种类别的划分——普通接口、加密参数接口、加密内容接口、非常规协议接口。所以整个大纲是这样子的:网页爬取服务端渲染客户端渲染App 爬取普通接口加密参数接口加密内容接口非常规协议接口爬取 / 网页爬取服务端渲染的意思就是页面的结果是由服务器渲染后返回的,有效信息包含在请求的 HTML 页面里面,比如猫眼电影这个站点。客户端渲染的意思就是页面的主要内容由 JavaScript 渲染而成,真实的数据是通过 Ajax 接口等形式获取的,比如淘宝、微博手机版等等站点。服务端渲染的情况就比较简单了,用一些基本的 HTTP 请求库就可以实现爬取,如 urllib、urllib3、pycurl、hyper、requests、grab 等框架,其中应用最多的可能就是 requests 了。对于客户端渲染,这里我又划分了四个处理方法:寻找 Ajax 接口,此种情形可以直接使用 Chrome/Firefox 的开发者工具直接查看 Ajax 具体的请求方式、参数等内容,然后用 HTTP 请求库模拟即可,另外还可以通过设置代理抓包来查看接口,如 Fiddler/Charles。模拟浏览器执行,此种情形适用于网页接口和逻辑较为复杂的情况,可以直接以可见即可爬的方式进行爬取,如可以使用 Selenium、Splinter、Spynner、pyppeteer、PhantomJS、Splash、requests-html 等来实现。直接提取 JavaScript 数据,此种情形适用于真实数据没有经过 Ajax 接口获取,而是直接包含在 HTML 结果的某个变量中,直接使用正则表达式将其提取即可。模拟执行 JavaScript,某些情况下直接模拟浏览器执行效率会偏低,如果我们把 JavaScript 的某些执行和加密逻辑摸清楚了,可以直接执行相关的 JavaScript 来完成逻辑处理和接口请求,比如使用 Selenium、PyExecJS、PyV8、js2py 等库来完成即可。爬取 / App 爬取对于 App 的爬取,这里分了四个处理情况:对于普通无加密接口,这种直接抓包拿到接口的具体请求形式就好了,可用的抓包工具有 Charles、Fiddler、mitmproxy。对于加密参数的接口,一种方法可以实时处理,例如 Fiddler、mi**ump、Xposed 等,另一种方法是将加密逻辑破解,直接模拟构造即可,可能需要一些反编译的技巧。对于加密内容的接口,即接口返回结果完全看不懂是什么东西,可以使用可见即可爬的工具 Appium,也可以使用 Xposed 来 hook 获取渲染结果,也可以通过反编译和改写手机底层来实现破解。对于非常规协议,可以使用 Wireshark 来抓取所有协议的包,或者使用 Tcpdump 来进行 TCP 数据包截获。以上便是爬取流程的相关分类和对应的处理方法。解析对于解析来说,对于 HTML 类型的页面来说,常用的解析方法其实无非那么几种,正则、XPath、CSS Selector,另外对于某些接口,常见的可能就是 JSON、XML 类型,使用对应的库进行处理即可。这些规则和解析方法其实写起来是很繁琐的,如果我们要爬上万个网站,如果每个网站都去写对应的规则,那么不就太累了吗?所以智能解析便是一个需求。智能解析意思就是说,如果能提供一个页面,算法可以自动来提取页面的标题、正文、日期等内容,同时把无用的信息给刨除,例如上图,这是 Safari 中自带的阅读模式自动解析的结果。对于智能解析,下面分为四个方法进行了划分:readability 算法,这个算法定义了不同区块的不同标注集合,通过权重计算来得到最可能的区块位置。疏密度判断,计算单位个数区块内的平均文本内容长度,根据疏密程度来大致区分。Scrapyly 自学习,是 Scrapy 开发的组件,指定⻚页⾯面和提取结果样例例,其可⾃自学习提取规则,提取其他同类⻚页⾯面。深度学习,使⽤用深度学习来对解析位置进⾏行行有监督学习,需要⼤大量量标注数据。如果能够容忍一定的错误率,可以使用智能解析来大大节省时间。目前这部分内容我也还在探索中,准确率有待继续提高。存储存储,即选用合适的存储媒介来存储爬取到的结果,这里还是分为四种存储方式来进行介绍。文件,如 JSON、CSV、TXT、图⽚、视频、⾳频等,常用的一些库有 csv、xlwt、json、pandas、pickle、python-docx 等。数据库,分为关系型数据库、非关系型数据库,如 MySQL、MongoDB、HBase 等,常用的库有 pymysql、pymssql、redis-py、pymongo、py2neo、thrift。搜索引擎,如 Solr、ElasticSearch 等,便于检索和实现⽂本匹配,常用的库有 elasticsearch、pysolr 等。云存储,某些媒体文件可以存到如七⽜牛云、又拍云、阿里云、腾讯云、Amazon S3 等,常用的库有 qiniu、upyun、boto、azure-storage、google-cloud-storage 等。这部分的关键在于和实际业务相结合,看看选用哪种方式更可以应对业务需求。反爬反爬这部分是个重点,爬虫现在已经越来越难了,非常多的网站已经添加了各种反爬措施,在这里可以分为非浏览器检测、封 IP、验证码、封账号、字体反爬等。下面主要从封 IP、验证码、封账号三个方面来阐述反爬的处理手段。反爬 / 封 IP对于封 IP 的情况,可以分为几种情况来处理:首先寻找手机站点、App 站点,如果存在此类站点,反爬会相对较弱。使用代理,如抓取免费代理、购买付费代理、使用 Tor 代理、Socks 代理等。在代理的基础上维护自己的代理池,防止代理浪费,保证实时可用。搭建 ADSL 拨号代理,稳定高效。反爬 / 验证码验证码分为非常多种,如普通图形验证码、算术题验证码、滑动验证码、点触验证码、手机验证码、扫二维码等。对于普通图形验证码,如果非常规整且没有变形或干扰,可以使用 OCR 识别,也可以使用机器学习、深度学习来进行模型训练,当然打码平台是最方便的方式。对于算术题验证码,推荐直接使用打码平台。对于滑动验证码,可以使用破解算法,也可以模拟滑动。后者的关键在于缺口的找寻,可以使用图片比对,也可以写基本的图形识别算法,也可以对接打码平台,也可以使用深度学习训练识别接口。对于点触验证码,推荐使用打码平台。对于手机验证码,可以使用验证码分发平台,也可以购买专门的收码设备,也可以人工验证。对于扫二维码,可以人工扫码,也可以对接打码平台。反爬 / 封账号某些网站需要登录才能爬取,但是一个账号登录之后请求过于频繁会被封号,为了避免封号,可以采取如下措施:寻找手机站点或 App 站点,此种类别通常是接口形式,校验较弱。寻找无登录接口,尽可能寻找⽆无需登录即可爬取的接口。维护 Cookies 池,使⽤用批量账号模拟登录,使⽤时随机挑选可用 Cookies 使⽤即可,实现:https://github.com/Python3WebSpider/CookiesPool加速当爬取的数据量非常大时,如何高效快速地进行数据抓取是关键。常见的措施有多线程、多进程、异步、分布式、细节优化等。加速 / 多线程、多进程爬虫是网络请求密集型任务,所以使用多进程和多线程可以大大提高抓取效率,如使用 threading、multiprocessing 等。加速 / 异步将爬取过程改成非阻塞形式,当有响应式再进行处理,否则在等待时间内可以运行其他任务,如使用 asyncio、aiohttp、Tornado、Twisted、gevent、grequests、pyppeteer、pyspider、Scrapy 等。加速 / 分布式分布式的关键在于共享爬取队列,可以使用 celery、huey、rq、rabbitmq、kafka 等来实现任务队列的对接,也可以使用现成的框架 pyspider、Scrapy-Redis、Scrapy-Cluster 等。加速 / 优化可以采取某些优化措施来实现爬取的加速,如:DNS 缓存使用更快的解析方法使用更高效的去重方法模块分离化管控加速 / 架构如果搭建了分布式,要实现高效的爬取和管理调度、监控等操作,我们可以使用两种架构来维护我们的爬虫项目。将 Scrapy 项目打包为 Docker 镜像,使用 K8S 控制调度过程。将 Scrapy 项目部署到 Scrapyd,使用专用的管理工具如 SpiderKeeper、Gerapy 等管理。另外对于这部分内容,其实还有我制作的更丰富的思维导图,预览图如下:转载自:华为云开发者论坛
  • 竞享实例,瞬享自由算力
    近日,华为云正式对外推出竞享实例。竞享型实例作为创新商业模式,用户可以先到先享,并将颗粒化算力瞬间批量组织起来用于各类可容错场景,其以远低于同类产品的价格以及出色稳定的性能为离线转码、离线渲染、基因测序及Web应用等可容错业务场景提供强有力的基础支撑,助力用户瞬享自由算力。极致性能,激发潜能竞享实例承袭华为云C系列高性能云服务器技术基因,最高支持64核256G,同规格同性能机型价格低至按需实例的1.5折。为动画制作特效师、建筑设计师等提供稳定、高效、安全的渲染环境。影视特效作为电影产业中或不可缺的元素之一,为电影的发展做出了巨大的贡献。优秀的电影都在极致追求逼真的场景构建、人物刻画,道具。而逼真的视觉效果往往需要渲染更多帧数、耗费更长时间。以某科幻大片为例,全片共有2003个特效镜头,开场从地面拉向太空的2mins长镜头共有3000帧。以目前主流4K影片在3000台按需24核服务器集群渲染为例,每台服务器并行渲染1帧。每帧平均时间20h,总时间需要20h,按需成本约32W。而在实际影视制作中,通常一个画面需要渲染,修改多次才能出片,实际成本和时间会更高。同样情况下,若采用竞享实例,保质保量完成渲染任务的同时,成本仅需8万元,成本可节省75%。为缩短渲染周期、节省渲染成本提供最优解,满足高性能渲染对算力的强需求,让技术与艺术融合,加速中国影视产业的腾飞。  灵活便捷,玩转潮汐竞享实例支持短周期可容错业务,随取随用。业务波峰期,用户可根据业务量选择购买时长,到期后自动释放实例。配合业务的自身的容错机制与弹性伸缩机制,助力用户轻松玩转潮汐对各类Web应用而言,如社交类产品,经常出现因为社会热点话题引发的流量峰值,且流量峰值不可预估,如潮汐效应,在月球引潮力作用下, 海水发生周期性涨落的现象,而受自然规律和社会环境的共同影响,人类行为也会有周期性“潮汐效应”。竞享实例以灵活便捷及超高性价比的优势脱颖而出,业务高峰期,根据业务诉求批量开通竞享实例资源,设置时长到期后自动释放,同时还能以远低于目前市面上实例的价格拥有同等性能,助力企业轻松应对业务浪涌。高性价比,百里挑一竞享实例支持申请超大规模算力负载用于业务快速闭环,最高可支持万核资源购买申请,以超低价格构建计算集群,万核资源每小时使用费用低至300元。如对于基因测序场景而言,人类第一次完成全基因组测序耗时13年,耗资27亿美金,随着技术的不断演进,在2018年JP Morgan健康大会,高质量的个人全基因组测序的价格降为600美金,这标志着基因测序的成本越来越低,速度越来越快。基因测序行业巨擘甚至预测在不远的未来,它将成为常规的体检项目,除了基因测序技术高速发展外,与算力成本的下降也是密不可分的。**中的人类全基因组有30亿碱基,需要对基因进行拼装、排序、去重、变异检测后,才算完成一个完整的WGS (人类全基因组测序)流程。分析竞享实例以极致性价比的优势,单核0.035元/小时起 ,完美匹配基因测序场景诉求,有效降低分析计算成本,缩短测序周期,真正做到同样性能更低价格,百里挑一。竞享实例是面向各种无状态,容错或者灵活的应用程序推出的全新一代计算服务,短时间、大规模、高性价比算力一触即发,助力企业实现数字化转型和智能化升级                            目前竞享实例正在热卖中,点击【优惠算力,先到先享】!
总条数:44 到第
上滑加载中