
jquery抓取网页内容
jquery抓取网页内容 技巧:二.主要设计操作
网站优化 • 优采云 发表了文章 • 0 个评论 • 177 次浏览 • 2022-09-24 11:14
一.基本信息
jQuery 于 2006 年发布,目前是前端寿命最长的库。它是世界上使用最广泛的图书馆,拥有全球80%以上的网站。
二.主要设计操作
1.获取网页元素
一种是通过jQuery选择器选择元素,可以直接获取单个或批量元素;
另一种是通过jQuery遍历关联来选择元素,常用于获取层级比较复杂的页面中的元素。
2.jQuery 链接
jQuery的第三个设计思路是网页元素最终被选中后,可以对其进行一系列的操作,所有的操作可以连在一起,写成链的形式。
例子:
$('div').find('h3').eq(2).html('Hello');
3.jQuery 创建元素
(1)使用$函数创建新元素
var $newElement=$('<p>段落');//创建元素,返回jQuery对象</p>
(2)添加子元素
使用追加方法
使用 appendTo 方法
4.移动元素
操纵网页中元素的位置和移动。一组方法是直接移动元素,另一组方法是移动其他元素,使目标元素在我们想要的位置。
假设我们选择了一个 div 元素,需要将它移到 p 元素的后面。
第一种方法是使用.insertAfter()将div元素移到p元素后面:
$('div').insertAfter($('p'));
第二种方法是使用.after()将p元素添加到div元素的前面:
$('p').after($('div'));
从表面上看,两种方法的效果是一样的,唯一的区别似乎是操作的视角不同。
5.修改元素属性
在jquery中,可以使用attr()方法来修改元素的属性和内容。
例子:
$("button").click(function(){
$("#w3s").attr("href","http:www.123.com.cn");
});
attr() 也提供回调函数。
回调函数有两个参数:当前元素在被选元素列表中的索引,以及原创(旧)值。然后返回你希望使用的字符串和函数的新值。
例子:
$("button").click(function(){
$("#s").attr("href", function(i,Value){
return Value + "/jquery";
});
});
测评:百度链接提交与自动抓取的区别,该如何选择?
那么,百度链接提交和自动抓取有什么区别,如何选择?
根据以往百度网站提交的经验,T3模板网将通过以下内容进行讲解:
1、主动提交
对于普通收录和快速收录权限,我们认为它具有以下特点:
①提高搜索引擎发现新链接的时间。
2)快速进入百度搜索指数评测频道,简单理解就是增加指数量。
③节省目标页面被百度发现的成本,如:外链建设,引蜘蛛的成本。
但同时,根据我们广泛的测试,这种形式的数据提交仍然受到以下因素的影响,例如:
①网站链接提交的时间节点。
②网站链接提交次数和频率。
当搜索引擎的爬取通道太忙时,很容易造成一些URL地址的遗漏,也就是在不同的时间点提交URL,收录的数量可能会有很大的变化。
其次,如果网站链接提交的次数和频率过于密集,整个站点的链接率也会有一定的波动。
2、自动爬取
一般来说,所谓搜索引擎自动爬取主要是指百度蜘蛛主动爬取你的页面内容,主要受以下因素影响:
①优质外链的数量和增长的频率。
②网站优质内容的更新次数占整个网站内容的比例。
③页面内容的更新频率。
一般来说,如果能在某个时间节点保持一定程度的活跃度,自动爬取是非常有规律的,而且随着整个站点质量的提高,网站很容易进入“秒收录”的状态。
这个时候,我们根本不需要考虑。对于链接提交的问题,只要更新内容,我们就可以继续得到收录。
同时,我们根据一些日常操作做了基本的判断,发现:
如果你主动提交的链接质量比例不高,是对搜索引擎的质量评价,可以很快得到。如果长期处于低质量状态,很容易出现链接不良收录,同时引起自动爬取的页面虽然是收录,但大部分会进入低质量库,即检索相关的新页面,没有任何排名。
相比之下,我们认为自动抓取在这方面可能相对广泛。
3、合理的选择
综合以上因素,我们认为如果你有能力,我们还是建议你选择网站让百度搜索自动爬取和收录,并且可以适当减少< @网站 链接,除非:
①你的外链资源有限,很难建立一些比较优质的链接。
②您的目标页面在全站目录层次较深,搜索引擎难以发现和抓取。
③您的网站是新推出的,尚未经过沙盒或质量评估。 (使用链接提交,可以快速通过这个循环,前提是结构和内容质量一定要好。)
总结:seo是一项细致的工作,我们应该善于发现百度搜索产品的差异,当然以上内容只是经验,仅供参考。 查看全部
jquery抓取网页内容 技巧:二.主要设计操作
一.基本信息
jQuery 于 2006 年发布,目前是前端寿命最长的库。它是世界上使用最广泛的图书馆,拥有全球80%以上的网站。
二.主要设计操作
1.获取网页元素
一种是通过jQuery选择器选择元素,可以直接获取单个或批量元素;
另一种是通过jQuery遍历关联来选择元素,常用于获取层级比较复杂的页面中的元素。
2.jQuery 链接
jQuery的第三个设计思路是网页元素最终被选中后,可以对其进行一系列的操作,所有的操作可以连在一起,写成链的形式。
例子:
$('div').find('h3').eq(2).html('Hello');
3.jQuery 创建元素
(1)使用$函数创建新元素
var $newElement=$('<p>段落');//创建元素,返回jQuery对象</p>

(2)添加子元素
使用追加方法
使用 appendTo 方法
4.移动元素
操纵网页中元素的位置和移动。一组方法是直接移动元素,另一组方法是移动其他元素,使目标元素在我们想要的位置。
假设我们选择了一个 div 元素,需要将它移到 p 元素的后面。
第一种方法是使用.insertAfter()将div元素移到p元素后面:
$('div').insertAfter($('p'));
第二种方法是使用.after()将p元素添加到div元素的前面:
$('p').after($('div'));
从表面上看,两种方法的效果是一样的,唯一的区别似乎是操作的视角不同。
5.修改元素属性

在jquery中,可以使用attr()方法来修改元素的属性和内容。
例子:
$("button").click(function(){
$("#w3s").attr("href","http:www.123.com.cn");
});
attr() 也提供回调函数。
回调函数有两个参数:当前元素在被选元素列表中的索引,以及原创(旧)值。然后返回你希望使用的字符串和函数的新值。
例子:
$("button").click(function(){
$("#s").attr("href", function(i,Value){
return Value + "/jquery";
});
});
测评:百度链接提交与自动抓取的区别,该如何选择?
那么,百度链接提交和自动抓取有什么区别,如何选择?
根据以往百度网站提交的经验,T3模板网将通过以下内容进行讲解:
1、主动提交
对于普通收录和快速收录权限,我们认为它具有以下特点:
①提高搜索引擎发现新链接的时间。
2)快速进入百度搜索指数评测频道,简单理解就是增加指数量。
③节省目标页面被百度发现的成本,如:外链建设,引蜘蛛的成本。
但同时,根据我们广泛的测试,这种形式的数据提交仍然受到以下因素的影响,例如:
①网站链接提交的时间节点。

②网站链接提交次数和频率。
当搜索引擎的爬取通道太忙时,很容易造成一些URL地址的遗漏,也就是在不同的时间点提交URL,收录的数量可能会有很大的变化。
其次,如果网站链接提交的次数和频率过于密集,整个站点的链接率也会有一定的波动。
2、自动爬取
一般来说,所谓搜索引擎自动爬取主要是指百度蜘蛛主动爬取你的页面内容,主要受以下因素影响:
①优质外链的数量和增长的频率。
②网站优质内容的更新次数占整个网站内容的比例。
③页面内容的更新频率。
一般来说,如果能在某个时间节点保持一定程度的活跃度,自动爬取是非常有规律的,而且随着整个站点质量的提高,网站很容易进入“秒收录”的状态。
这个时候,我们根本不需要考虑。对于链接提交的问题,只要更新内容,我们就可以继续得到收录。

同时,我们根据一些日常操作做了基本的判断,发现:
如果你主动提交的链接质量比例不高,是对搜索引擎的质量评价,可以很快得到。如果长期处于低质量状态,很容易出现链接不良收录,同时引起自动爬取的页面虽然是收录,但大部分会进入低质量库,即检索相关的新页面,没有任何排名。
相比之下,我们认为自动抓取在这方面可能相对广泛。
3、合理的选择
综合以上因素,我们认为如果你有能力,我们还是建议你选择网站让百度搜索自动爬取和收录,并且可以适当减少< @网站 链接,除非:
①你的外链资源有限,很难建立一些比较优质的链接。
②您的目标页面在全站目录层次较深,搜索引擎难以发现和抓取。
③您的网站是新推出的,尚未经过沙盒或质量评估。 (使用链接提交,可以快速通过这个循环,前提是结构和内容质量一定要好。)
总结:seo是一项细致的工作,我们应该善于发现百度搜索产品的差异,当然以上内容只是经验,仅供参考。
免费获取:网页数据抓取-免费网站爬虫数据抓取软件自动抓取发布软件
网站优化 • 优采云 发表了文章 • 0 个评论 • 94 次浏览 • 2022-09-23 11:10
网页数据抓取,什么是网页抓取。如何快速抓取网络数据。今天给大家分享一款免费的网页数据抓取软件,只需要输入网站域名自动抓取网站页面数据,自动抓取网页数据+自动网站< @k6@ > 发帖,详情请看图片。
选择域名很重要,因为这是网站优化的第一步。注册域名时,我们首先要找一个与主题相关的域名。首选是中文拼音,其次是英文。如果不是,请选择一个域名较短的域名。它是衡量SEO效果的必要因素,虽然它对SEO的效果影响不大。很大,但我们还是要跟着,一定的水平有利于后期的优化和传播。
1)注册的域名越短越好。域名越短,越容易记住。域名注册可以去万网或者其他大品牌,因为安全性会更好。
2)为网站的主题选择一个域名
注册域名时需要定位网站主题,根据网站主题选择相关域名
3)关键字优先使用中文拼音,关键字优先使用英文。现在指定的域名不用直接选择双拼了,推荐双拼+号的方式。另外,建议一次性注册一组域名,以免其他域名被别人注册,日后要天价。 , 如果英语语言注册最好针对目标群体,如果是大众群体,不利于优化交流。网页数据抓取,例如站长组,对英文包括bbs、news、blog等比较熟悉,好记。
4).Com .Cn .Org . . . .net .gov .edu
域名的后缀对于SEO也很重要。一般来说,.gov .edu 非政府和教育机构无权注册,但.gov .edu 域名拥有最高权限,.com .org .gov 国际域名更适合国内域名。 . . ,主要是声誉成本问题。
注意:国际域名升值空间更大。 .cc .tv .me 等其他域名使用相对较少。不建议注册一些不常见的声誉后缀,除非你是专门做域名研究的,否则就是浪费钱。
5)域名注册时间越长越好
搜索引擎无论是否抓取网络,都会抓取域名的whois信息。网页数据爬取包括域名的whois信息,如注册时间、过期时间等。
既然seo是网站的收录,那么它和数量有直接关系,原因是由于网站的搜索效果的概率。更何况长尾关键词,长尾关键词的排名优化会直接影响长尾关键词的排名次数,网页数据抓取而网站的排名有数百个因素影响网站收录。
那我给大家讲讲影响网站收录的因素,首先是网站的开启速度,网站的开启速度大家都知道速度不仅影响你的网站,对用户体验也不利。网页数据爬取,那你觉得如果网站打开慢会影响蜘蛛的阅读,所以网站打开速度很重要,关于影响网站@打开速度> 原因包括服务器提供商、服务器宽带速度、服务器硬件质量、服务器操作系统、服务器软件操作、DNS等。
接下来是网站权重的影响。每个人可能都不知道网站的重量。这个权重问题将直接关系到搜索引擎对网站的信誉值的评价。 网站 如果权重高,那么搜索引擎的抓取会更及时,有时会秒到。这也是由于该站点的权重相对较高。相反,如果网站的权重很低,则证明搜索引擎对网站的声誉值的评价很低。蜘蛛对站点的爬取频率会低,一旦站点正常,灰腕很可能直接被K驻扎。
其次要说一下网站结构的结构设置,结构外观美观,小场地多规划为平面结构,大场地多规划为树结构。网页数据抓取是我们的网站结构和规划复杂,复杂的内部链规划会导致网站收录慢,尤其是二级栏目和三级内容页面收录 甚至更慢甚至没有网站 @收录。比如我们要建一栋楼,每一层都要一样,每一层的结构都要非常坚固。因此,当我们规划网站的构建时,不同的列可以直接链接内部链。同时网站的目录深度最好限制在三层,以免影响蜘蛛对收录爬得太深。
免费:如何推广网站维护托管蜘蛛抓取爬虫
如果一个网站想要排名好,肯定有蜘蛛在爬。蜘蛛爬网以获取它想要的信息。 网站 的链接起着非常重要的作用。如果要增加网站,就必须增加蜘蛛爬行的频率。那么如何在网站维护、托管和维护中增加蜘蛛爬取的频率呢?首先我们要了解影响蜘蛛爬取频率的因素: 1网站结构:网站在构造时,首先选择短域名,简化目录层次,避免网址过长或动态参数太多,短的更容易记住。 2 页面速度:百度多次提到的移动优先级指标,最重要的是页面的首次加载速度,必须控制在3秒以内。 3 入站链接:理论上只要是外部链接,无论其质量和形状如何,都可以引导爬虫进行爬取。 4 主动提交:主动提交网站地图、JS访问、官方API等,接下来看看如何提高网站维护主机维护百度蜘蛛爬虫抓取频率:1、网站更新频率:网站只要更新频率越高,爬虫就会越多。 2. 网站重量:重 网站 轨道也经常抓取。 3. 网站内容质量:网站内容新颖优质,可以解决用户的问题,提高爬虫的爬取频率。 4、外链和内链的构建:链接是页面的入口,优质的外链和内链可以更好的引导蜘蛛进入和捕获。希望小编分享的关于网站维护主机维护增加蜘蛛爬取频率的内容可以对大家有所帮助。
查看全部
免费获取:网页数据抓取-免费网站爬虫数据抓取软件自动抓取发布软件
网页数据抓取,什么是网页抓取。如何快速抓取网络数据。今天给大家分享一款免费的网页数据抓取软件,只需要输入网站域名自动抓取网站页面数据,自动抓取网页数据+自动网站< @k6@ > 发帖,详情请看图片。
选择域名很重要,因为这是网站优化的第一步。注册域名时,我们首先要找一个与主题相关的域名。首选是中文拼音,其次是英文。如果不是,请选择一个域名较短的域名。它是衡量SEO效果的必要因素,虽然它对SEO的效果影响不大。很大,但我们还是要跟着,一定的水平有利于后期的优化和传播。
1)注册的域名越短越好。域名越短,越容易记住。域名注册可以去万网或者其他大品牌,因为安全性会更好。
2)为网站的主题选择一个域名
注册域名时需要定位网站主题,根据网站主题选择相关域名

3)关键字优先使用中文拼音,关键字优先使用英文。现在指定的域名不用直接选择双拼了,推荐双拼+号的方式。另外,建议一次性注册一组域名,以免其他域名被别人注册,日后要天价。 , 如果英语语言注册最好针对目标群体,如果是大众群体,不利于优化交流。网页数据抓取,例如站长组,对英文包括bbs、news、blog等比较熟悉,好记。
4).Com .Cn .Org . . . .net .gov .edu
域名的后缀对于SEO也很重要。一般来说,.gov .edu 非政府和教育机构无权注册,但.gov .edu 域名拥有最高权限,.com .org .gov 国际域名更适合国内域名。 . . ,主要是声誉成本问题。
注意:国际域名升值空间更大。 .cc .tv .me 等其他域名使用相对较少。不建议注册一些不常见的声誉后缀,除非你是专门做域名研究的,否则就是浪费钱。
5)域名注册时间越长越好
搜索引擎无论是否抓取网络,都会抓取域名的whois信息。网页数据爬取包括域名的whois信息,如注册时间、过期时间等。

既然seo是网站的收录,那么它和数量有直接关系,原因是由于网站的搜索效果的概率。更何况长尾关键词,长尾关键词的排名优化会直接影响长尾关键词的排名次数,网页数据抓取而网站的排名有数百个因素影响网站收录。
那我给大家讲讲影响网站收录的因素,首先是网站的开启速度,网站的开启速度大家都知道速度不仅影响你的网站,对用户体验也不利。网页数据爬取,那你觉得如果网站打开慢会影响蜘蛛的阅读,所以网站打开速度很重要,关于影响网站@打开速度> 原因包括服务器提供商、服务器宽带速度、服务器硬件质量、服务器操作系统、服务器软件操作、DNS等。
接下来是网站权重的影响。每个人可能都不知道网站的重量。这个权重问题将直接关系到搜索引擎对网站的信誉值的评价。 网站 如果权重高,那么搜索引擎的抓取会更及时,有时会秒到。这也是由于该站点的权重相对较高。相反,如果网站的权重很低,则证明搜索引擎对网站的声誉值的评价很低。蜘蛛对站点的爬取频率会低,一旦站点正常,灰腕很可能直接被K驻扎。
其次要说一下网站结构的结构设置,结构外观美观,小场地多规划为平面结构,大场地多规划为树结构。网页数据抓取是我们的网站结构和规划复杂,复杂的内部链规划会导致网站收录慢,尤其是二级栏目和三级内容页面收录 甚至更慢甚至没有网站 @收录。比如我们要建一栋楼,每一层都要一样,每一层的结构都要非常坚固。因此,当我们规划网站的构建时,不同的列可以直接链接内部链。同时网站的目录深度最好限制在三层,以免影响蜘蛛对收录爬得太深。
免费:如何推广网站维护托管蜘蛛抓取爬虫

如果一个网站想要排名好,肯定有蜘蛛在爬。蜘蛛爬网以获取它想要的信息。 网站 的链接起着非常重要的作用。如果要增加网站,就必须增加蜘蛛爬行的频率。那么如何在网站维护、托管和维护中增加蜘蛛爬取的频率呢?首先我们要了解影响蜘蛛爬取频率的因素: 1网站结构:网站在构造时,首先选择短域名,简化目录层次,避免网址过长或动态参数太多,短的更容易记住。 2 页面速度:百度多次提到的移动优先级指标,最重要的是页面的首次加载速度,必须控制在3秒以内。 3 入站链接:理论上只要是外部链接,无论其质量和形状如何,都可以引导爬虫进行爬取。 4 主动提交:主动提交网站地图、JS访问、官方API等,接下来看看如何提高网站维护主机维护百度蜘蛛爬虫抓取频率:1、网站更新频率:网站只要更新频率越高,爬虫就会越多。 2. 网站重量:重 网站 轨道也经常抓取。 3. 网站内容质量:网站内容新颖优质,可以解决用户的问题,提高爬虫的爬取频率。 4、外链和内链的构建:链接是页面的入口,优质的外链和内链可以更好的引导蜘蛛进入和捕获。希望小编分享的关于网站维护主机维护增加蜘蛛爬取频率的内容可以对大家有所帮助。

解决方案:搜索引擎页面抓取方式?(附网站结构应该怎样部署才能实现双赢呢)
网站优化 • 优采云 发表了文章 • 0 个评论 • 94 次浏览 • 2022-09-23 11:09
8.2 搜索引擎抓取重要页面 搜索引擎如何抓取网站中比较重要的页面。
由于互联网信息量巨大,为了向用户展示更有价值的信息,搜索引擎会优先抓取每个网站@中比较重要的页面(即权限较高的页面) >。但是,搜索引擎是如何发现这些相对重要的页面的呢?根据重要页面的链接指向的页面也可能是重要页面的思路,搜索引擎会首先从权限比较高的页面(即源页面1)开始,跟踪其中的链接,从而去爬取其他比较重要的页面(即目标页面1)页面1)。
1 在链接关系中,我们称存储链接的页面为源页面,链接指向的页面为目标页面。
1 在链接关系中,我们称存储链接的页面为源页面,链接指向的页面为目标页面。
例如,一般情况下,搜索引擎会从网站的首页开始,跟随其中的链接,爬取网站中其他比较重要的页面。
由此我们了解到,增加页面成为收录的机会的最佳方法是缩短页面与重要页面之间的链接路径。
站长在搭建网站的时候,会接触到网站的各种元素。其中,网站结构是一个非常重要的环节。关于网站结构不否认有些人会直接忽略,但是网站结构可以涉及到网站的很多方面,在网站的优化中,关于网站结构的优化也是必不可少的。有的站长甚至将结构优化划分为单独的优化重点。可见,网站结构的重要性我们就不用多说了。 网站后期收录包括用户体验好不好很大程度上取决于网站的结构。如果网站遵循用户的操作习惯,符合搜索引擎的爬取状态,那么网站自然会受到用户的喜爱,所以网站应该结构清晰,导航简洁,方便用户找到在最短的时间内获得相应的内容,还可以促进搜索引擎对站点信息和收录的爬取。聊了这么久,我们应该如何部署网站结构才能实现双赢呢?别着急,往下看。
首先要说的是网站构造前的排版问题。每个站长对于网站的排版都有自己的想法,但有些是为了突出网站的区别个性化,违反用户操作习惯在网站上部署,这样的行为会适得其反首先要保证用户打开页面的第一印象不会恶心,这样网站才有机会留住用户,包括前期的页面加载时间。如果时间过长,用户可以直接关闭页面。电路板的布局应根据用户习惯进行设计。一个与用户体验背道而驰的网站,必然要进行有效的改进。
我们在上面谈到了页面加载速度的问题。造成这种现象的因素很多。其中最重要的因素之一是在 网站 页面中部署了大量的 image flash 格式的文件。虽然网站的视觉效果有了一定的提升,但是由于文件比较大,网页的加载时间也受到了影响,包括网站JS等,建议减少尽可能多的这样的文件。使用div+css更好的迎合搜索引擎的爬取,包括网站的404页等,可以在一定程度上提升网站的用户体验。
接下来说一个常见的,就是网站的面包屑导航。相信大家都接触过这种导航方式。面包屑导航有优点也有缺点。 网站简洁明了的结构是为了让用户在最短的时间内找到自己需要的信息,所以面包屑导航可以引导用户方便快捷地浏览网站,告知用户当前位置等信息,这样用户就不会在网站上迷路。通过面包屑导航的部署,用户可以清楚的知道自己的页面在整个网站中的具体位置,也方便用户返回上一级。另一方面,也能很好地迎合搜索引擎对网站信息的抓取。蜘蛛可以通过面包屑导航路径一一抓取网站信息,从而达到促进网站收录改进的目的,所以面包屑导航也是一个重要且不可缺少的功能站长优化网站.
<p>最后说一下网站框架的问题。大家一定听说过,在网站的优化中尽量避免框架的出现会影响收录的问题。错了,对于页面中的某个block,frame是固定的,其他部分的信息可以通过滚动点来显示。虽然框架使用起来很方便,但是在这样的页面中,搜索引擎无法抓取到更多的信息,所以会影响到网站整体收录,建议在 查看全部
解决方案:搜索引擎页面抓取方式?(附网站结构应该怎样部署才能实现双赢呢)
8.2 搜索引擎抓取重要页面 搜索引擎如何抓取网站中比较重要的页面。
由于互联网信息量巨大,为了向用户展示更有价值的信息,搜索引擎会优先抓取每个网站@中比较重要的页面(即权限较高的页面) >。但是,搜索引擎是如何发现这些相对重要的页面的呢?根据重要页面的链接指向的页面也可能是重要页面的思路,搜索引擎会首先从权限比较高的页面(即源页面1)开始,跟踪其中的链接,从而去爬取其他比较重要的页面(即目标页面1)页面1)。
1 在链接关系中,我们称存储链接的页面为源页面,链接指向的页面为目标页面。
1 在链接关系中,我们称存储链接的页面为源页面,链接指向的页面为目标页面。
例如,一般情况下,搜索引擎会从网站的首页开始,跟随其中的链接,爬取网站中其他比较重要的页面。

由此我们了解到,增加页面成为收录的机会的最佳方法是缩短页面与重要页面之间的链接路径。
站长在搭建网站的时候,会接触到网站的各种元素。其中,网站结构是一个非常重要的环节。关于网站结构不否认有些人会直接忽略,但是网站结构可以涉及到网站的很多方面,在网站的优化中,关于网站结构的优化也是必不可少的。有的站长甚至将结构优化划分为单独的优化重点。可见,网站结构的重要性我们就不用多说了。 网站后期收录包括用户体验好不好很大程度上取决于网站的结构。如果网站遵循用户的操作习惯,符合搜索引擎的爬取状态,那么网站自然会受到用户的喜爱,所以网站应该结构清晰,导航简洁,方便用户找到在最短的时间内获得相应的内容,还可以促进搜索引擎对站点信息和收录的爬取。聊了这么久,我们应该如何部署网站结构才能实现双赢呢?别着急,往下看。
首先要说的是网站构造前的排版问题。每个站长对于网站的排版都有自己的想法,但有些是为了突出网站的区别个性化,违反用户操作习惯在网站上部署,这样的行为会适得其反首先要保证用户打开页面的第一印象不会恶心,这样网站才有机会留住用户,包括前期的页面加载时间。如果时间过长,用户可以直接关闭页面。电路板的布局应根据用户习惯进行设计。一个与用户体验背道而驰的网站,必然要进行有效的改进。
我们在上面谈到了页面加载速度的问题。造成这种现象的因素很多。其中最重要的因素之一是在 网站 页面中部署了大量的 image flash 格式的文件。虽然网站的视觉效果有了一定的提升,但是由于文件比较大,网页的加载时间也受到了影响,包括网站JS等,建议减少尽可能多的这样的文件。使用div+css更好的迎合搜索引擎的爬取,包括网站的404页等,可以在一定程度上提升网站的用户体验。

接下来说一个常见的,就是网站的面包屑导航。相信大家都接触过这种导航方式。面包屑导航有优点也有缺点。 网站简洁明了的结构是为了让用户在最短的时间内找到自己需要的信息,所以面包屑导航可以引导用户方便快捷地浏览网站,告知用户当前位置等信息,这样用户就不会在网站上迷路。通过面包屑导航的部署,用户可以清楚的知道自己的页面在整个网站中的具体位置,也方便用户返回上一级。另一方面,也能很好地迎合搜索引擎对网站信息的抓取。蜘蛛可以通过面包屑导航路径一一抓取网站信息,从而达到促进网站收录改进的目的,所以面包屑导航也是一个重要且不可缺少的功能站长优化网站.
<p>最后说一下网站框架的问题。大家一定听说过,在网站的优化中尽量避免框架的出现会影响收录的问题。错了,对于页面中的某个block,frame是固定的,其他部分的信息可以通过滚动点来显示。虽然框架使用起来很方便,但是在这样的页面中,搜索引擎无法抓取到更多的信息,所以会影响到网站整体收录,建议在
jquery抓取网页内容有两种实现:+axios来网页
网站优化 • 优采云 发表了文章 • 0 个评论 • 147 次浏览 • 2022-09-20 12:18
jquery抓取网页内容有两种实现:
1、继承webqueryinternet.gethtmlstream('js')的prequery方法,根据传入的代码请求页面内容。
2、第三方js库,把页面内容以html格式合成到js格式中,再合成回string。从而实现网页抓取功能。抓取js部分要抓取jquery源码,这个太难了,一般是要自己写脚本去解析,这样的效率并不高。现在可以用ajax实现,但ajax抓取只抓取js,对css提取没有办法。所以采用phantomjs+axios来抓取网页内容。
首先我们要先在浏览器中创建一个窗口,我将创建一个名叫phantomjs的浏览器,代码为constphantomjs=newwebdriver。phantomjs({profileurl:phantomjs。url。create('phantomjs'),pages:[],documentopen:function(request,response){console。log('请求'+request。url);}});然后在web浏览器中编写网页内容抓取方法。
1、网址采用get方法:constqueryobject={//获取本页所有的html元素//1个object.link结尾是指该内容是一个html元素。不支持长锚链接}constresult={};//数据url是指网页urlconstdataurl={//获取网页源码信息fileurl:'/webstorm/sharp_xml.js'}constmyheader='title'constresultheader={}//获取网页全部内容fromjson.node_env.array.map(constobj=newdate('y-m-d-1'),consttype='string',constpageslink={maxratio:type.getratio(),maxrows:0,maxly:0,alllocations:type.getratio(),origin:type.getratio(),relativeregion:0,followregion:1,origin:type.getratio(),dropload:type.getratio(),nocached:null,useragent:null,scrollfilename:'',//获取地址filename:'',//去掉隐藏的parent字段usercontent:'',//去掉默认图片字段pageurl:''})//获取第一页内容fromjson.node_env.array.map(constobj=newdate('y-m-d-1'),constclienturl='',constcontenturl='')//设置username=''exports.clienturl='/webstorm/sharp_xml.js'exports.clienturl=''exports.myheader=''exports.m。 查看全部
jquery抓取网页内容有两种实现:+axios来网页
jquery抓取网页内容有两种实现:

1、继承webqueryinternet.gethtmlstream('js')的prequery方法,根据传入的代码请求页面内容。
2、第三方js库,把页面内容以html格式合成到js格式中,再合成回string。从而实现网页抓取功能。抓取js部分要抓取jquery源码,这个太难了,一般是要自己写脚本去解析,这样的效率并不高。现在可以用ajax实现,但ajax抓取只抓取js,对css提取没有办法。所以采用phantomjs+axios来抓取网页内容。

首先我们要先在浏览器中创建一个窗口,我将创建一个名叫phantomjs的浏览器,代码为constphantomjs=newwebdriver。phantomjs({profileurl:phantomjs。url。create('phantomjs'),pages:[],documentopen:function(request,response){console。log('请求'+request。url);}});然后在web浏览器中编写网页内容抓取方法。
1、网址采用get方法:constqueryobject={//获取本页所有的html元素//1个object.link结尾是指该内容是一个html元素。不支持长锚链接}constresult={};//数据url是指网页urlconstdataurl={//获取网页源码信息fileurl:'/webstorm/sharp_xml.js'}constmyheader='title'constresultheader={}//获取网页全部内容fromjson.node_env.array.map(constobj=newdate('y-m-d-1'),consttype='string',constpageslink={maxratio:type.getratio(),maxrows:0,maxly:0,alllocations:type.getratio(),origin:type.getratio(),relativeregion:0,followregion:1,origin:type.getratio(),dropload:type.getratio(),nocached:null,useragent:null,scrollfilename:'',//获取地址filename:'',//去掉隐藏的parent字段usercontent:'',//去掉默认图片字段pageurl:''})//获取第一页内容fromjson.node_env.array.map(constobj=newdate('y-m-d-1'),constclienturl='',constcontenturl='')//设置username=''exports.clienturl='/webstorm/sharp_xml.js'exports.clienturl=''exports.myheader=''exports.m。
用Python轻松制作一个股票K线图网站
网站优化 • 优采云 发表了文章 • 0 个评论 • 148 次浏览 • 2022-09-18 10:48
在前面的文章中,我们学习了如何使用 Tkinter 构建股票数据抓取以及展示K线图功能,虽然大致的功能已经具备,但是在当今这个人手一个 Web 服务的年代,GUI 程序还是没有 Web 服务来的香啊。
我们需要用到的知识包括 PyEcharts 的使用,tushare 库获取股票数据的方法以及 Flask 的基本用法。
获取股票数据
我们先来看下 tushare 的使用,这个应该是当前最为流行的股票数据库了吧,一行代码,就能轻松获取某支股票的历史数据
import tushare as ts<br />df = ts.get_hist_data('000001')<br />print(df)<br />
现在股票的历史数据有了,我们还需要一份股票名称和股票代码的对应表,同样通过 tushare 来获取
stock_list = ts.get_stock_basics()<br />stock_list.reset_index(inplace=True)<br />stock_list[['code', 'name']].to_csv('stock_code_name.csv')<br />
这样就成功保存了一份股票名称和股票代码的对应数据
PyEcharts 作图
下面再来看看如何通过 PyEcharts 来制作 K 线图,其实官网上的例子已经非常具体了,我们只需要把拿到的历史股票数据做些简单处理即可,我这里直接给我的数据处理过程
mydate = df[:30].index.tolist()<br />mydata = df[:30][['open', 'close', 'low', 'high']].values.tolist()<br /><br /><br />def kline_base(mydate, data) -> Kline:<br /> c = (<br /> Kline()<br /> .add_xaxis(mydate)<br /> .add_yaxis("kline", data)<br /> .set_global_opts(<br /> yaxis_opts=opts.AxisOpts(is_scale=True,<br /> splitarea_opts=opts.SplitAreaOpts(<br /> is_show=True, areastyle_opts=opts.AreaStyleOpts(opacity=1)<br /> ),<br /> ),<br /> xaxis_opts=opts.AxisOpts(is_scale=True,<br /> axislabel_opts=opts.LabelOpts(rotate=-30)),<br /> title_opts=opts.TitleOpts(title="Kline-基本示例"),<br /> datazoom_opts=[opts.DataZoomOpts()],<br /> )<br /> )<br /> return c<br /><br />kline_base(mydate, mydata).render_notebook()<br />
这样就可以得到一个不错的 K 线图了
下面我们就可以着手完成 Flask 的代码啦
构建 Web 框架
首先我们先来完成 Web 框架的整体编写,为了页面的美观与编码的方便,直接使用 bootstrap 来构建前端页面
视图函数编写
首先完成初始化工作,在项目目录下创建一个 app.py 文件
from flask import Flask, render_template, request<br />from pyecharts import options as opts<br />from pyecharts.charts import Kline<br />import tushare as ts<br />import pandas as pd<br />from flask_bootstrap import Bootstrap<br /><br />app = Flask(__name__)<br />bootstrap = Bootstrap(app)<br />
导入需要用到的库,并完成 flask app 的初始化工作。
接下来再写一个 404 的视图函数,统一处理所有的 Not Found 页面
@app.errorhandler(404)<br />def page_not_found(e):<br /> return render_template("404.html"), 404<br />
接着我们绑定根地址到 index 视图函数上,返回到 index.html 模板文件上
@app.route("/")<br />def index():<br /> return render_template("index.html")<br />
模板编写
在同级目录创建一个 templates 文件夹,创建三个 HTML 文件,分别为 404.html,base.html 和 index.html
base.html 是所有其他页面 HTML 模板的母模板
{% extends "bootstrap/base.html" %}<br /><br />{% block title %}我的股票走势网站{% endblock %}<br /><br /><br />{% block navbar %}<br /><br /> <br /> <br /> <br /> Toggle navigation<br /> <br /> <br /> <br /> <br /> Stock-Data<br /> <br /> <br /> <br /> Home<br /> <br /> <br /> <br /><br />{% endblock %}<br /><br />{% block content %}<br /><br /> {% block page_content %}<br /> {% endblock %}<br /><br />{% endblock %}<br />
创建一个导航栏,并定义相关的 block 内容
接下来编写 404.html 文件,展示非法 url 请求地址时的页面
{% extends "base.html" %}<br /><br />{% block title %}Page Not Found{% endblock %}<br /><br />{% block page_content %}<br /><br /> Not Found<br /><br />{% endblock %}<br />
对于 index.html 文件,就是我们需要展示 K 线图的页面,我们后面再处理。
编辑主逻辑
首先编写一个检查股票正确性的函数
def check_stock(code):<br /> n = 0<br /> l = []<br /> stock_code = pd.read_csv("stock_code_name.csv", dtype=object)<br /> stock_code.drop('Unnamed: 0', axis=1, inplace=True)<br /> stock_list = stock_code.values.tolist()<br /> for i in stock_list:<br /> if code in i:<br /> n += 1<br /> l = i<br /> else:<br /> continue<br /> return n, l<br />
如果股票正确,则返回 n=1,否则返回 n=0
接下来再编写获取股票数据的函数
def get_stock_data(code, ctime):<br /> df = ts.get_hist_data(code)<br /> mydate = df[:ctime].index.tolist()<br /> mydata = df[:ctime][['open', 'close', 'low', 'high']].values.tolist()<br /> return [mydate, mydata]<br />
下面就是把 PyEcharts 集成到 Flask 应用了,可以按照官方的教程走,把 PyEcharts 的样式文件等拷贝到自己的 templates 目录下,再编写一个用于调用 kline_base() 函数的视图函数
@app.route("/Kline", methods=['GET', 'POST'])<br />def get_kline_chart():<br /> stock_name = request.form.get('stockName')<br /> query_time = request.form.get('queryTime')<br /> if not stock_name:<br /> stock_name = '平安银行'<br /> if not query_time:<br /> query_time = 30<br /> status, stock_code = check_stock(stock_name)<br /> if status == 0:<br /> return 'error stock code or name'<br /> mydate, mydata = get_stock_data(stock_code[0], int(query_time))<br /> c = kline_base(mydate, mydata, stock_code[1])<br /> return c.dump_options()<br />
首先通过 request 变量获取到前端传递过来的数据,分别为 stockName 和 queryTime,如果这两个参数是空值时,则赋予它们一个默认值。
接着判断股票代码的正确性并获取股票历史数据。
最后调用 kline_base 函数画出 K 线图,并渲染到前端页面上。
前端页面编写
最后我们来完成前端页面的工作
首先定义一个表单,用于传递股票名称,查询时间
<br /> 股票名称:<br /> <br /> <br /> 查询时间:<br /> <br /> <br /> <br /> <br />
然后就是通过 JQuery 来动态获取数据
function getData() {<br /> var chart = echarts.init(document.getElementById('kline'), 'white', {renderer: 'canvas'});<br /> $.ajax({<br /> type: "POST",<br /> dataType: "json",<br /> url: "/Kline" ,<br /> data: $('#form1').serialize(),<br /> success: function (result) {<br /> chart.setOption(result);<br /> },<br /> error: function() {<br /> alert("错误的股票代码!");<br /> }<br /> });<br /> }<br />
最后我们看下整体的效果
是不是效果还不错呢,后面还可以继续添加功能来完善我们的小小网站!
【室长原理课】系列在不正经地科普一些互联网小知识,没有太多高深的内容,把这个系列分享给你的朋友吧!
喜欢此内容的人还喜欢
主线教程系列(有更新):
番外系列:
Pandas、Numpy系列:
数据可视化系列:
【Python教程】让图表的色彩更丰富!matplotlib中的colormap
办公自动化系列: 查看全部
用Python轻松制作一个股票K线图网站
在前面的文章中,我们学习了如何使用 Tkinter 构建股票数据抓取以及展示K线图功能,虽然大致的功能已经具备,但是在当今这个人手一个 Web 服务的年代,GUI 程序还是没有 Web 服务来的香啊。
我们需要用到的知识包括 PyEcharts 的使用,tushare 库获取股票数据的方法以及 Flask 的基本用法。
获取股票数据
我们先来看下 tushare 的使用,这个应该是当前最为流行的股票数据库了吧,一行代码,就能轻松获取某支股票的历史数据
import tushare as ts<br />df = ts.get_hist_data('000001')<br />print(df)<br />
现在股票的历史数据有了,我们还需要一份股票名称和股票代码的对应表,同样通过 tushare 来获取
stock_list = ts.get_stock_basics()<br />stock_list.reset_index(inplace=True)<br />stock_list[['code', 'name']].to_csv('stock_code_name.csv')<br />
这样就成功保存了一份股票名称和股票代码的对应数据
PyEcharts 作图
下面再来看看如何通过 PyEcharts 来制作 K 线图,其实官网上的例子已经非常具体了,我们只需要把拿到的历史股票数据做些简单处理即可,我这里直接给我的数据处理过程
mydate = df[:30].index.tolist()<br />mydata = df[:30][['open', 'close', 'low', 'high']].values.tolist()<br /><br /><br />def kline_base(mydate, data) -> Kline:<br /> c = (<br /> Kline()<br /> .add_xaxis(mydate)<br /> .add_yaxis("kline", data)<br /> .set_global_opts(<br /> yaxis_opts=opts.AxisOpts(is_scale=True,<br /> splitarea_opts=opts.SplitAreaOpts(<br /> is_show=True, areastyle_opts=opts.AreaStyleOpts(opacity=1)<br /> ),<br /> ),<br /> xaxis_opts=opts.AxisOpts(is_scale=True,<br /> axislabel_opts=opts.LabelOpts(rotate=-30)),<br /> title_opts=opts.TitleOpts(title="Kline-基本示例"),<br /> datazoom_opts=[opts.DataZoomOpts()],<br /> )<br /> )<br /> return c<br /><br />kline_base(mydate, mydata).render_notebook()<br />
这样就可以得到一个不错的 K 线图了
下面我们就可以着手完成 Flask 的代码啦
构建 Web 框架
首先我们先来完成 Web 框架的整体编写,为了页面的美观与编码的方便,直接使用 bootstrap 来构建前端页面
视图函数编写

首先完成初始化工作,在项目目录下创建一个 app.py 文件
from flask import Flask, render_template, request<br />from pyecharts import options as opts<br />from pyecharts.charts import Kline<br />import tushare as ts<br />import pandas as pd<br />from flask_bootstrap import Bootstrap<br /><br />app = Flask(__name__)<br />bootstrap = Bootstrap(app)<br />
导入需要用到的库,并完成 flask app 的初始化工作。
接下来再写一个 404 的视图函数,统一处理所有的 Not Found 页面
@app.errorhandler(404)<br />def page_not_found(e):<br /> return render_template("404.html"), 404<br />
接着我们绑定根地址到 index 视图函数上,返回到 index.html 模板文件上
@app.route("/")<br />def index():<br /> return render_template("index.html")<br />
模板编写
在同级目录创建一个 templates 文件夹,创建三个 HTML 文件,分别为 404.html,base.html 和 index.html
base.html 是所有其他页面 HTML 模板的母模板
{% extends "bootstrap/base.html" %}<br /><br />{% block title %}我的股票走势网站{% endblock %}<br /><br /><br />{% block navbar %}<br /><br /> <br /> <br /> <br /> Toggle navigation<br /> <br /> <br /> <br /> <br /> Stock-Data<br /> <br /> <br /> <br /> Home<br /> <br /> <br /> <br /><br />{% endblock %}<br /><br />{% block content %}<br /><br /> {% block page_content %}<br /> {% endblock %}<br /><br />{% endblock %}<br />
创建一个导航栏,并定义相关的 block 内容
接下来编写 404.html 文件,展示非法 url 请求地址时的页面
{% extends "base.html" %}<br /><br />{% block title %}Page Not Found{% endblock %}<br /><br />{% block page_content %}<br /><br /> Not Found<br /><br />{% endblock %}<br />
对于 index.html 文件,就是我们需要展示 K 线图的页面,我们后面再处理。
编辑主逻辑
首先编写一个检查股票正确性的函数
def check_stock(code):<br /> n = 0<br /> l = []<br /> stock_code = pd.read_csv("stock_code_name.csv", dtype=object)<br /> stock_code.drop('Unnamed: 0', axis=1, inplace=True)<br /> stock_list = stock_code.values.tolist()<br /> for i in stock_list:<br /> if code in i:<br /> n += 1<br /> l = i<br /> else:<br /> continue<br /> return n, l<br />
如果股票正确,则返回 n=1,否则返回 n=0
接下来再编写获取股票数据的函数
def get_stock_data(code, ctime):<br /> df = ts.get_hist_data(code)<br /> mydate = df[:ctime].index.tolist()<br /> mydata = df[:ctime][['open', 'close', 'low', 'high']].values.tolist()<br /> return [mydate, mydata]<br />

下面就是把 PyEcharts 集成到 Flask 应用了,可以按照官方的教程走,把 PyEcharts 的样式文件等拷贝到自己的 templates 目录下,再编写一个用于调用 kline_base() 函数的视图函数
@app.route("/Kline", methods=['GET', 'POST'])<br />def get_kline_chart():<br /> stock_name = request.form.get('stockName')<br /> query_time = request.form.get('queryTime')<br /> if not stock_name:<br /> stock_name = '平安银行'<br /> if not query_time:<br /> query_time = 30<br /> status, stock_code = check_stock(stock_name)<br /> if status == 0:<br /> return 'error stock code or name'<br /> mydate, mydata = get_stock_data(stock_code[0], int(query_time))<br /> c = kline_base(mydate, mydata, stock_code[1])<br /> return c.dump_options()<br />
首先通过 request 变量获取到前端传递过来的数据,分别为 stockName 和 queryTime,如果这两个参数是空值时,则赋予它们一个默认值。
接着判断股票代码的正确性并获取股票历史数据。
最后调用 kline_base 函数画出 K 线图,并渲染到前端页面上。
前端页面编写
最后我们来完成前端页面的工作
首先定义一个表单,用于传递股票名称,查询时间
<br /> 股票名称:<br /> <br /> <br /> 查询时间:<br /> <br /> <br /> <br /> <br />
然后就是通过 JQuery 来动态获取数据
function getData() {<br /> var chart = echarts.init(document.getElementById('kline'), 'white', {renderer: 'canvas'});<br /> $.ajax({<br /> type: "POST",<br /> dataType: "json",<br /> url: "/Kline" ,<br /> data: $('#form1').serialize(),<br /> success: function (result) {<br /> chart.setOption(result);<br /> },<br /> error: function() {<br /> alert("错误的股票代码!");<br /> }<br /> });<br /> }<br />
最后我们看下整体的效果
是不是效果还不错呢,后面还可以继续添加功能来完善我们的小小网站!
【室长原理课】系列在不正经地科普一些互联网小知识,没有太多高深的内容,把这个系列分享给你的朋友吧!
喜欢此内容的人还喜欢
主线教程系列(有更新):
番外系列:
Pandas、Numpy系列:
数据可视化系列:
【Python教程】让图表的色彩更丰富!matplotlib中的colormap
办公自动化系列:
jqueryjquery的方法有问题,我记得有个jquery,
网站优化 • 优采云 发表了文章 • 0 个评论 • 81 次浏览 • 2022-09-17 01:04
jquery抓取网页内容::$("#title").find("span").innerhtml()如果你想用bs3:$("#title").find("span").innerhtml().strip()
$('#title').find('span').children()
$('#title').find('span').children()即可。
如果你所想要抓取的数据不需要数据验证,那么建议你考虑用bs4来做,比较快。如果你不懂什么bs,那么可以使用jquery生成对象然后使用对象的属性接受值。
chrome...
jqueryscriptsrcexperimental下id为jquerylistdata的文件
用jquery就够了,
$('#title').find('span').strip()
jquery的方法有问题吧,我记得有个jquery,jquerytitleforlocalf...好像差不多都是这个意思,这个好像是google做的一个开源的解决你这个问题的框架。
$('#title').find('span').children()如果要防止抓取掉像github这样的网站的话建议上reverser。
$('#title').find('text').children()
html5里面id很重要的一个属性好像叫src 查看全部
jqueryjquery的方法有问题,我记得有个jquery,
jquery抓取网页内容::$("#title").find("span").innerhtml()如果你想用bs3:$("#title").find("span").innerhtml().strip()
$('#title').find('span').children()
$('#title').find('span').children()即可。

如果你所想要抓取的数据不需要数据验证,那么建议你考虑用bs4来做,比较快。如果你不懂什么bs,那么可以使用jquery生成对象然后使用对象的属性接受值。
chrome...
jqueryscriptsrcexperimental下id为jquerylistdata的文件
用jquery就够了,

$('#title').find('span').strip()
jquery的方法有问题吧,我记得有个jquery,jquerytitleforlocalf...好像差不多都是这个意思,这个好像是google做的一个开源的解决你这个问题的框架。
$('#title').find('span').children()如果要防止抓取掉像github这样的网站的话建议上reverser。
$('#title').find('text').children()
html5里面id很重要的一个属性好像叫src
jquery抓取网页内容用jquery抓取(标题1)_
网站优化 • 优采云 发表了文章 • 0 个评论 • 156 次浏览 • 2022-08-27 21:01
jquery抓取网页内容用jquery抓取网页内容,当用jquery。page()。getelementsbytagname('网页标题')或者jquery。page()。getelementsbytagname('标题1')。getelementsbytagname('标题2')即可获取到页面中所有标题,或使用jquery。
getelementsbytagname('标题1')。getelementsbytagname('标题2');。
简单说,jquery等,可以通过搜索一个关键字获取前面三页网页。更换关键字后每页只能抓取一页。
用jqueryscrapy抓取百度网页可以这样:1.定位页面,获取到每一个页面的url地址,然后用这个url开始构造urlhtml文件,这里我写一个简单的:#!/usr/bin/envpython#_*_coding:utf-8_*_importurllib.requestfrombs4importbeautifulsoupurl='/'netdata=urllib.request.urlopen(url)text=netdata.read()page=netdata.request(url=url)data=json.loads(text)2.html文件构造javascript文件构造:html.parser在web浏览器下可以用nodejs或者golang开发,可以通过文件构造实现抓取每一个html元素和css、js文件等。
<p>web页面抓取可以先用nodejs下的webpack插件,构建一个网页:'''@...defget_url(self):'''printstr(self.url)'''@...defget_html(self):'''printstr(self.html)'''page_list=json.loads(urllib.request.urlopen(self.url))'''@...defget_js(self):'''printstr(self.js)'''page_list=json.loads(urllib.request.urlopen(self.url))'''@...defget_xxx(self):'''printstr(self.xxx)'''returnstr(self.page_list)data=json.loads(text)'''可以利用javascript去加密data,然后存在一个js文件中:'''#def_w._webpage_page_policy(self):'''returnjsondata'''#def_w._webpage_policy(self):'''returnjsonjsdata_w=_w._webpage_policy(self)return_w3.接下来写一个模板语言'''/''' 查看全部
jquery抓取网页内容用jquery抓取(标题1)_
jquery抓取网页内容用jquery抓取网页内容,当用jquery。page()。getelementsbytagname('网页标题')或者jquery。page()。getelementsbytagname('标题1')。getelementsbytagname('标题2')即可获取到页面中所有标题,或使用jquery。

getelementsbytagname('标题1')。getelementsbytagname('标题2');。
简单说,jquery等,可以通过搜索一个关键字获取前面三页网页。更换关键字后每页只能抓取一页。

用jqueryscrapy抓取百度网页可以这样:1.定位页面,获取到每一个页面的url地址,然后用这个url开始构造urlhtml文件,这里我写一个简单的:#!/usr/bin/envpython#_*_coding:utf-8_*_importurllib.requestfrombs4importbeautifulsoupurl='/'netdata=urllib.request.urlopen(url)text=netdata.read()page=netdata.request(url=url)data=json.loads(text)2.html文件构造javascript文件构造:html.parser在web浏览器下可以用nodejs或者golang开发,可以通过文件构造实现抓取每一个html元素和css、js文件等。
<p>web页面抓取可以先用nodejs下的webpack插件,构建一个网页:'''@...defget_url(self):'''printstr(self.url)'''@...defget_html(self):'''printstr(self.html)'''page_list=json.loads(urllib.request.urlopen(self.url))'''@...defget_js(self):'''printstr(self.js)'''page_list=json.loads(urllib.request.urlopen(self.url))'''@...defget_xxx(self):'''printstr(self.xxx)'''returnstr(self.page_list)data=json.loads(text)'''可以利用javascript去加密data,然后存在一个js文件中:'''#def_w._webpage_page_policy(self):'''returnjsondata'''#def_w._webpage_policy(self):'''returnjsonjsdata_w=_w._webpage_policy(self)return_w3.接下来写一个模板语言'''/'''
豆瓣记者关爱:jquery抓取网页内容的一些技巧,放出链接
网站优化 • 优采云 发表了文章 • 0 个评论 • 74 次浏览 • 2022-08-27 06:05
jquery抓取网页内容是比较普遍常见的,但是对于javascript来说,也不是很熟悉,下面就由豆瓣记者关爱的匿名用户给我们讲解javascript抓取网页内容的一些技巧,这里就放出链接,请大家参看。
你说的是jquery里面的$('#book');的实现方式吧jquery$('#book').html();
这个问题其实是这样:是否存在一个链接,能够把左边文字链接到右边结构不一样的页面?分两种情况:1.如果左边是一个独立的html结构,而右边是一个html页面,那么就可以通过pjax爬取。2.如果左边是单页面的html页面,右边是页面中的js代码,
如果想获取链接为"javascript如何抓取页面url?"的话,
我知道dom4j官方提供jsobject可以实现你要的答案。如下图jsobject是否更改我代码这是下图可以看到jsobject是定义一对index和mainkey建立一对连接,每次循环其中一个去修改index,另一个获取页面。您可以自己验证,但是我更推荐您试试还有jqueryjquery=json={"index":"edit","mainkey":"/pjax/ajax_push"};jquery=json={"index":"ajax_push","mainkey":"public"};。 查看全部
豆瓣记者关爱:jquery抓取网页内容的一些技巧,放出链接
jquery抓取网页内容是比较普遍常见的,但是对于javascript来说,也不是很熟悉,下面就由豆瓣记者关爱的匿名用户给我们讲解javascript抓取网页内容的一些技巧,这里就放出链接,请大家参看。

你说的是jquery里面的$('#book');的实现方式吧jquery$('#book').html();
这个问题其实是这样:是否存在一个链接,能够把左边文字链接到右边结构不一样的页面?分两种情况:1.如果左边是一个独立的html结构,而右边是一个html页面,那么就可以通过pjax爬取。2.如果左边是单页面的html页面,右边是页面中的js代码,

如果想获取链接为"javascript如何抓取页面url?"的话,
我知道dom4j官方提供jsobject可以实现你要的答案。如下图jsobject是否更改我代码这是下图可以看到jsobject是定义一对index和mainkey建立一对连接,每次循环其中一个去修改index,另一个获取页面。您可以自己验证,但是我更推荐您试试还有jqueryjquery=json={"index":"edit","mainkey":"/pjax/ajax_push"};jquery=json={"index":"ajax_push","mainkey":"public"};。
jquery抓取网页内容文本分析(抓取)(图)
网站优化 • 优采云 发表了文章 • 0 个评论 • 122 次浏览 • 2022-08-25 07:02
jquery抓取网页内容文本分析打开网页看看需要爬取的文本,然后利用js实现代码解析用js去解析后面的代码,把爬取到的文本放到json格式中不同的li元素id代表着不同的文本元素,我们抓取一个文本是要抓取文本的一部分,可以把代码放到extends和meta中jsondataurllib(jsonobject,jsonfile)-jsonfileparser-pythononlinejsonfiledatamodel-pythondjangowebframework。
不需要nodejs,前端你自己去搞swiperjs可以爬取图片中的数据图片中的数据存入html文件中,直接用phpjavascriptsqlalchemy可以实现前端数据库库操作,
如果你不管什么语言,不管什么库,不管用过什么库。那你永远不可能获取数据。下面是我处理的图片地址。requests抓包看看。可以获取到基本数据图片内容里的头像imgurl,整体地址表格数据,
我可以知道,但是保证不了是否能获取出来,取决于你爬取数据的目的。如果你是针对一个服务器发起爬取请求,服务器是可以识别,并进行返回有效请求的。爬取到的数据可以为正则表达式或者html解析后的text,结构化的html数据。如果是要返回给客户端,因为ua(浏览器),ip等因素和ip是分段获取,爬取到的数据结构可能是随机,对一些网站来说,如果请求没有返回数据,可能会直接报错。所以,要深入理解一门语言,是否完全掌握要看你的爬取目的。仅此而已。谢谢。 查看全部
jquery抓取网页内容文本分析(抓取)(图)
jquery抓取网页内容文本分析打开网页看看需要爬取的文本,然后利用js实现代码解析用js去解析后面的代码,把爬取到的文本放到json格式中不同的li元素id代表着不同的文本元素,我们抓取一个文本是要抓取文本的一部分,可以把代码放到extends和meta中jsondataurllib(jsonobject,jsonfile)-jsonfileparser-pythononlinejsonfiledatamodel-pythondjangowebframework。

不需要nodejs,前端你自己去搞swiperjs可以爬取图片中的数据图片中的数据存入html文件中,直接用phpjavascriptsqlalchemy可以实现前端数据库库操作,

如果你不管什么语言,不管什么库,不管用过什么库。那你永远不可能获取数据。下面是我处理的图片地址。requests抓包看看。可以获取到基本数据图片内容里的头像imgurl,整体地址表格数据,
我可以知道,但是保证不了是否能获取出来,取决于你爬取数据的目的。如果你是针对一个服务器发起爬取请求,服务器是可以识别,并进行返回有效请求的。爬取到的数据可以为正则表达式或者html解析后的text,结构化的html数据。如果是要返回给客户端,因为ua(浏览器),ip等因素和ip是分段获取,爬取到的数据结构可能是随机,对一些网站来说,如果请求没有返回数据,可能会直接报错。所以,要深入理解一门语言,是否完全掌握要看你的爬取目的。仅此而已。谢谢。
jquery抓取网页内容原生支持模块化开发支持
网站优化 • 优采云 发表了文章 • 0 个评论 • 72 次浏览 • 2022-08-01 08:01
jquery抓取网页内容,原生支持模块化开发支持爬虫内置ajax,websocket设置jsonheader返回json数据jsheader简化js调试模块化就是没有一个插件的概念,也就是可以是动态加载的js文件,也可以是脚本,可以是全局变量或者对象等等。有axios组件,做动态连接。不要设置json地址,只获取json字符串,如果需要json字符串,用。json的json读取库加载数据,可以用其他库如json。parse。json。dump。
html新手入门数据在js里面设置,不在页面。 查看全部
jquery抓取网页内容原生支持模块化开发支持

jquery抓取网页内容,原生支持模块化开发支持爬虫内置ajax,websocket设置jsonheader返回json数据jsheader简化js调试模块化就是没有一个插件的概念,也就是可以是动态加载的js文件,也可以是脚本,可以是全局变量或者对象等等。有axios组件,做动态连接。不要设置json地址,只获取json字符串,如果需要json字符串,用。json的json读取库加载数据,可以用其他库如json。parse。json。dump。

html新手入门数据在js里面设置,不在页面。
jquery抓取网页内容 我质朴的梦想:个人网站
网站优化 • 优采云 发表了文章 • 0 个评论 • 74 次浏览 • 2022-07-30 14:29
我对个人网站的向往,大概就跟想要拥有一套自己的房子的心情是相通的:既可以随自己折腾安排,还可以接待客人——毕竟搞设计总得整理作品和履历。
虽然做网页不是什么很新鲜的事情,但如果想高度自定义,对于我这种既不是计算机专业的、也不是从小就热爱编程的人来说,仍然还是有一道技术的大山横亘在面前。
不过从大学时就多多少少有一些写Processing的经验,所以前阵子下决心学一学Web前端的东西,尽自己力所能及的程度实际地动手搭建自己的网站。
本文就是一篇关于我从初学状态开始,到做出阶段性成果来的开发过程记录。
心路历程与实现思路参半。
结尾有过程中参考资料的总结,可按需跳转。
后续还要填其他部分的坑,以后完成了再开放访问吧。
先放放做出来的东西
除了搭网站框架,主要就是做了主页部分的设计。
设计的意图很单纯:因为是“开水的主页”,所以想要给网页注入自己的灵魂(注水hhh),作品自然是水里的内容。以此想法为基础,考虑了“水”的感受如何表现,同时也想试着打破一下规矩的网格排列方式,所以做了这样的表现尝试。
实现上主要用到了HEXO搭网站框架,然后基于P5.js和Matter.js实现交互效果。
第1阶段:搭框架
怎样搭出一个网站来?
起初,把基础教程过了一遍之后,虽然理解了单个网页的效果是怎样实现的,但我有一个疑问仍然没有被解答:一个网站是由多个互相关联的网页组成的,总不能都一个一个写吧?
(WEB开发三剑客的分工)
记得一位做前端的朋友跟我说过,他们会用到VUE写框架。但我并没有立刻投入精力去了解学习VUE,因为我猜想专业开发者在工作中用到的东西,可能对我来说有点太牛刀小试了。
无非就是个人博客的程度,正想着应该有更精简一点能满足我的需求的做法,B站的算法就给我推了基于HEXO搭建个人博客,并部署到GitHubPage的内容——反正也不用花钱,于是我就抱着好奇心,跟着人家装上了。
整个安装和使用过程用的是命令行工具,装好之后就会生成一个默认的博客网站了。
(HEXO预览网页效果)
创建新文章、生成页面预览、部署到GitHub上等都是要通过在终端敲指令来操作,我还是第一次体验。好在以前也稍稍了解过一点MAC终端常用操作和Git的知识,所以到这里为止,还不算太懵逼。
(HEXO创建新文章)
很好,借助HEXO就可以简单地解决“未来需要不断地增加新内容”这个需求。
那么下一步当然就是搞装修了!
满头问号的旅程也随即真正开始。
第2阶段:从文件到效果
如何控制HEXO的主题风格?
首先,不管怎么说先换个干净顺眼的主题吧。
然后把内容填一填。
(只是把封面放进去,就还挺有样子了)
接着便打算研究研究主题文件,看看它是怎么弄出来这些效果的。
但当时我的认知程度仅仅是“定性的”:source文件夹里装的是文章内容,themes里装的是主题文件,其他全都不知道。
(当时我对HEXO的浅薄认知)
我原以为主题就是css的事情而已,然而一打开themes文件夹——小问号你是否有许多朋友。
layout 里的文件全是.ejs 格式,这是个什么格式?
为什么里面写的好像是HTML的内容,但又被一些不认识的语法格式包裹着??
为什么每一个文件都只有几行内容???
于是补充知识
HEXO的主题模板是怎么写出来的?
不知编程世界里是不是都默认大家知道各种东西的
HEXO官方文档只写出了文章变量的引用方法。
我主要靠跟着某篇文章实操了一遍+在油管上看了个细讲HEXO的教程,才理解了这些ejs文件的作用:ejs是一个模板语言(其实用的都是js语言),用它可以把网页的各个部分写成一个个单独的模块,然后按需引用模块来组合成不同的页面。(例如页头、页脚、内容分开写成三个文件)
(HEXO的主题文件夹里,ejs文件的结构关系示意)
此外读别人写的主题文件时,高频出现了类似于 col-xs-6 之类长得很像分子链,但意义不明的class表示方法。
继续补充知识
col-xs-6 这些是什么东西?
搜了搜,好家伙,原来这就是网页开发中用来控制内容排版的栅格系统工具!
终于出现了亲切的名词,忽然就get到了平面设计中学的网格系统和实际开发的联系。
这是一个名为Bootstrap的css框架,相当于别人提前写好了很多css样式,封装起来供开发者使用。
它最大的好处在于提高排版的效率,你不需要每次都从0开始写css样式来安排内容的位置了。只要在html里按规则写类名,就可以直接达成效果。这对响应式设计非常有帮助。
字母都是有语义的,比如col是列,xs是超小屏,最后的数字表示占多少列。col-xs-6就表示“在超小屏里,这部分内容占6列”。由于容器默认被分成12列,所以占6列相当于从左到右,占了一半的位置。
(响应式设计,内容的排版会根据窗口大小改变)
了解了这些,就基本搞懂了主题文件和网页效果之间的关系。
接着自己动动手实现了一些小需求:
说起来简单,实际做起来光是要搞懂要给哪个div标签加背景色,我也是弄了半个上午
(论夯实基础的重要性)
第3阶段:创意和实现怎样实现我想要的交互?搞明白主题文件和网页效果的关系之后,终于到了可以让创意介入的、让人开心的交互部分!网页的交互就要用上JavaScript,但js基础教程里搞的那些对我好像不太直接有用,我就把注意力转移到了P5.js上。P5.js是js的一个库,写法和Processing很像,可以很方便地“绘制”元素。
构想所以要做一个怎样的首页呢?开头也说过,因为是“开水的主页”,所以想要给网页注入灵魂(注水)。作品自然是水里的内容。所以如果能让它们像冰块那样拥有体积,会发生碰撞效果,可能会挺有意思的。
(灵魂草图)
所以从需求开始考虑,在大方向上,我知道至少需要做到这3件事情:将P5的画布插入到现在的主页里,且不影响原有的导航栏功能。要想让作品们能在P5的画布里被显示和打开,需要读到HTML里的数据(图片、标题、链接等),才能做进一步的操作。
要实现有体积碰撞的效果,需要借助物理库。
But我从来没写过p5,所以还是要补习一下。
稍微补充知识:P5.js的基础
教程当然就是去看Daniel Shiffman,看他敲代码真是快乐哈哈。顺便在这里推荐一下他的那个网站,除了P5和Processing,还有关于模拟自然系统的算法、机器学习等专题内容。整合得很好,比油管频道更方便查阅或开启新的学习。
()
弄懂p5怎么用之后,随便写个“随机Design”效果
试试把画板放进网页里,好,可以正常运行。
(好简陋hhhh没关系,千里之行始于足下。)如果有朋友也想试试自己用HEXO+P5.js来玩玩看,在这里顺便提醒一下HEXO里引用js文件的方法(引用css也同理):
接着就是要往画板里加入内容了。琢磨:如何在P5的画板里显示出这些作品?思路就是读取HTML文档,然后抓取需要的图片路径,再加载到P5里。我一开始以为这很顺利就能解决,但实操起来发现P5里竟然没有能直接读到HTML元素的src的方法
(如果有请告诉我)
于是就想到,既然这是是基于js的语言,那我在外面先用jQuery来获取一下数据,再通过全局变量来传入P5应该行得通。
试了之后确实可行,开心。于是之后的标题、时间、详情页链接我都是用这个方式来获取的。
(用jQuery主要是因为我觉得它的语法比较好读…但调用一个库却只干一件事情感觉挺铺张浪费的)
补充新知识:物理库Matter.js的用法。想要让我的图片们能发生自然的物理碰撞效果,得借助一下物理库的力量。Matter.js就是一个js的物理库,它可以在平面的世界里做出自然的物理效果。Daniel Shiffman也有对Matter.js的入门专题,总之就是继续和他学学学。
( matter.js官方动画 )
然后结合着实现了这样的效果。
不过物体落下来这种方式,显然很不“水”。所以稍微设置了一下重力,让这些块块都能往上漂,营造一种液体里的感觉。
定义“如何打开作品详情?”做到这里,就不得不开始考虑这个问题了。因为想要用户在首页可以点击拖拽这些块块随便玩玩,那“单击”就不能作为打开链接的触发指令。所以就想到使用拖拽到某个区域后松开手即打开的方式。顺便,考虑到访客不可能通过那小小的图片就知道每个块是什么内容,至少要把作品标题和时间信息传达出来。初步实现了这样的效果。
该有的信息和功能基本都有了,接下来就是增强反馈的表现效果。
反馈增强1 · 标题信息特效静止的标题出现和消失都太生硬了。我想要做出那种点击之后,读取数据,最后才变成最终信息的感觉。先另开了新文件里写了一个试试。
出来的效果并不复杂,但是编程逻辑上还挺锻炼到我的。(1) 编写标题特效以单个字符为单位写了类,然后以要显示的标题为基础创建实例,储存在数组里。动画分成入场、切换、退场三个状态,并且为了更细腻一点,字符之间的进退场还设定了一些随机的时间差。
(2)节约魔法消耗(运算资源)
Prototype在这个过程中还另外去补习了js里特有的prototype的概念。因为每个特效字的update和show的过程都是一样的,所以把这些function都写进了这个类的prototype里,就可以让实例们在演算时调用同一个function,从而达到节约运算资源的目的。和Processing里有的“继承”的概念很像,不过以前我都没用过,现学现用了。
整合代码因为特效字的代码是先在别的地方写的,所以还要整合到网页里并调用。整合有两种思路:显然第二种方法更为轻巧,节省资源。(实际上起初用了第一种方法,一运行就卡了…照理说就算这样粒子数也没有很多,或许是我还有其他的bug
)
(3)标题字大小自适应窗口宽度算法本身没啥好特别说的,只是有些细节在这里才留心注意到了。textSize( )表示的是字符的高度,单位是px。textWidth()测量的字符串宽度,是基于最近一次被应用的textSize算出来的。
反馈增强2·底部打开提示关于先前写好的这个功能,点击图片,窗口下方会弹出一个“拖拽至此处打开”的提示。
这个部分觉得可以做得更灵动一点,想到了既然是“水”的主题,不如就做个水波好了。水波的实现逻辑不难,核心就是用sin函数来绘制要波动的点,然后把这些顶点连成一个封闭图形就好。
研究三角函数为了更准确地理解并控制波形,还是单独花了一些时间去复习学习三角函数在编程绘图里的实际应用。果然自己归纳一遍还是很重要,考虑之后写一篇关于三角函数和PerlineNoise的理解(开始挖坑)。
优化后的提示栏,更有情绪。提示 “拖拽至此查看详情” 时比较平静淡定,
可以“松手打开”时比较活泼,有种雀跃的感觉哈哈。
把标题效果和底部提示整合进去之后就基本上也是最后的效果了。
反馈增强3·点按后,其他未激活物体缩小或半透明(技能点不足,未解决)图片的缩放是很简单,但没搞懂跟图片绑定的Matter.js的Body的缩放,到底是参照着什么来缩的。图片和Body的边界一直对不上
好吧,既然缩放我走不通,那做做图片透明度吧。结果搞透明要用的tint函数一加进去,程序就变得很卡,去查了查,别人还会提前创建一个graphic来装……有点嫌麻烦。
最后曲线救国,给了个半透明背景色的蒙版,意思一下它们没有被选中。算了算了,来日方长。
反馈增强4·点按后,在背景中显示大图(效果不满意,弃用)试了下给点按之后在大背景中也显示大图,还特地花了些功夫写了充满窗口尺寸和淡出淡入的效果,不过出来的效果感觉视线不知道该放哪里,于是弃用了。就单纯一点吧。
其他调试除了视觉效果之外,有些东西是要考虑到浏览器环境的问题。这部分主要做了图片块、物理边界、波浪宽度等自适应浏览器窗口大小。以及mouseReleased(松开鼠标)事件在手机端似乎不被识别,所以还要另外补写touch事件。
快乐结语主页开发的过程基本上就是这样了。新手如我,想要实现一个需求,就得去学一个新的东西,天天和问号Bug做朋友。要做成一件事情就是得不停地寻找答案。很需要耐心,解决的时候也真的很快乐。非专业或许在知识储备上有很多漏洞,但正因为这不是自己的专业,也没有人要求自己这么干,由兴趣驱动的钻研就很快乐了。
参考资料:
【油管web前端入门教程-优质熟肉】LearnCode.academy,翻译搬运:鱼C-小甲鱼,
【HEXO搭建教程】CodeSheep,手把手教你从0开始搭建自己的个人博客,
【油管优质HEXO教程】Mike Dane, Hexo - Static Site Generator Tutorial,【HEXO官方文档】【Bootstrap介绍】全局css样式 ,
【快乐学习创意编程TheCodingTrain】Daniel Shiffman,P5.js专题教程,Daniel Shiffman,Matter.js专题教程, Daniel Shiffman,简单正弦波运动,
真的有人看到了这里吗,太让我感动了。才知道2018年后注册的公众号不带留言功能了
如果你乐意给我按个赞,或是通过别的方式给我反馈,我会很开心~这也会成为我继续更新的动力之一。
感谢阅读,谢谢! 查看全部
jquery抓取网页内容 我质朴的梦想:个人网站
我对个人网站的向往,大概就跟想要拥有一套自己的房子的心情是相通的:既可以随自己折腾安排,还可以接待客人——毕竟搞设计总得整理作品和履历。
虽然做网页不是什么很新鲜的事情,但如果想高度自定义,对于我这种既不是计算机专业的、也不是从小就热爱编程的人来说,仍然还是有一道技术的大山横亘在面前。
不过从大学时就多多少少有一些写Processing的经验,所以前阵子下决心学一学Web前端的东西,尽自己力所能及的程度实际地动手搭建自己的网站。
本文就是一篇关于我从初学状态开始,到做出阶段性成果来的开发过程记录。
心路历程与实现思路参半。
结尾有过程中参考资料的总结,可按需跳转。
后续还要填其他部分的坑,以后完成了再开放访问吧。
先放放做出来的东西
除了搭网站框架,主要就是做了主页部分的设计。
设计的意图很单纯:因为是“开水的主页”,所以想要给网页注入自己的灵魂(注水hhh),作品自然是水里的内容。以此想法为基础,考虑了“水”的感受如何表现,同时也想试着打破一下规矩的网格排列方式,所以做了这样的表现尝试。
实现上主要用到了HEXO搭网站框架,然后基于P5.js和Matter.js实现交互效果。
第1阶段:搭框架
怎样搭出一个网站来?
起初,把基础教程过了一遍之后,虽然理解了单个网页的效果是怎样实现的,但我有一个疑问仍然没有被解答:一个网站是由多个互相关联的网页组成的,总不能都一个一个写吧?
(WEB开发三剑客的分工)
记得一位做前端的朋友跟我说过,他们会用到VUE写框架。但我并没有立刻投入精力去了解学习VUE,因为我猜想专业开发者在工作中用到的东西,可能对我来说有点太牛刀小试了。
无非就是个人博客的程度,正想着应该有更精简一点能满足我的需求的做法,B站的算法就给我推了基于HEXO搭建个人博客,并部署到GitHubPage的内容——反正也不用花钱,于是我就抱着好奇心,跟着人家装上了。
整个安装和使用过程用的是命令行工具,装好之后就会生成一个默认的博客网站了。
(HEXO预览网页效果)
创建新文章、生成页面预览、部署到GitHub上等都是要通过在终端敲指令来操作,我还是第一次体验。好在以前也稍稍了解过一点MAC终端常用操作和Git的知识,所以到这里为止,还不算太懵逼。
(HEXO创建新文章)
很好,借助HEXO就可以简单地解决“未来需要不断地增加新内容”这个需求。
那么下一步当然就是搞装修了!
满头问号的旅程也随即真正开始。
第2阶段:从文件到效果
如何控制HEXO的主题风格?
首先,不管怎么说先换个干净顺眼的主题吧。
然后把内容填一填。
(只是把封面放进去,就还挺有样子了)
接着便打算研究研究主题文件,看看它是怎么弄出来这些效果的。
但当时我的认知程度仅仅是“定性的”:source文件夹里装的是文章内容,themes里装的是主题文件,其他全都不知道。
(当时我对HEXO的浅薄认知)
我原以为主题就是css的事情而已,然而一打开themes文件夹——小问号你是否有许多朋友。
layout 里的文件全是.ejs 格式,这是个什么格式?
为什么里面写的好像是HTML的内容,但又被一些不认识的语法格式包裹着??
为什么每一个文件都只有几行内容???
于是补充知识

HEXO的主题模板是怎么写出来的?
不知编程世界里是不是都默认大家知道各种东西的
HEXO官方文档只写出了文章变量的引用方法。
我主要靠跟着某篇文章实操了一遍+在油管上看了个细讲HEXO的教程,才理解了这些ejs文件的作用:ejs是一个模板语言(其实用的都是js语言),用它可以把网页的各个部分写成一个个单独的模块,然后按需引用模块来组合成不同的页面。(例如页头、页脚、内容分开写成三个文件)
(HEXO的主题文件夹里,ejs文件的结构关系示意)
此外读别人写的主题文件时,高频出现了类似于 col-xs-6 之类长得很像分子链,但意义不明的class表示方法。
继续补充知识
col-xs-6 这些是什么东西?
搜了搜,好家伙,原来这就是网页开发中用来控制内容排版的栅格系统工具!
终于出现了亲切的名词,忽然就get到了平面设计中学的网格系统和实际开发的联系。
这是一个名为Bootstrap的css框架,相当于别人提前写好了很多css样式,封装起来供开发者使用。
它最大的好处在于提高排版的效率,你不需要每次都从0开始写css样式来安排内容的位置了。只要在html里按规则写类名,就可以直接达成效果。这对响应式设计非常有帮助。
字母都是有语义的,比如col是列,xs是超小屏,最后的数字表示占多少列。col-xs-6就表示“在超小屏里,这部分内容占6列”。由于容器默认被分成12列,所以占6列相当于从左到右,占了一半的位置。
(响应式设计,内容的排版会根据窗口大小改变)
了解了这些,就基本搞懂了主题文件和网页效果之间的关系。
接着自己动动手实现了一些小需求:
说起来简单,实际做起来光是要搞懂要给哪个div标签加背景色,我也是弄了半个上午
(论夯实基础的重要性)
第3阶段:创意和实现怎样实现我想要的交互?搞明白主题文件和网页效果的关系之后,终于到了可以让创意介入的、让人开心的交互部分!网页的交互就要用上JavaScript,但js基础教程里搞的那些对我好像不太直接有用,我就把注意力转移到了P5.js上。P5.js是js的一个库,写法和Processing很像,可以很方便地“绘制”元素。
构想所以要做一个怎样的首页呢?开头也说过,因为是“开水的主页”,所以想要给网页注入灵魂(注水)。作品自然是水里的内容。所以如果能让它们像冰块那样拥有体积,会发生碰撞效果,可能会挺有意思的。
(灵魂草图)
所以从需求开始考虑,在大方向上,我知道至少需要做到这3件事情:将P5的画布插入到现在的主页里,且不影响原有的导航栏功能。要想让作品们能在P5的画布里被显示和打开,需要读到HTML里的数据(图片、标题、链接等),才能做进一步的操作。
要实现有体积碰撞的效果,需要借助物理库。
But我从来没写过p5,所以还是要补习一下。
稍微补充知识:P5.js的基础
教程当然就是去看Daniel Shiffman,看他敲代码真是快乐哈哈。顺便在这里推荐一下他的那个网站,除了P5和Processing,还有关于模拟自然系统的算法、机器学习等专题内容。整合得很好,比油管频道更方便查阅或开启新的学习。
()
弄懂p5怎么用之后,随便写个“随机Design”效果
试试把画板放进网页里,好,可以正常运行。
(好简陋hhhh没关系,千里之行始于足下。)如果有朋友也想试试自己用HEXO+P5.js来玩玩看,在这里顺便提醒一下HEXO里引用js文件的方法(引用css也同理):
接着就是要往画板里加入内容了。琢磨:如何在P5的画板里显示出这些作品?思路就是读取HTML文档,然后抓取需要的图片路径,再加载到P5里。我一开始以为这很顺利就能解决,但实操起来发现P5里竟然没有能直接读到HTML元素的src的方法
(如果有请告诉我)
于是就想到,既然这是是基于js的语言,那我在外面先用jQuery来获取一下数据,再通过全局变量来传入P5应该行得通。

试了之后确实可行,开心。于是之后的标题、时间、详情页链接我都是用这个方式来获取的。
(用jQuery主要是因为我觉得它的语法比较好读…但调用一个库却只干一件事情感觉挺铺张浪费的)
补充新知识:物理库Matter.js的用法。想要让我的图片们能发生自然的物理碰撞效果,得借助一下物理库的力量。Matter.js就是一个js的物理库,它可以在平面的世界里做出自然的物理效果。Daniel Shiffman也有对Matter.js的入门专题,总之就是继续和他学学学。
( matter.js官方动画 )
然后结合着实现了这样的效果。
不过物体落下来这种方式,显然很不“水”。所以稍微设置了一下重力,让这些块块都能往上漂,营造一种液体里的感觉。
定义“如何打开作品详情?”做到这里,就不得不开始考虑这个问题了。因为想要用户在首页可以点击拖拽这些块块随便玩玩,那“单击”就不能作为打开链接的触发指令。所以就想到使用拖拽到某个区域后松开手即打开的方式。顺便,考虑到访客不可能通过那小小的图片就知道每个块是什么内容,至少要把作品标题和时间信息传达出来。初步实现了这样的效果。
该有的信息和功能基本都有了,接下来就是增强反馈的表现效果。
反馈增强1 · 标题信息特效静止的标题出现和消失都太生硬了。我想要做出那种点击之后,读取数据,最后才变成最终信息的感觉。先另开了新文件里写了一个试试。
出来的效果并不复杂,但是编程逻辑上还挺锻炼到我的。(1) 编写标题特效以单个字符为单位写了类,然后以要显示的标题为基础创建实例,储存在数组里。动画分成入场、切换、退场三个状态,并且为了更细腻一点,字符之间的进退场还设定了一些随机的时间差。
(2)节约魔法消耗(运算资源)
Prototype在这个过程中还另外去补习了js里特有的prototype的概念。因为每个特效字的update和show的过程都是一样的,所以把这些function都写进了这个类的prototype里,就可以让实例们在演算时调用同一个function,从而达到节约运算资源的目的。和Processing里有的“继承”的概念很像,不过以前我都没用过,现学现用了。
整合代码因为特效字的代码是先在别的地方写的,所以还要整合到网页里并调用。整合有两种思路:显然第二种方法更为轻巧,节省资源。(实际上起初用了第一种方法,一运行就卡了…照理说就算这样粒子数也没有很多,或许是我还有其他的bug
)
(3)标题字大小自适应窗口宽度算法本身没啥好特别说的,只是有些细节在这里才留心注意到了。textSize( )表示的是字符的高度,单位是px。textWidth()测量的字符串宽度,是基于最近一次被应用的textSize算出来的。
反馈增强2·底部打开提示关于先前写好的这个功能,点击图片,窗口下方会弹出一个“拖拽至此处打开”的提示。
这个部分觉得可以做得更灵动一点,想到了既然是“水”的主题,不如就做个水波好了。水波的实现逻辑不难,核心就是用sin函数来绘制要波动的点,然后把这些顶点连成一个封闭图形就好。
研究三角函数为了更准确地理解并控制波形,还是单独花了一些时间去复习学习三角函数在编程绘图里的实际应用。果然自己归纳一遍还是很重要,考虑之后写一篇关于三角函数和PerlineNoise的理解(开始挖坑)。
优化后的提示栏,更有情绪。提示 “拖拽至此查看详情” 时比较平静淡定,
可以“松手打开”时比较活泼,有种雀跃的感觉哈哈。
把标题效果和底部提示整合进去之后就基本上也是最后的效果了。
反馈增强3·点按后,其他未激活物体缩小或半透明(技能点不足,未解决)图片的缩放是很简单,但没搞懂跟图片绑定的Matter.js的Body的缩放,到底是参照着什么来缩的。图片和Body的边界一直对不上
好吧,既然缩放我走不通,那做做图片透明度吧。结果搞透明要用的tint函数一加进去,程序就变得很卡,去查了查,别人还会提前创建一个graphic来装……有点嫌麻烦。
最后曲线救国,给了个半透明背景色的蒙版,意思一下它们没有被选中。算了算了,来日方长。
反馈增强4·点按后,在背景中显示大图(效果不满意,弃用)试了下给点按之后在大背景中也显示大图,还特地花了些功夫写了充满窗口尺寸和淡出淡入的效果,不过出来的效果感觉视线不知道该放哪里,于是弃用了。就单纯一点吧。
其他调试除了视觉效果之外,有些东西是要考虑到浏览器环境的问题。这部分主要做了图片块、物理边界、波浪宽度等自适应浏览器窗口大小。以及mouseReleased(松开鼠标)事件在手机端似乎不被识别,所以还要另外补写touch事件。
快乐结语主页开发的过程基本上就是这样了。新手如我,想要实现一个需求,就得去学一个新的东西,天天和问号Bug做朋友。要做成一件事情就是得不停地寻找答案。很需要耐心,解决的时候也真的很快乐。非专业或许在知识储备上有很多漏洞,但正因为这不是自己的专业,也没有人要求自己这么干,由兴趣驱动的钻研就很快乐了。
参考资料:
【油管web前端入门教程-优质熟肉】LearnCode.academy,翻译搬运:鱼C-小甲鱼,
【HEXO搭建教程】CodeSheep,手把手教你从0开始搭建自己的个人博客,
【油管优质HEXO教程】Mike Dane, Hexo - Static Site Generator Tutorial,【HEXO官方文档】【Bootstrap介绍】全局css样式 ,
【快乐学习创意编程TheCodingTrain】Daniel Shiffman,P5.js专题教程,Daniel Shiffman,Matter.js专题教程, Daniel Shiffman,简单正弦波运动,
真的有人看到了这里吗,太让我感动了。才知道2018年后注册的公众号不带留言功能了
如果你乐意给我按个赞,或是通过别的方式给我反馈,我会很开心~这也会成为我继续更新的动力之一。
感谢阅读,谢谢!
用Node抓站(一):怎么写出自己满意的代码
网站优化 • 优采云 发表了文章 • 0 个评论 • 79 次浏览 • 2022-07-09 01:26
如果只写怎么抓取网页,肯定会被吐槽太水,满足不了读者的逼格要求,所以本文会通过不断的审视代码,做到令自己满意(撸码也要不断迸发新想法!
本文目标:抓取什么值得买网站国内优惠的最新商品,并且作为对象输出出来,方便后续入库等操作
抓取常用到的npm模块
本文就介绍两个:request和cheerio,另外lodash是个工具库,不做介绍,后面篇幅会继续介绍其他用到的npm库。
request 示例
<p>var request = require('request');
request('http://www.smzdm.com/youhui/', (err, req)=>{
if(!err){
console.log(Object.keys(req))
}
})</p>
通过上面的代码就看到req实际是个response对象,包括headers、statusCode、body等,我们用body就是网站的html内容
cheerio 示例
<p>var request = require('request')
var cheerio = require('cheerio')
cheerio.prototype.removeTagText = function () {
var html = this.html()
return html.replace(/ {
if (!err) {
var body = req.body
var $ = cheerio.load(body, {
decodeEntities: false
})
$('.list.list_preferential').each((i, item) => {
var $title = $('.itemName a', item)
var url = $title.attr('href')
var title = $title.removeTagText().trim()
var hl = $title.children().text().trim()
var img = $('img', item).attr('src')
var desc = $('.lrInfo', item).html().trim()
desc = desc.replace(/阅读全文/g, '')
var mall = $('.botPart a.mall', item).text().trim()
console.log({title, hl, url, img, desc, mall})
})
}
})</p>
简单解释下,removeTagText是直接扩展了cheerio的一个方法,目的是去掉类似
<p>再特价:QuanU 全友 布艺沙发组合2798元包邮(需定金99元,3.1付尾款)</p>
里面span之后的文字。执行后得到下面的结果:

怎么写出自己满意的代码
从上面需求来看,只需要提取列表页面的商品信息,而取到数据之后,使用cheerio进行了解析,然后通过一些「选择器」对数据进行「提取加工」,得到想要的数据。
重点是选择器和提取加工,如果想要的字段多了,那么代码会越写越多,维护困难,最重要的是「不环保」,今天抓什么值得买,明天抓惠惠网,代码还要copy一份改一改!一来二去,抓的越多,那么代码越乱,想想哪天不用request了,是不是要挨个修改呢?所以要抓重点,从最后需要的数据结构入手,关注选择器和提取加工。
handlerMap
从最后需要的数据结构入手,关注选择器和提取加工。我设计一种对象结构,作为参数传入,这个参数我起名:handlerMap,最后实现一个spider的函数,用法如下:
<p>spider(url, callback, handlerMap)</p>
从目标数据结构出发,最后数据什么样子,那么handlerMap结构就是什么样子,key就是最后输出数据的key,是由selector和handler两个key组成的对象,类似我们需要最后产出的数据是:
<p>[{
title: '',
ht: '',
url: '',
img: '',
mall: '',
desc: ''
}, {item2..}...]</p>
那么需要的handlerMap就是:
<p>{
title: {
selector: '.itemName a',
handler: 'removeTagText'
},
ht: {
selector: '.itemName a span',
handler: 'text'
},
url: {
selector: '.itemName a',
handler: 'atrr:href'
},
img: {
selector: 'img',
handler: 'attr:src'
},
mall: {
selector: '.botPart a.mall',
handler: 'text'
},
desc: {
selector: '.lrInfo',
handler: function (data){
return data.replace(/阅读全文/g, '')
}
}
}</p>
再酷一点,就是简写方法:url:".itemName a!attr:href”,另外再加上如果抓取的是JSON数据,也要一起处理的情况。经过分析之后,开始改造代码,代码最后分为了两个模块:
虽然增加不少代码工作量,但是抽象后的代码在使用的时候就更加方便了,自己还是别人在使用的时候,不用关心代码实现,只需要关注抓取的页面url、要提取的页面内容和数据得到后的继续操作即可,使用起来要比之前混杂在一起的代码更加清晰简洁;并且抓取任意页面都不需要动核心的代码,只需要填写前面提到的handlerMap。
总结
其实Node抓取页面很简单,本文只是通过一个简单的抓取任务,不断深入思考,进行抽象,写出自己满意的代码,以小见大,希望本文对读者有所启发
今天到此结束,完成一个基础抓取的库,有空继续介绍Node抓站的知识,欢迎大家交流讨论
本文的完整代码,在github/ksky521/mpdemo/ 对应文章名文件夹下可以找到
EOF三水清
2017年02月25日 查看全部
用Node抓站(一):怎么写出自己满意的代码
如果只写怎么抓取网页,肯定会被吐槽太水,满足不了读者的逼格要求,所以本文会通过不断的审视代码,做到令自己满意(撸码也要不断迸发新想法!
本文目标:抓取什么值得买网站国内优惠的最新商品,并且作为对象输出出来,方便后续入库等操作
抓取常用到的npm模块
本文就介绍两个:request和cheerio,另外lodash是个工具库,不做介绍,后面篇幅会继续介绍其他用到的npm库。
request 示例
<p>var request = require('request');
request('http://www.smzdm.com/youhui/', (err, req)=>{
if(!err){
console.log(Object.keys(req))
}
})</p>
通过上面的代码就看到req实际是个response对象,包括headers、statusCode、body等,我们用body就是网站的html内容
cheerio 示例
<p>var request = require('request')
var cheerio = require('cheerio')
cheerio.prototype.removeTagText = function () {
var html = this.html()
return html.replace(/ {
if (!err) {
var body = req.body
var $ = cheerio.load(body, {
decodeEntities: false
})
$('.list.list_preferential').each((i, item) => {
var $title = $('.itemName a', item)
var url = $title.attr('href')
var title = $title.removeTagText().trim()
var hl = $title.children().text().trim()
var img = $('img', item).attr('src')
var desc = $('.lrInfo', item).html().trim()
desc = desc.replace(/阅读全文/g, '')
var mall = $('.botPart a.mall', item).text().trim()
console.log({title, hl, url, img, desc, mall})
})
}
})</p>
简单解释下,removeTagText是直接扩展了cheerio的一个方法,目的是去掉类似

<p>再特价:QuanU 全友 布艺沙发组合2798元包邮(需定金99元,3.1付尾款)</p>
里面span之后的文字。执行后得到下面的结果:

怎么写出自己满意的代码
从上面需求来看,只需要提取列表页面的商品信息,而取到数据之后,使用cheerio进行了解析,然后通过一些「选择器」对数据进行「提取加工」,得到想要的数据。
重点是选择器和提取加工,如果想要的字段多了,那么代码会越写越多,维护困难,最重要的是「不环保」,今天抓什么值得买,明天抓惠惠网,代码还要copy一份改一改!一来二去,抓的越多,那么代码越乱,想想哪天不用request了,是不是要挨个修改呢?所以要抓重点,从最后需要的数据结构入手,关注选择器和提取加工。
handlerMap
从最后需要的数据结构入手,关注选择器和提取加工。我设计一种对象结构,作为参数传入,这个参数我起名:handlerMap,最后实现一个spider的函数,用法如下:
<p>spider(url, callback, handlerMap)</p>
从目标数据结构出发,最后数据什么样子,那么handlerMap结构就是什么样子,key就是最后输出数据的key,是由selector和handler两个key组成的对象,类似我们需要最后产出的数据是:

<p>[{
title: '',
ht: '',
url: '',
img: '',
mall: '',
desc: ''
}, {item2..}...]</p>
那么需要的handlerMap就是:
<p>{
title: {
selector: '.itemName a',
handler: 'removeTagText'
},
ht: {
selector: '.itemName a span',
handler: 'text'
},
url: {
selector: '.itemName a',
handler: 'atrr:href'
},
img: {
selector: 'img',
handler: 'attr:src'
},
mall: {
selector: '.botPart a.mall',
handler: 'text'
},
desc: {
selector: '.lrInfo',
handler: function (data){
return data.replace(/阅读全文/g, '')
}
}
}</p>
再酷一点,就是简写方法:url:".itemName a!attr:href”,另外再加上如果抓取的是JSON数据,也要一起处理的情况。经过分析之后,开始改造代码,代码最后分为了两个模块:
虽然增加不少代码工作量,但是抽象后的代码在使用的时候就更加方便了,自己还是别人在使用的时候,不用关心代码实现,只需要关注抓取的页面url、要提取的页面内容和数据得到后的继续操作即可,使用起来要比之前混杂在一起的代码更加清晰简洁;并且抓取任意页面都不需要动核心的代码,只需要填写前面提到的handlerMap。
总结
其实Node抓取页面很简单,本文只是通过一个简单的抓取任务,不断深入思考,进行抽象,写出自己满意的代码,以小见大,希望本文对读者有所启发
今天到此结束,完成一个基础抓取的库,有空继续介绍Node抓站的知识,欢迎大家交流讨论
本文的完整代码,在github/ksky521/mpdemo/ 对应文章名文件夹下可以找到
EOF三水清
2017年02月25日
新世纪Nerv战士 - 京东首页补完计划
网站优化 • 优采云 发表了文章 • 0 个评论 • 69 次浏览 • 2022-06-20 09:13
16版的京东首页,在性能、体验、灾备策略等各方面都做到了极致。站在如此高大的巨人肩上,除了满满的自信,我们心里更怕扑街。毫无疑问,我们在接到改版需求的那一刻,立马就敲定了新首页的技术选型:妥妥的jQuery+SeaJS!
但很快,我们就发现这样一点都不酷。jQuery是2006年的框架了,SeaJS停止维护也已经四年。这些项目的诞生都是为了解决当时业界的一些痛点:比如jQuery最开始是为了方便程序员在页面中操作DOM,绑定事件等;SeaJS则是为了在浏览器中实现CMD规范的模块开发和加载。但在各种VirtualDOM框架横飞的现在,程序员已经很少会直接操作DOM元素,而模块的开发和加载也有早已有了别的方案。
就在这时,Nerv项目的作者提出了建议:“不然用Nerv来一发?”我记得,当时他脸上洋溢着淳朴的笑,Nerv也仅仅是部门内部的一个小项目。我们回想了首页这个业务,技术栈已经好几年未曾更新过,开发流程也不够理想。如果再不做出改变,明年的这个时候我们依然会面对一堆陈年老代码头疼不已。抱着试一试的心态,我们接受了他的提议。没想到,这个决定让首页从此摆脱了落后的技术架构,而Nerv现在也已经成长为GitHub上3k+ Star的热门项目。
Q: 为什么不使用React/Preact/Vue?
A: 这三者都是前端圈子中相当流行的项目。React有完善的体系和浓厚的社区氛围,Preact有着羞涩的体积,Vue则使用了先进的html模板和数据绑定机制。但是,上边这三者都无法兼容IE8。我们在经过相关数据的论证后,发现IE8的用户还是有一定的价值,这才最终激发了我们团队内部自己造轮子的想法。当然,在造轮子的过程中,我们也不忘向上面这些优秀框架的看齐。最终,Nerv在完美兼容React语法的同时,具有着出众的性能表现,在Gzip后也只占用9Kb的体积。
整体架构
在这次的项目中,我们基于上一年久经考验的前端体系(详细介绍),进行了升级:
开发模式Athena2.0
1.0版本的Athena,基于vinyl-fs的流操作,或者说是类似于gulp的压缩、编译等等操作的任务流。而到了2017年,webpack早已在前端圈中流行。同行们也早已经习惯在项目中直接基于最新的语言特性去开发,在webpack.config.js加上一个babel-loader就可以完美支持新语法并完成打包。Athena 1.0背着太沉重的历史包袱,已经很难快速实现对babel转译的支持。所以在首页的开发前,我们将Athena升级到了全新的2.0版本。
一如既往,Athena会为项目提供init(初始化),serve(实时预览),build(编译),publish(发布)等功能。除此之外,由于2.0版本的Athena是基于webpack的,所以项目中可以统一用npm来管理依赖,也可以直接使用最新的ES语言特性来进行开发。
使用Athena2.0开发时,建议的文件架构如下:
前后端协作
我们依然是采用了前后端分离的协作模式,由后端给出json格式的数据,前端拉取json数据进行渲染。对于大部分的组件来说,都会在constructor中做好组件的初始化工作,在componentDidMount的生命周期中拉取数据写入组件的state,再通过render函数进行渲染。
代码规范约束
有一千个读者,就会有一千个哈姆雷特。
上面这句名言,深刻地体现在了16版首页的代码仓库中。同一个组件,如果是基于jQuery+SeaJS的模式,一千个程序猿就会有一千种写法。结果在同一个项目中,代码风格不尽相同,代码质量良莠不齐,多人协作也会无从下手。
何以解忧?唯有统一代码风格了。通过ESLint+Husky,我们对每次代码提交都做了代码风格检查,对是否使用prefer const的变量声明、代码缩进该使用Tab还是空格等等的规则都做了约束。一开始定下规范的时候,团队成员或多或少都会有些不习惯。但通过偷偷在代码里下毒Athena的生成的项目模板中添加对应的规则,潜移默化地,团队成员们也都开始接受、习惯这些约束。
禁用变量重声明等规则,在一定程度上保证了代码质量;而统一的代码样式风格,则使得项目的多人协作更加便利。
Q: 保证代码质量,促进多人协作的终极好处是什么?
A: 由于项目代码风格统一,通俗易懂容易上手,我们首页的开发团队终于开始有妹纸加入了!一群雄性程序猿敲代码能敲出什么火花啊…
对性能优化的探索首屏直出
直出可能是加快首屏加载最行之有效的办法了。它在减少页面加载时间、首屏请求数等方面的好处自然不必再提,结合jQuery,也可以很方便地在直出的DOM上进行更多的操作。
Nerv框架对于它内部的组件、DOM有着良好的操作性,但是对于体系外的DOM节点,却是天生的操作无力。举个例子,比如在页面文件中我们直出一个轮播图:
使用Nerv为这段HTML添加轮播逻辑,成为了非常艰难的操作。终极的解决方案,应该是使用SSR(Server Side Render)的方案,搭建Nerv-server中间层来将组件直出。但现在革命尚未成功,首屏直出尚且依赖后端的研发同学,首页上线又迫在眉睫。被逼急的我们最终选择了比较trick的方式来过渡这个问题:在组件初始化的时候先通过DOM操作获取渲染所需的数据,再将DOM替换成Nerv渲染后的内容。
代码分割
在生产环境中,随着代码体积增大,浏览器解压Gzip、执行等操作也会需要更多的开销。在SeaJS的时代,我们尚且会通过SeaJS.use或者require.async异步加载模块代码,避免一次性加载过多内容。但webpack的默认行为却会将整个页面的代码打包为一个单独的文件,这明显不是最佳的实践。对此,webpack给出的解决方案是动态引入(Dynamic Imports)。我们可以通过如下的代码来使用这个便利的特性:
与此同时,webpack会将使用了动态引入的组件从主bundle文件中抽离出来,这就减小了主bundle文件的体积。
对于我们的具体需求而言,需要做动态引入的一般是Nerv的组件。对于组件的动态引入,业界已经有非常好的实现方案react-loadable。
举个栗子,通过下面的代码,我们可以在页面中使用来实现对组件MyComponent的动态引入,并且具有加载超时、错误、加载中等不同状态的展示:
再进一步,我们希望对于屏幕外的组件,仅仅是在它进入用户视野后再开始加载,这也就是我们常说的滚动懒加载。这可以结合业界已有的懒加载组件react-lazyload来实现。针对上面的,在下面的例子中,只有进入用户屏幕后,MyComponent才会开始加载:
上面的例子为lazyload的组件设置了200px的占位高度。并且设定了占位元素的类名,方便设定样式。
代码延后加载
在给首页全面升级技术栈的时候,我们忽略了一个问题:页面上还引用着少量来自兄弟团队的SeaJS模块,我们升级了技术栈是可以,但是强迫兄弟团队也一起去掉SeaJS重构一遍代码,这就有点不合理了。我们也不能仅仅为了这部分模块,就把SeaJS给打包进代码里面,这也是不科学的。
上面讲到的动态引入功能,帮我们很好地解决了这个问题。我们在代码中单独抽离了一个legacy模块,其中包含了SeaJS、SeaJS-combo等老模块并做了导出。这部分代码在首屏中并不直接引入,而是在需要执行的时候,通过上面的动态引入功能,单独请求下来使用:
打包性能优化
webpack默认会对整个项目引用到的文件进行编译、打包,其中还包括了Nervjs、es5-polyfill等基础的依赖库。这些文件从加入项目开始,基本都不会再有任何更改;然而在每次构建新版本时,webpack打包的这些基础库都会与上一版本有一些细微的区别,这会导致用户浏览器中对应的代码缓存失效。为此,我们考虑将这些基础库分开打包。
针对这种需求,webpack官方建议使用DLL插件来优化。DLL是Dynamic Link Library的简称,是windows系统中对于应用程序依赖的函数库的称呼。对于webpack,我们需要使用一个单独的webpack配置去生成DLL:
接下来,在我们的项目的webpack配置中引用DllReferencdPlugin,传入上面生成的json文件:
这样就完成了动态链接库的生成和引用。除了最开始的一次编译,后续开发中如果基础库没有变动,DLL就再也不需要重新编译,这也就解决了上面的代码变动的问题。
体验优化探索兼容IE8
兼容旧版本IE浏览器一直是前端开发人员心中永远的痛。过去,我们使用jQuery去统一不同浏览器的DOM操作和绑定事件,通过jQuery元素实例的map、each等类数组函数批量做JavaScript动画,等等。
但是在使用Nerv之后,从体系外直接操作DOM就显得很不优雅;更推荐的写法,是通过组件的ref属性来访问原生DOM。而map、each等函数,IE9+的浏览器也已经在Array.prototype下有了相应的实现。如果我们在代码中直接引入jQuery,这肯定是不科学的,这将使页面的脚本体积提高许多,同时还引入了很多我们根本用不上的多余功能。
面对这种情况,我们做了一个仅针对ie8的轻量级的兼容库es5-polyfill。它包括这些实现:Object的扩展函数、ES5对Array.prototype的扩充、标准的addEventListener和removeEventListener等。在入口文件顶部使用require('es5-polyfill');引入es5-polyfill后,只需3分钟,你就会和我一样,爱上这款框架可以在代码中愉快地使用上面说到的那些IE8不支持的API了。
但是,通过上面的CMD方式引入不就意味着对于IE9+的用户都引入了这些代码吗?这并不符合我们“随用随取,避免浪费”的原则。我们更推荐的做法,是在webpack中为配置多个entry,再使用HTMLWebpackPlugin在HTML模板中为es5-polyfill输出一段针对IE8的条件注释。具体实现可以参考nerv-webpack-boilerplate。
SVG Sprite
在页面中使用SVG,可以有效提升小图标在高清屏中的体验。类似于图片Sprite,SVG也可以通过Sprite来减少页面的请求(参考文章:拥抱Web设计新趋势:SVG Sprites实践应用)。
举个栗子,我们在Nerv中声明svgSprite组件,用以存放页面中用到的svg小图标:
接下来,我们可以在页面中动态引入上面的svgSprite组件就可以了:
在页面中挂载后,我们即可使用形如的代码去引用相应的图标了。
数据大屏
除了用户,我们同样也关注运营人员的体验。如果可以将运营数据都以直观的图表展示,这对于运营同学、产品同学都是十分幸福的事。这次的首页改版,我们与数据方合作,为首页配套开发了数据大屏项目SEE,用于运营数据的实时滚动展示。
SEE基于Nerv+Redux开发,使用ECharts进行数据的可视化展示。除了线上数据,SEE还有专门针对开发人员需求的性能版大屏,实时展示开发人员关心的页面onload时间,接口成功率,js报错数等指标。我们也希望未来SEE可以在更多的业务中用起来。
页面可用性保障和监控
我们做了许多优化工作来提升页面在性能、体验上的优良表现。但如果页面出现了JS逻辑错误,或者展示有问题,前面的优化工作就都前功尽弃了。所以在保证项目进度的基础上,我们又做了一系列的工作来保证首页的安全与稳定。
统一上线
同一份代码,经过不同版本的开发工具进行编译、压缩,生成的文件可能会天差地别,这种情况在多人协作中是相当致命的。比如:开发人员A的代码使用了新版本开发工具的API,而无辜的开发人员B对此毫不知情,使用了老版本的开发工具进行编译和发布…说多了都是泪,又是一场人间悲剧。
为了消除差异,我们希望不同开发人员的开发环境保持严格统一,但这其实是难以保证的:除了开发工具版本不同,有时候windows下,macOS下甚至是Linux下的表现也是不一样的。
为了解决这个问题,我们将编译的工作挪到了服务器端。开发人员在本地进行开发、自测,联调通过后提交到代码仓库中,确认上线后,上线平台拉取项目的代码,使用服务器端的工具链进行编译、压缩、发布等工作。
此外,上线平台还提供上线代码diff功能,可以将待上线的文件与线上的版本进行diff,待开发人员确认完才能继续上线操作。
接入上线平台后,开发人员再也不必担心开发环境的差异影响了编译结果,也不会误操作将其他同事开发中的分支带上线。就算是出现了线上bug,开发人员也可以轻松地通过上线平台记录的git commitId进行精确快速的回滚,有效保障了页面的可用性。
自动化测试
我们注意到,每次上线迭代,在经过编译工具的压缩、组合后,都有可能会对代码中其他部分的代码造成影响。如果在测试时只验证了当前迭代的功能点,疏漏了原先其他功能点的验证,就有可能引起一些意想不到的BUG。传统的DOM元素监控并无法满足我们的需求,因为有的bug出现的时机是在一连串特定的操作后。所以我们认为,我们造轮子的时候又到了。我们需要在Athena监控体系中增加一套针对页面中各个功能点的自动进行验证测试的系统。
这个系统基于selenium webdriver搭建。在后台中,开发者针对CSS选择器配置一系列的动作链Actionchain,包括点击、hover、输入文字、拖拽等操作,再通过对指定CSS选择器的HTML属性、样式、值等因素指定一个预期结果。后台服务会定时打开页面,执行预设的操作,如果与预期结果有出入,就会触发告警。
这种单服务器运行的e2e测试,容易碰到一些偶然网络波动的影响而导致乱告警。事实上,我们刚开始跑这套服务的时候,我们经常收到告警,但事实上页面展示并没有任何问题。针对这种偶发情况,我们在验证的过程中加入了失败重试的机制。只有在连续3次测试状态都为fail的情况,才会触发告警。经过优化后,监控的准确性有了质的提升。
素材监控
在生产环境中,除了程序BUG,数据运营的一些不规范操作也有可能影响到用户的体验。举例来说,如果页面中的图片体积过于庞大,会导致页面的加载时间变长,用户等待的时间会更久;如果页面中图片尺寸不合规,会导致 图片展示不正常,出现拉伸/压缩等现象,页面就会给人很山寨的感觉了。
针对这些素材异常情况,我们部署了针对性的监控服务。开发者针对特定的CSS选择器配置图片的标准体积以及尺寸。监控服务定时开启headless浏览器抓取页面中的图片并判断是否符合规则,不符合就触发告警。通过这样的手段,我们可以第一时间知道页面上出现的超限素材,通知运营的同学修改。
实时告警
作为Athena前端体系的一环,我们接入了Athena系统用于收集首页各种性能以及用户环境相关的数据。在这套系统中,我们可以获取到用户的屏幕分辨率占比,浏览器占比,同时还有页面加载时间、接口成功率等性能数据。
基于这些数据,我们在发现问题时可以进行有针对性的优化,比如调整特定接口的等待时间,或者是调整特定请求的重试策略。但是,并没有谁会一整天对这些数据的仪表盘盯着看;等我们发现问题,可能已经是上线后第二天,在工位上吃着早餐喝着牛奶的时候了。
解决信息滞后的问题,加强消息触达是关键。我们强化了Athena监控平台的功能:除了平台上仪表盘直观的数据展示,还支持配置告警规则。在绑定告警接收人的微信号后,平台就可以通过部门公众号实时推送告警信息,真正做到24小时监控,360度无盲点触达。
更长远的探索
“如何做得更好?”这是一个永不过时的问题。以前我们觉得在页面使用CMD的模块加载体系非常酷,所以后来会在项目中使用SeaJS;去年我们渴求一次架构升级,所以我们今年用上了Nerv。今年我们又会渴望什么呢?
前后端同构
作为提升性能的一个捷径,代码同构、服务器端渲染是目前看来的终极解决方案。我们计划搭建中间层,同样使用Nerv来渲染,从而减少首屏的代码逻辑,这将对页面的加载速度有大幅度的优化。
引入强类型校验
除了更快,我们也希望能够更稳。作为弱类型语言,JavaScript有着强大的灵活性,数据类型的相互转换十分便利;但也由于各种不严谨的类型转换,代码中存在着大量不可预测的分支走向,容易出现undefined的报错,调用不存在的API等等。
强类型语言TypeScript从13年诞生到现在,已经十分成熟了。在类型推断、静态验证上,TypeScript明显会更胜一筹;而在减少了多余的类型转换之后,TypeScript的性能表现也比常规JavaScript更强。我们希望未来可以将TypeScript在项目中用起来,这对于提升页面可靠性和性能,是很有意义的。
总结
这篇文章从整体开发架构与模式,性能、体验优化的探索,页面可用性的保障等方面对京东首页的开发过程做了简单的介绍。之所以说简单,是因为短短的篇幅完全无法说完我们在开发期间的故事和感悟:许多问题的解决并不像上面讲的那样水到渠成;除此之外更是有一大堆深夜加班撸串的故事没有地方讲。
最后献上个照片,这是项目上线成功之后在公司拍的通宵证明。虽然现在会觉得这拍得真……丑,但是项目成功上线的喜悦之情,我相信屏幕前的你也一样可以感受到。
查看全部
新世纪Nerv战士 - 京东首页补完计划
16版的京东首页,在性能、体验、灾备策略等各方面都做到了极致。站在如此高大的巨人肩上,除了满满的自信,我们心里更怕扑街。毫无疑问,我们在接到改版需求的那一刻,立马就敲定了新首页的技术选型:妥妥的jQuery+SeaJS!
但很快,我们就发现这样一点都不酷。jQuery是2006年的框架了,SeaJS停止维护也已经四年。这些项目的诞生都是为了解决当时业界的一些痛点:比如jQuery最开始是为了方便程序员在页面中操作DOM,绑定事件等;SeaJS则是为了在浏览器中实现CMD规范的模块开发和加载。但在各种VirtualDOM框架横飞的现在,程序员已经很少会直接操作DOM元素,而模块的开发和加载也有早已有了别的方案。
就在这时,Nerv项目的作者提出了建议:“不然用Nerv来一发?”我记得,当时他脸上洋溢着淳朴的笑,Nerv也仅仅是部门内部的一个小项目。我们回想了首页这个业务,技术栈已经好几年未曾更新过,开发流程也不够理想。如果再不做出改变,明年的这个时候我们依然会面对一堆陈年老代码头疼不已。抱着试一试的心态,我们接受了他的提议。没想到,这个决定让首页从此摆脱了落后的技术架构,而Nerv现在也已经成长为GitHub上3k+ Star的热门项目。
Q: 为什么不使用React/Preact/Vue?
A: 这三者都是前端圈子中相当流行的项目。React有完善的体系和浓厚的社区氛围,Preact有着羞涩的体积,Vue则使用了先进的html模板和数据绑定机制。但是,上边这三者都无法兼容IE8。我们在经过相关数据的论证后,发现IE8的用户还是有一定的价值,这才最终激发了我们团队内部自己造轮子的想法。当然,在造轮子的过程中,我们也不忘向上面这些优秀框架的看齐。最终,Nerv在完美兼容React语法的同时,具有着出众的性能表现,在Gzip后也只占用9Kb的体积。
整体架构
在这次的项目中,我们基于上一年久经考验的前端体系(详细介绍),进行了升级:
开发模式Athena2.0
1.0版本的Athena,基于vinyl-fs的流操作,或者说是类似于gulp的压缩、编译等等操作的任务流。而到了2017年,webpack早已在前端圈中流行。同行们也早已经习惯在项目中直接基于最新的语言特性去开发,在webpack.config.js加上一个babel-loader就可以完美支持新语法并完成打包。Athena 1.0背着太沉重的历史包袱,已经很难快速实现对babel转译的支持。所以在首页的开发前,我们将Athena升级到了全新的2.0版本。
一如既往,Athena会为项目提供init(初始化),serve(实时预览),build(编译),publish(发布)等功能。除此之外,由于2.0版本的Athena是基于webpack的,所以项目中可以统一用npm来管理依赖,也可以直接使用最新的ES语言特性来进行开发。
使用Athena2.0开发时,建议的文件架构如下:
前后端协作
我们依然是采用了前后端分离的协作模式,由后端给出json格式的数据,前端拉取json数据进行渲染。对于大部分的组件来说,都会在constructor中做好组件的初始化工作,在componentDidMount的生命周期中拉取数据写入组件的state,再通过render函数进行渲染。
代码规范约束
有一千个读者,就会有一千个哈姆雷特。
上面这句名言,深刻地体现在了16版首页的代码仓库中。同一个组件,如果是基于jQuery+SeaJS的模式,一千个程序猿就会有一千种写法。结果在同一个项目中,代码风格不尽相同,代码质量良莠不齐,多人协作也会无从下手。
何以解忧?唯有统一代码风格了。通过ESLint+Husky,我们对每次代码提交都做了代码风格检查,对是否使用prefer const的变量声明、代码缩进该使用Tab还是空格等等的规则都做了约束。一开始定下规范的时候,团队成员或多或少都会有些不习惯。但通过偷偷在代码里下毒Athena的生成的项目模板中添加对应的规则,潜移默化地,团队成员们也都开始接受、习惯这些约束。
禁用变量重声明等规则,在一定程度上保证了代码质量;而统一的代码样式风格,则使得项目的多人协作更加便利。
Q: 保证代码质量,促进多人协作的终极好处是什么?
A: 由于项目代码风格统一,通俗易懂容易上手,我们首页的开发团队终于开始有妹纸加入了!一群雄性程序猿敲代码能敲出什么火花啊…
对性能优化的探索首屏直出
直出可能是加快首屏加载最行之有效的办法了。它在减少页面加载时间、首屏请求数等方面的好处自然不必再提,结合jQuery,也可以很方便地在直出的DOM上进行更多的操作。
Nerv框架对于它内部的组件、DOM有着良好的操作性,但是对于体系外的DOM节点,却是天生的操作无力。举个例子,比如在页面文件中我们直出一个轮播图:
使用Nerv为这段HTML添加轮播逻辑,成为了非常艰难的操作。终极的解决方案,应该是使用SSR(Server Side Render)的方案,搭建Nerv-server中间层来将组件直出。但现在革命尚未成功,首屏直出尚且依赖后端的研发同学,首页上线又迫在眉睫。被逼急的我们最终选择了比较trick的方式来过渡这个问题:在组件初始化的时候先通过DOM操作获取渲染所需的数据,再将DOM替换成Nerv渲染后的内容。
代码分割
在生产环境中,随着代码体积增大,浏览器解压Gzip、执行等操作也会需要更多的开销。在SeaJS的时代,我们尚且会通过SeaJS.use或者require.async异步加载模块代码,避免一次性加载过多内容。但webpack的默认行为却会将整个页面的代码打包为一个单独的文件,这明显不是最佳的实践。对此,webpack给出的解决方案是动态引入(Dynamic Imports)。我们可以通过如下的代码来使用这个便利的特性:
与此同时,webpack会将使用了动态引入的组件从主bundle文件中抽离出来,这就减小了主bundle文件的体积。
对于我们的具体需求而言,需要做动态引入的一般是Nerv的组件。对于组件的动态引入,业界已经有非常好的实现方案react-loadable。
举个栗子,通过下面的代码,我们可以在页面中使用来实现对组件MyComponent的动态引入,并且具有加载超时、错误、加载中等不同状态的展示:
再进一步,我们希望对于屏幕外的组件,仅仅是在它进入用户视野后再开始加载,这也就是我们常说的滚动懒加载。这可以结合业界已有的懒加载组件react-lazyload来实现。针对上面的,在下面的例子中,只有进入用户屏幕后,MyComponent才会开始加载:
上面的例子为lazyload的组件设置了200px的占位高度。并且设定了占位元素的类名,方便设定样式。
代码延后加载
在给首页全面升级技术栈的时候,我们忽略了一个问题:页面上还引用着少量来自兄弟团队的SeaJS模块,我们升级了技术栈是可以,但是强迫兄弟团队也一起去掉SeaJS重构一遍代码,这就有点不合理了。我们也不能仅仅为了这部分模块,就把SeaJS给打包进代码里面,这也是不科学的。
上面讲到的动态引入功能,帮我们很好地解决了这个问题。我们在代码中单独抽离了一个legacy模块,其中包含了SeaJS、SeaJS-combo等老模块并做了导出。这部分代码在首屏中并不直接引入,而是在需要执行的时候,通过上面的动态引入功能,单独请求下来使用:
打包性能优化
webpack默认会对整个项目引用到的文件进行编译、打包,其中还包括了Nervjs、es5-polyfill等基础的依赖库。这些文件从加入项目开始,基本都不会再有任何更改;然而在每次构建新版本时,webpack打包的这些基础库都会与上一版本有一些细微的区别,这会导致用户浏览器中对应的代码缓存失效。为此,我们考虑将这些基础库分开打包。
针对这种需求,webpack官方建议使用DLL插件来优化。DLL是Dynamic Link Library的简称,是windows系统中对于应用程序依赖的函数库的称呼。对于webpack,我们需要使用一个单独的webpack配置去生成DLL:
接下来,在我们的项目的webpack配置中引用DllReferencdPlugin,传入上面生成的json文件:
这样就完成了动态链接库的生成和引用。除了最开始的一次编译,后续开发中如果基础库没有变动,DLL就再也不需要重新编译,这也就解决了上面的代码变动的问题。
体验优化探索兼容IE8
兼容旧版本IE浏览器一直是前端开发人员心中永远的痛。过去,我们使用jQuery去统一不同浏览器的DOM操作和绑定事件,通过jQuery元素实例的map、each等类数组函数批量做JavaScript动画,等等。
但是在使用Nerv之后,从体系外直接操作DOM就显得很不优雅;更推荐的写法,是通过组件的ref属性来访问原生DOM。而map、each等函数,IE9+的浏览器也已经在Array.prototype下有了相应的实现。如果我们在代码中直接引入jQuery,这肯定是不科学的,这将使页面的脚本体积提高许多,同时还引入了很多我们根本用不上的多余功能。
面对这种情况,我们做了一个仅针对ie8的轻量级的兼容库es5-polyfill。它包括这些实现:Object的扩展函数、ES5对Array.prototype的扩充、标准的addEventListener和removeEventListener等。在入口文件顶部使用require('es5-polyfill');引入es5-polyfill后,只需3分钟,你就会和我一样,爱上这款框架可以在代码中愉快地使用上面说到的那些IE8不支持的API了。
但是,通过上面的CMD方式引入不就意味着对于IE9+的用户都引入了这些代码吗?这并不符合我们“随用随取,避免浪费”的原则。我们更推荐的做法,是在webpack中为配置多个entry,再使用HTMLWebpackPlugin在HTML模板中为es5-polyfill输出一段针对IE8的条件注释。具体实现可以参考nerv-webpack-boilerplate。
SVG Sprite
在页面中使用SVG,可以有效提升小图标在高清屏中的体验。类似于图片Sprite,SVG也可以通过Sprite来减少页面的请求(参考文章:拥抱Web设计新趋势:SVG Sprites实践应用)。
举个栗子,我们在Nerv中声明svgSprite组件,用以存放页面中用到的svg小图标:
接下来,我们可以在页面中动态引入上面的svgSprite组件就可以了:
在页面中挂载后,我们即可使用形如的代码去引用相应的图标了。
数据大屏
除了用户,我们同样也关注运营人员的体验。如果可以将运营数据都以直观的图表展示,这对于运营同学、产品同学都是十分幸福的事。这次的首页改版,我们与数据方合作,为首页配套开发了数据大屏项目SEE,用于运营数据的实时滚动展示。
SEE基于Nerv+Redux开发,使用ECharts进行数据的可视化展示。除了线上数据,SEE还有专门针对开发人员需求的性能版大屏,实时展示开发人员关心的页面onload时间,接口成功率,js报错数等指标。我们也希望未来SEE可以在更多的业务中用起来。
页面可用性保障和监控
我们做了许多优化工作来提升页面在性能、体验上的优良表现。但如果页面出现了JS逻辑错误,或者展示有问题,前面的优化工作就都前功尽弃了。所以在保证项目进度的基础上,我们又做了一系列的工作来保证首页的安全与稳定。
统一上线
同一份代码,经过不同版本的开发工具进行编译、压缩,生成的文件可能会天差地别,这种情况在多人协作中是相当致命的。比如:开发人员A的代码使用了新版本开发工具的API,而无辜的开发人员B对此毫不知情,使用了老版本的开发工具进行编译和发布…说多了都是泪,又是一场人间悲剧。
为了消除差异,我们希望不同开发人员的开发环境保持严格统一,但这其实是难以保证的:除了开发工具版本不同,有时候windows下,macOS下甚至是Linux下的表现也是不一样的。
为了解决这个问题,我们将编译的工作挪到了服务器端。开发人员在本地进行开发、自测,联调通过后提交到代码仓库中,确认上线后,上线平台拉取项目的代码,使用服务器端的工具链进行编译、压缩、发布等工作。
此外,上线平台还提供上线代码diff功能,可以将待上线的文件与线上的版本进行diff,待开发人员确认完才能继续上线操作。
接入上线平台后,开发人员再也不必担心开发环境的差异影响了编译结果,也不会误操作将其他同事开发中的分支带上线。就算是出现了线上bug,开发人员也可以轻松地通过上线平台记录的git commitId进行精确快速的回滚,有效保障了页面的可用性。
自动化测试
我们注意到,每次上线迭代,在经过编译工具的压缩、组合后,都有可能会对代码中其他部分的代码造成影响。如果在测试时只验证了当前迭代的功能点,疏漏了原先其他功能点的验证,就有可能引起一些意想不到的BUG。传统的DOM元素监控并无法满足我们的需求,因为有的bug出现的时机是在一连串特定的操作后。所以我们认为,我们造轮子的时候又到了。我们需要在Athena监控体系中增加一套针对页面中各个功能点的自动进行验证测试的系统。
这个系统基于selenium webdriver搭建。在后台中,开发者针对CSS选择器配置一系列的动作链Actionchain,包括点击、hover、输入文字、拖拽等操作,再通过对指定CSS选择器的HTML属性、样式、值等因素指定一个预期结果。后台服务会定时打开页面,执行预设的操作,如果与预期结果有出入,就会触发告警。
这种单服务器运行的e2e测试,容易碰到一些偶然网络波动的影响而导致乱告警。事实上,我们刚开始跑这套服务的时候,我们经常收到告警,但事实上页面展示并没有任何问题。针对这种偶发情况,我们在验证的过程中加入了失败重试的机制。只有在连续3次测试状态都为fail的情况,才会触发告警。经过优化后,监控的准确性有了质的提升。
素材监控
在生产环境中,除了程序BUG,数据运营的一些不规范操作也有可能影响到用户的体验。举例来说,如果页面中的图片体积过于庞大,会导致页面的加载时间变长,用户等待的时间会更久;如果页面中图片尺寸不合规,会导致 图片展示不正常,出现拉伸/压缩等现象,页面就会给人很山寨的感觉了。
针对这些素材异常情况,我们部署了针对性的监控服务。开发者针对特定的CSS选择器配置图片的标准体积以及尺寸。监控服务定时开启headless浏览器抓取页面中的图片并判断是否符合规则,不符合就触发告警。通过这样的手段,我们可以第一时间知道页面上出现的超限素材,通知运营的同学修改。
实时告警
作为Athena前端体系的一环,我们接入了Athena系统用于收集首页各种性能以及用户环境相关的数据。在这套系统中,我们可以获取到用户的屏幕分辨率占比,浏览器占比,同时还有页面加载时间、接口成功率等性能数据。
基于这些数据,我们在发现问题时可以进行有针对性的优化,比如调整特定接口的等待时间,或者是调整特定请求的重试策略。但是,并没有谁会一整天对这些数据的仪表盘盯着看;等我们发现问题,可能已经是上线后第二天,在工位上吃着早餐喝着牛奶的时候了。
解决信息滞后的问题,加强消息触达是关键。我们强化了Athena监控平台的功能:除了平台上仪表盘直观的数据展示,还支持配置告警规则。在绑定告警接收人的微信号后,平台就可以通过部门公众号实时推送告警信息,真正做到24小时监控,360度无盲点触达。
更长远的探索
“如何做得更好?”这是一个永不过时的问题。以前我们觉得在页面使用CMD的模块加载体系非常酷,所以后来会在项目中使用SeaJS;去年我们渴求一次架构升级,所以我们今年用上了Nerv。今年我们又会渴望什么呢?
前后端同构
作为提升性能的一个捷径,代码同构、服务器端渲染是目前看来的终极解决方案。我们计划搭建中间层,同样使用Nerv来渲染,从而减少首屏的代码逻辑,这将对页面的加载速度有大幅度的优化。
引入强类型校验
除了更快,我们也希望能够更稳。作为弱类型语言,JavaScript有着强大的灵活性,数据类型的相互转换十分便利;但也由于各种不严谨的类型转换,代码中存在着大量不可预测的分支走向,容易出现undefined的报错,调用不存在的API等等。
强类型语言TypeScript从13年诞生到现在,已经十分成熟了。在类型推断、静态验证上,TypeScript明显会更胜一筹;而在减少了多余的类型转换之后,TypeScript的性能表现也比常规JavaScript更强。我们希望未来可以将TypeScript在项目中用起来,这对于提升页面可靠性和性能,是很有意义的。
总结
这篇文章从整体开发架构与模式,性能、体验优化的探索,页面可用性的保障等方面对京东首页的开发过程做了简单的介绍。之所以说简单,是因为短短的篇幅完全无法说完我们在开发期间的故事和感悟:许多问题的解决并不像上面讲的那样水到渠成;除此之外更是有一大堆深夜加班撸串的故事没有地方讲。
最后献上个照片,这是项目上线成功之后在公司拍的通宵证明。虽然现在会觉得这拍得真……丑,但是项目成功上线的喜悦之情,我相信屏幕前的你也一样可以感受到。
jquery抓取网页内容?去除js,javascript等的混淆和去除化javascript
网站优化 • 优采云 发表了文章 • 0 个评论 • 68 次浏览 • 2022-06-20 06:04
jquery抓取网页内容???去除js,javascript等的混淆和去除语义化javascript标签seo优化网页优化响应式seo分享javascriptsimilarity。jsesjsesfjavascript下载javascriptextension。5。6。1更新:???seo导航-v1。
3。4。9更新:???javascript预处理器v4。6。3javascript预处理器ejsv4。25。1(v4。23。
2)url
抛砖引玉吧,随便写写。1.变量和常量的区别。首先要明确两个概念,常量:number,string,long,boolean,null,undefined等字符串,基本数据类型字符串变量:数值,number,string,boolean,symbol等字符串变量...还有..数组(object)类型。变量-字符串常量不常量常量_百度百科,如下这一段:常量functionfuck(){console.log('eatthebean');}console.log(fuck())//eatthebean常量functionfuck(){console.log('eatthebean');}fuck();//eatthebean常量functionfuck(){console.log('eatthebean');}fuck();//eatthebean}常量functionfuck(){console.log('eatthebean');}fuck();//eatthebean因为变量fuck(),而fuck又一直是fuck,所以常量就是常数。
这也是比较符合人的思维方式的。之前有人问我,如何让javascript为所欲为,让他去执行我们定义的fuck,我又是怎么回答他的呢?我想了下,大概应该是这样:常量fuck();常量fuck();常量fuck();定义一个函数的时候,用fuck(),那么常量fuck()就变成了fuck函数的常量。定义一个变量,也可以用fuck(),那么常量fuck()就变成了这个变量的常量,特别是对于数组,变量fuck()的作用范围是1-99,如果让我自己定义,我会这么定义:varmy_check_check=[1,2,3,4,5,6,7,8,9];functionprice(value){returnvalue;}my_check_check(。
1)//=>1functionadd_check_check(value){returnvalue+1;}my_check_check
3)//=>5functionmy_check_check(value){returnvalue+value+1;}my_check_check
5)//=>10现在这个变量就是纯虚的常量,你想让它干什么就干什么。现在有人问我,如何让javascript写不出来虚函数和匿名函数,我就想到前段时间在慕课网上的演讲上提到的一个方法,就是把一个正则里有虚函数、没有匿名函数作为一个模板自动生成一个匿名函数,这样一来, 查看全部
jquery抓取网页内容?去除js,javascript等的混淆和去除化javascript
jquery抓取网页内容???去除js,javascript等的混淆和去除语义化javascript标签seo优化网页优化响应式seo分享javascriptsimilarity。jsesjsesfjavascript下载javascriptextension。5。6。1更新:???seo导航-v1。
3。4。9更新:???javascript预处理器v4。6。3javascript预处理器ejsv4。25。1(v4。23。
2)url
抛砖引玉吧,随便写写。1.变量和常量的区别。首先要明确两个概念,常量:number,string,long,boolean,null,undefined等字符串,基本数据类型字符串变量:数值,number,string,boolean,symbol等字符串变量...还有..数组(object)类型。变量-字符串常量不常量常量_百度百科,如下这一段:常量functionfuck(){console.log('eatthebean');}console.log(fuck())//eatthebean常量functionfuck(){console.log('eatthebean');}fuck();//eatthebean常量functionfuck(){console.log('eatthebean');}fuck();//eatthebean}常量functionfuck(){console.log('eatthebean');}fuck();//eatthebean因为变量fuck(),而fuck又一直是fuck,所以常量就是常数。
这也是比较符合人的思维方式的。之前有人问我,如何让javascript为所欲为,让他去执行我们定义的fuck,我又是怎么回答他的呢?我想了下,大概应该是这样:常量fuck();常量fuck();常量fuck();定义一个函数的时候,用fuck(),那么常量fuck()就变成了fuck函数的常量。定义一个变量,也可以用fuck(),那么常量fuck()就变成了这个变量的常量,特别是对于数组,变量fuck()的作用范围是1-99,如果让我自己定义,我会这么定义:varmy_check_check=[1,2,3,4,5,6,7,8,9];functionprice(value){returnvalue;}my_check_check(。
1)//=>1functionadd_check_check(value){returnvalue+1;}my_check_check
3)//=>5functionmy_check_check(value){returnvalue+value+1;}my_check_check
5)//=>10现在这个变量就是纯虚的常量,你想让它干什么就干什么。现在有人问我,如何让javascript写不出来虚函数和匿名函数,我就想到前段时间在慕课网上的演讲上提到的一个方法,就是把一个正则里有虚函数、没有匿名函数作为一个模板自动生成一个匿名函数,这样一来,
《简单爬虫》——抓取一部小说
网站优化 • 优采云 发表了文章 • 0 个评论 • 85 次浏览 • 2022-06-05 02:52
例子是基于nodejs的,也不是很复杂,有兴趣的可以自己也来敲敲代码。
情不知从何起
很多人喜欢手机阅读,当然我也是,不过我比较喜欢看一些盗墓偏玄幻类的小说,比如《鬼吹灯》、《盗墓笔记》这样的。现在用手机来阅读也是很方便的事情,下个app就好了,什么起点、追书神器、QQ阅读之类的,但是这些多数都是要收费的,特别是那种还未完结的。收费还贵,1章少一点的5毛,多一点的那种要收1块,这类小说不写个1000多章都不好意思出来混。
对于我这样的免(dao)费(ban)程序受益者,怎么可能花那么大的代(jin)价(qian)来支持他。所以,第一原则:百度找免费的。百度一下果然有免费的,而且还是txt的,下载完成。开始看书。
当然,不可能所有事情都是那么顺利就解决的。比如下载的txt有时候会少章节,有时候少内容,看的不够尽兴。然后我又开始找网站了,一搜一大把,然后问题又来了,一章一个页面,每次看好一章就要去刷一下页面,每次还会加载很多小广告,这里的小广告就厉害了,不点掉会挡住部分文字,点一下就不知道跳转到哪里去了,有时候点击下一章也会是这样的。这流量刷刷刷的就没有了,对于我这样的程(qiong)序(diao)员(si),一个月300M流量不够用啊。
怎么办呢?毕竟咱们是个程序员啊,想点办法吧。那么我们用爬虫把页面里面的文字爬下来就好了嘛,于是就又了今天的内容了《简单爬虫》——抓取一部小说。
而一往情深
选角
爬虫当然有很多可以选择的,各种后端语言(java、.net)、脚本语言(php、nodejs、python),很多都可以的,但是作为一名前端工程师,在现在这个大环境下,多少需要知道一些nodejs,就算不会,但是至少还是需要知道的,不然怎么能成为一名合格的前端工程师呢?所以这次试着用用nodejs。
开搞
首先,我们要找到一个我们需要抓取的文章页面。这里抓一下天蚕土豆的《大主宰》。天蚕土豆应该都知道吧,当年的《斗破苍穹》就是这家伙写的,火的不行。
思路
我们需要抓取每一章小说内容,然后将每一章按照顺序排列好之后,写入文件——大主宰.txt中。好了思路大致就这样,接下来开撸代码。
var superagent = require("superagent")//用superagent去抓取
var cheerio = require("cheerio");//分析网页结构
//大主宰第一章页面
var first_url = "";
superagent.get(first_url).end(function(err,sres){
var $ = cheerio.load(sres.text);
var con = $("#content").text();
consoe.log(con);
})
先说一下代码superagent是一个http的库,这边我们用来发起http请求。然后cheerio这东西就更好了,说白了就是一个node版的jquery,这里我通过页面的代码结构知道文章都在一个ID为content的div里面。
运行一下居然是一堆乱码。。。一看就知道是编码问题了,看一下啊html编码是gbk的,一般我们的网站都是utf-8的,但是很多小说的网站都是gbk的编码,这是为什么呢?查看一下GBK编码:是指中国的中文字符,它包含了简体中文与繁体中文字符,另外还有一种字符“gb2312”,这种字符仅能存储简体中文字符。GBK编码格式,它的功能少,仅限于中文字符,当然它所占用的空间大小会随着它的功能而减少,打开网页的速度比较快。
知道为什么用gbk了,现在就开始转码吧
var superagent = require("superagent")//用superagent去抓取
var cheerio = require("cheerio");//分析网页结构
require('superagent-charset')(superagent);//引入gbk转换插件
//大主宰第一章页面
var first_url = "";
superagent.get(first_url).charset('gbk').end(function(err,sres){
var $ = cheerio.load(sres.text);
var con = $("#content").text();
console.log(con);
})
哈哈,搞定了,单个页面抓取成功了,那么就剩多页面抓取啦。
思路:列表页抓取单个章节的文章链接,然后再去抓取单页的文章,最后拼接写入文件。恩,思路可行,那么我们来看一下列表页面...
抓取list下的里面的a标签的href即可
//大主宰列表页面
var list_url = "";
superagent.get(list_url).end(function(err,sres){
var $ = cheerio.load(sres.text);
var listurl = new Array();//存放单章的url
$("#list dl dd a").each(function(idex,element){
var $ele = $(element);
listurl.push(list_url+$ele.attr("href").replace("/0_757/",""));
});
console.log(listurl);
})
我们这里打印一下列表页面的链接地址看一下。
恩 打印的链接没问题
superagent.get(list_url).end(function(err,sres){
var $ = cheerio.load(sres.text);
var listurl = new Array();//存放单章的url
$("#list dl dd a").each(function(idex,element){
var $ele = $(element);
listurl.push(list_url+$ele.attr("href").replace("/0_757/",""));
});
var content = "";//最终要的文章内容
var tmplisturl = listurl.slice(0,10);
tmplisturl.forEach(function(item){
superagent.get(item).charset('gbk').end(function(err,sres){
var $ = cheerio.load(sres.text);
var con = $("#content").text();
content+=con;
})
});
console.log(content)
});
那么来看一下代码多了一个循环,但是我打印出来的content居然是空...why?哦原来这个superagent抓取的时候是一个异步操作。所以我需要一个监听事件来告诉我,异步完成了才行。
我们这里用eventproxy,用来处理异步协作是很有用的,有兴趣的可以去#%E9%87%8D%E5%A4%8D%E5%BC%82%E6%AD%A5%E5%8D%8F%E4%BD%9C 学习一下。
superagent.get(list_url).end(function(err,sres){
var $ = cheerio.load(sres.text);
var listurl = new Array();//存放单章的url
$("#list dl dd a").each(function(idex,element){
var $ele = $(element);
listurl.push(list_url+$ele.attr("href").replace("/0_757/",""));
});
var content = "";//最终要的文章内容
var tmplisturl = listurl.slice(0,10);
tmplisturl.forEach(function(item){
superagent.get(item).charset('gbk').end(function(err,sres){
var $ = cheerio.load(sres.text);
var name = $(".bookname h1").text();
var con = $("#content").text().replace(/\n/g,'');
content+=name+"\n"+con+"\n";
ep.emit("finish");
})
});
ep.after("finish",tmplisturl.length,function(){
console.log(content);
});
});
ok啦!文章都能打印出来了,这里试了一下搞了前面10篇,加了点去除换行之类的小东西。但是还是有点问题,看下面的打印截图:
因为是异步的原因,所以没有按照我们原来设想的那样按照章节的顺序输出。想想是不是得先用对象保存起来然后再来排序输出呢?
tmplisturl.forEach(function(item){
superagent.get(item.url).charset('gbk').end(function(err,sres){
var $ = cheerio.load(sres.text);
var name = $(".bookname h1").text();
var con = $("#content").text().replace(/\n/g,'');
item.con = name+"\n"+con+"\n";
ep.emit("finish");
})
});
ep.after("finish",tmplisturl.length,function(){ //模拟10次
var content = "";
for(var i=0;i
content+=tmplisturl[i].con;
}
console.log(content);
});
试了一下果然ok啦。打完收工,领盒饭~
哦,还有最后需要写入文件
//'a+' - 以读取和追加模式打开文件。如果文件不存在,则会被创建
fs.open("大主宰.txt",'a+',function(){
fs.appendFile("大主宰.txt",content,function(){
console.log("写完了");
});
})
这里还有一点要提一下,别一次写入太多东西到文件中,内存不足会崩溃的。我这边做了一下分割循环。大概是30章一分割,也就是每次请求30章内容然后写入文件,待文件写入成功后继续请求30章,直到完成为止。但是我这边却只是写了600多章就没有了,这让我很惆怅...检查一下代码好像没什么问题,然后再次跑了一次,依然是600章不到的地方不动了..
看看打印的log信息:
想了想是不是我的内存不够用啊?看了一下,还真是这么一回事!
然后开始清理一下自己开的东西。微信,chrome,qq,outlook,sourcetree,phpstorm关了,然后瞬间清爽了。
剩余了10个G,继续跑一遍试试看,靠...还是不行,这次比之前的都少了,才200多章就停了...还不是内存占用的问题,那就是异步写入导致的I/O阻塞?
fs.appendFile("大主宰.txt",content,function(){
console.log("写入成功"+go)
if(go){
setTimeout(function(){
write();
},1000);
}else{
console.log('完成');
}
});
改成这样试试看,为了测试猜想,我只是写入了文章的标题。看一下运行结果:
还真的可以啊,当然这里的用的setTimeout,回头改成文件的关闭事件就好了。
完整代码
var superagent = require("superagent")//用superagent去抓取
var cheerio = require("cheerio");//分析网页结构
var eventproxy = require("eventproxy");//控制并发
require('superagent-charset')(superagent);//引入gbk转换插件
var list_url = "";//大主宰列表页面
var ep = new eventproxy();//得到一个 eventproxy 的实例
var fs = require("fs");//文件模块
console.time("start")
superagent.get(list_url).end(function(err,sres){
var $ = cheerio.load(sres.text);
var listurl = new Array();//存放单章的url
$("#list dl dd a").each(function(idex,element){
var $ele = $(element);
var tmpurl = $ele.attr("href");
var tmpid = tmpurl.replace("/0_757/","").replace(".html","")*1;
var tmp = {
url:list_url+tmpurl.replace("/0_757/",""),
id:tmpid,
con:""
}
listurl.push(tmp);
});
write();
function write(){
var tmplisturl = listurl.splice(0,30);
var go = true;
if(tmplisturl.length 查看全部
《简单爬虫》——抓取一部小说
例子是基于nodejs的,也不是很复杂,有兴趣的可以自己也来敲敲代码。
情不知从何起
很多人喜欢手机阅读,当然我也是,不过我比较喜欢看一些盗墓偏玄幻类的小说,比如《鬼吹灯》、《盗墓笔记》这样的。现在用手机来阅读也是很方便的事情,下个app就好了,什么起点、追书神器、QQ阅读之类的,但是这些多数都是要收费的,特别是那种还未完结的。收费还贵,1章少一点的5毛,多一点的那种要收1块,这类小说不写个1000多章都不好意思出来混。
对于我这样的免(dao)费(ban)程序受益者,怎么可能花那么大的代(jin)价(qian)来支持他。所以,第一原则:百度找免费的。百度一下果然有免费的,而且还是txt的,下载完成。开始看书。
当然,不可能所有事情都是那么顺利就解决的。比如下载的txt有时候会少章节,有时候少内容,看的不够尽兴。然后我又开始找网站了,一搜一大把,然后问题又来了,一章一个页面,每次看好一章就要去刷一下页面,每次还会加载很多小广告,这里的小广告就厉害了,不点掉会挡住部分文字,点一下就不知道跳转到哪里去了,有时候点击下一章也会是这样的。这流量刷刷刷的就没有了,对于我这样的程(qiong)序(diao)员(si),一个月300M流量不够用啊。
怎么办呢?毕竟咱们是个程序员啊,想点办法吧。那么我们用爬虫把页面里面的文字爬下来就好了嘛,于是就又了今天的内容了《简单爬虫》——抓取一部小说。
而一往情深
选角
爬虫当然有很多可以选择的,各种后端语言(java、.net)、脚本语言(php、nodejs、python),很多都可以的,但是作为一名前端工程师,在现在这个大环境下,多少需要知道一些nodejs,就算不会,但是至少还是需要知道的,不然怎么能成为一名合格的前端工程师呢?所以这次试着用用nodejs。
开搞
首先,我们要找到一个我们需要抓取的文章页面。这里抓一下天蚕土豆的《大主宰》。天蚕土豆应该都知道吧,当年的《斗破苍穹》就是这家伙写的,火的不行。
思路
我们需要抓取每一章小说内容,然后将每一章按照顺序排列好之后,写入文件——大主宰.txt中。好了思路大致就这样,接下来开撸代码。
var superagent = require("superagent")//用superagent去抓取
var cheerio = require("cheerio");//分析网页结构
//大主宰第一章页面
var first_url = "";
superagent.get(first_url).end(function(err,sres){
var $ = cheerio.load(sres.text);
var con = $("#content").text();
consoe.log(con);
})
先说一下代码superagent是一个http的库,这边我们用来发起http请求。然后cheerio这东西就更好了,说白了就是一个node版的jquery,这里我通过页面的代码结构知道文章都在一个ID为content的div里面。
运行一下居然是一堆乱码。。。一看就知道是编码问题了,看一下啊html编码是gbk的,一般我们的网站都是utf-8的,但是很多小说的网站都是gbk的编码,这是为什么呢?查看一下GBK编码:是指中国的中文字符,它包含了简体中文与繁体中文字符,另外还有一种字符“gb2312”,这种字符仅能存储简体中文字符。GBK编码格式,它的功能少,仅限于中文字符,当然它所占用的空间大小会随着它的功能而减少,打开网页的速度比较快。
知道为什么用gbk了,现在就开始转码吧
var superagent = require("superagent")//用superagent去抓取
var cheerio = require("cheerio");//分析网页结构
require('superagent-charset')(superagent);//引入gbk转换插件
//大主宰第一章页面
var first_url = "";
superagent.get(first_url).charset('gbk').end(function(err,sres){
var $ = cheerio.load(sres.text);
var con = $("#content").text();
console.log(con);
})
哈哈,搞定了,单个页面抓取成功了,那么就剩多页面抓取啦。
思路:列表页抓取单个章节的文章链接,然后再去抓取单页的文章,最后拼接写入文件。恩,思路可行,那么我们来看一下列表页面...
抓取list下的里面的a标签的href即可
//大主宰列表页面
var list_url = "";
superagent.get(list_url).end(function(err,sres){
var $ = cheerio.load(sres.text);
var listurl = new Array();//存放单章的url
$("#list dl dd a").each(function(idex,element){
var $ele = $(element);
listurl.push(list_url+$ele.attr("href").replace("/0_757/",""));
});
console.log(listurl);
})
我们这里打印一下列表页面的链接地址看一下。
恩 打印的链接没问题
superagent.get(list_url).end(function(err,sres){
var $ = cheerio.load(sres.text);
var listurl = new Array();//存放单章的url
$("#list dl dd a").each(function(idex,element){
var $ele = $(element);
listurl.push(list_url+$ele.attr("href").replace("/0_757/",""));
});
var content = "";//最终要的文章内容
var tmplisturl = listurl.slice(0,10);
tmplisturl.forEach(function(item){
superagent.get(item).charset('gbk').end(function(err,sres){
var $ = cheerio.load(sres.text);
var con = $("#content").text();
content+=con;
})
});
console.log(content)
});
那么来看一下代码多了一个循环,但是我打印出来的content居然是空...why?哦原来这个superagent抓取的时候是一个异步操作。所以我需要一个监听事件来告诉我,异步完成了才行。
我们这里用eventproxy,用来处理异步协作是很有用的,有兴趣的可以去#%E9%87%8D%E5%A4%8D%E5%BC%82%E6%AD%A5%E5%8D%8F%E4%BD%9C 学习一下。
superagent.get(list_url).end(function(err,sres){
var $ = cheerio.load(sres.text);
var listurl = new Array();//存放单章的url
$("#list dl dd a").each(function(idex,element){
var $ele = $(element);
listurl.push(list_url+$ele.attr("href").replace("/0_757/",""));
});
var content = "";//最终要的文章内容
var tmplisturl = listurl.slice(0,10);
tmplisturl.forEach(function(item){
superagent.get(item).charset('gbk').end(function(err,sres){
var $ = cheerio.load(sres.text);
var name = $(".bookname h1").text();
var con = $("#content").text().replace(/\n/g,'');
content+=name+"\n"+con+"\n";
ep.emit("finish");
})
});
ep.after("finish",tmplisturl.length,function(){
console.log(content);
});
});
ok啦!文章都能打印出来了,这里试了一下搞了前面10篇,加了点去除换行之类的小东西。但是还是有点问题,看下面的打印截图:
因为是异步的原因,所以没有按照我们原来设想的那样按照章节的顺序输出。想想是不是得先用对象保存起来然后再来排序输出呢?
tmplisturl.forEach(function(item){
superagent.get(item.url).charset('gbk').end(function(err,sres){
var $ = cheerio.load(sres.text);
var name = $(".bookname h1").text();
var con = $("#content").text().replace(/\n/g,'');
item.con = name+"\n"+con+"\n";
ep.emit("finish");
})
});
ep.after("finish",tmplisturl.length,function(){ //模拟10次
var content = "";
for(var i=0;i
content+=tmplisturl[i].con;
}
console.log(content);
});
试了一下果然ok啦。打完收工,领盒饭~
哦,还有最后需要写入文件
//'a+' - 以读取和追加模式打开文件。如果文件不存在,则会被创建
fs.open("大主宰.txt",'a+',function(){
fs.appendFile("大主宰.txt",content,function(){
console.log("写完了");
});
})
这里还有一点要提一下,别一次写入太多东西到文件中,内存不足会崩溃的。我这边做了一下分割循环。大概是30章一分割,也就是每次请求30章内容然后写入文件,待文件写入成功后继续请求30章,直到完成为止。但是我这边却只是写了600多章就没有了,这让我很惆怅...检查一下代码好像没什么问题,然后再次跑了一次,依然是600章不到的地方不动了..
看看打印的log信息:
想了想是不是我的内存不够用啊?看了一下,还真是这么一回事!
然后开始清理一下自己开的东西。微信,chrome,qq,outlook,sourcetree,phpstorm关了,然后瞬间清爽了。
剩余了10个G,继续跑一遍试试看,靠...还是不行,这次比之前的都少了,才200多章就停了...还不是内存占用的问题,那就是异步写入导致的I/O阻塞?
fs.appendFile("大主宰.txt",content,function(){
console.log("写入成功"+go)
if(go){
setTimeout(function(){
write();
},1000);
}else{
console.log('完成');
}
});
改成这样试试看,为了测试猜想,我只是写入了文章的标题。看一下运行结果:
还真的可以啊,当然这里的用的setTimeout,回头改成文件的关闭事件就好了。
完整代码
var superagent = require("superagent")//用superagent去抓取
var cheerio = require("cheerio");//分析网页结构
var eventproxy = require("eventproxy");//控制并发
require('superagent-charset')(superagent);//引入gbk转换插件
var list_url = "";//大主宰列表页面
var ep = new eventproxy();//得到一个 eventproxy 的实例
var fs = require("fs");//文件模块
console.time("start")
superagent.get(list_url).end(function(err,sres){
var $ = cheerio.load(sres.text);
var listurl = new Array();//存放单章的url
$("#list dl dd a").each(function(idex,element){
var $ele = $(element);
var tmpurl = $ele.attr("href");
var tmpid = tmpurl.replace("/0_757/","").replace(".html","")*1;
var tmp = {
url:list_url+tmpurl.replace("/0_757/",""),
id:tmpid,
con:""
}
listurl.push(tmp);
});
write();
function write(){
var tmplisturl = listurl.splice(0,30);
var go = true;
if(tmplisturl.length
jquery抓取网页内容,完整教程可以参考我翻译的文章
网站优化 • 优采云 发表了文章 • 0 个评论 • 78 次浏览 • 2022-05-30 10:07
jquery抓取网页内容,完整教程可以参考我翻译的文章;-mocking/里面详细说明了如何抓取html页面。对于jquery并不熟悉,只是学过jquerydom,会用几个函数,并不知道该如何抓取html内容,所以对抓取的流程都不熟悉。初学者,建议看我的这篇文章jquery抓取内容,对抓取有一个大概的了解jquery抓取网页内容_技术博客_zim_新浪博客。
对于没有任何编程基础的同学,可以尝试先从这篇文章了解一下抓取html的步骤。了解完以后有可能对python抓取js源码这件事就会有一个感性的认识了。
可以通过如下方式:1.爬取百度搜索信息:搜索关键词-->点击列表列表信息2.爬取百度文库:百度文库页面内容
一、爬虫简介
1、爬虫是什么?顾名思义,爬虫就是“爬行”网页的机器,
2、爬虫有哪些分类?爬虫分为以下几类:搜索引擎爬虫、抓取网页的机器人等;
3、哪些网站需要爬虫?
1)自然搜索引擎,要学会谷歌搜索技术、关键词、竞价广告等基础抓取,
2)用户产生的内容,比如知乎的使用体验内容等,
3)小说、新闻等互联网专业产生的内容也可以靠爬虫解决。
二、爬虫入门
1、概念爬虫程序,
2、代码爬虫的入门,
1、如何通过浏览器输入网址链接来抓取网页内容
2、异步获取网页内容?在浏览器输入网址的时候,浏览器会按照特定的结构去结构化搜索内容(即将关键字进行编码储存,储存的结构为plainjs、postjs、basejs等格式,如:cookie),所以当你爬取的网页经过登录等安全防护的时候,就可以直接抓取你想要的网页,从而形成“爬虫”。
爬虫如何实现异步获取呢?看下下面的图解:异步获取网页内容
1、通过selenium模拟自己的电脑去执行命令;
2、通过asyncio库自己的异步机制
3、看一下如何异步获取网页内容:/,例如:requests(request)是异步下的模拟请求方法,通过selenium模拟其他人工操作浏览器,从而提取网页上的内容的方法。
三、爬虫延伸
1、高阶爬虫?本身爬虫主要目的是爬取网页数据,但是爬虫中也会涉及到一些高阶的操作,如自动发帖、自动上传图片等。
2、爬虫扩展
1)可爬取博客列表
1)博客列表分为新闻站,说明各个官方博客的站点有编辑把内容抓取过来,再存入对应的get方法中,进而把数据保存到目标post、head等方法中。
2)博客站点有几百个,虽然定位博客列表爬虫容易, 查看全部
jquery抓取网页内容,完整教程可以参考我翻译的文章
jquery抓取网页内容,完整教程可以参考我翻译的文章;-mocking/里面详细说明了如何抓取html页面。对于jquery并不熟悉,只是学过jquerydom,会用几个函数,并不知道该如何抓取html内容,所以对抓取的流程都不熟悉。初学者,建议看我的这篇文章jquery抓取内容,对抓取有一个大概的了解jquery抓取网页内容_技术博客_zim_新浪博客。
对于没有任何编程基础的同学,可以尝试先从这篇文章了解一下抓取html的步骤。了解完以后有可能对python抓取js源码这件事就会有一个感性的认识了。
可以通过如下方式:1.爬取百度搜索信息:搜索关键词-->点击列表列表信息2.爬取百度文库:百度文库页面内容
一、爬虫简介
1、爬虫是什么?顾名思义,爬虫就是“爬行”网页的机器,
2、爬虫有哪些分类?爬虫分为以下几类:搜索引擎爬虫、抓取网页的机器人等;
3、哪些网站需要爬虫?
1)自然搜索引擎,要学会谷歌搜索技术、关键词、竞价广告等基础抓取,
2)用户产生的内容,比如知乎的使用体验内容等,
3)小说、新闻等互联网专业产生的内容也可以靠爬虫解决。
二、爬虫入门
1、概念爬虫程序,
2、代码爬虫的入门,
1、如何通过浏览器输入网址链接来抓取网页内容
2、异步获取网页内容?在浏览器输入网址的时候,浏览器会按照特定的结构去结构化搜索内容(即将关键字进行编码储存,储存的结构为plainjs、postjs、basejs等格式,如:cookie),所以当你爬取的网页经过登录等安全防护的时候,就可以直接抓取你想要的网页,从而形成“爬虫”。
爬虫如何实现异步获取呢?看下下面的图解:异步获取网页内容
1、通过selenium模拟自己的电脑去执行命令;
2、通过asyncio库自己的异步机制
3、看一下如何异步获取网页内容:/,例如:requests(request)是异步下的模拟请求方法,通过selenium模拟其他人工操作浏览器,从而提取网页上的内容的方法。
三、爬虫延伸
1、高阶爬虫?本身爬虫主要目的是爬取网页数据,但是爬虫中也会涉及到一些高阶的操作,如自动发帖、自动上传图片等。
2、爬虫扩展
1)可爬取博客列表
1)博客列表分为新闻站,说明各个官方博客的站点有编辑把内容抓取过来,再存入对应的get方法中,进而把数据保存到目标post、head等方法中。
2)博客站点有几百个,虽然定位博客列表爬虫容易,
jquery抓取网页内容到json格式是javascript与dom结合的技术
网站优化 • 优采云 发表了文章 • 0 个评论 • 80 次浏览 • 2022-05-27 03:01
jquery抓取网页内容到json格式是javascript与dom结合的技术,如果您还没有了解过dom,请参考:[原创]firefox+jquery+nodejs做网页抓取实战[原创]jquery抓取网页内容用javascript解析我自己用less格式的html文件[原创]如何将请求url转化为nodejs可以解析的array格式{{"name":"crawler","version":1,"selector":"","callback":function(res,req){letname=req。
tostring(req。uri)。split("")[2]。replace(/\。(||)$/,"")。split("")。replace(/\。(||)$/,"")//。returnname}}分页文件可以这样格式:{{"name":"crawler","version":1,"selector":"","callback":function(res,req){letname=req。
tostring(req。uri)。split("")[2]。replace(/\。(||)$/,"")//。returnname}}这样做到目前为止,效果是是这样的:页面分页支持(假设page[1,numof]===0):{{"name":"crawler","version":1,"selector":"/","callback":function(res,req){letname=req。
tostring(req。uri)。split("")[2]。replace(/\。(||)$/,"")//。returnname}}每次发生请求或者http连接超时等情况导致page[1,numof]===0:{{"name":"crawler","version":1,"selector":"/","callback":function(res,req){letname=req。
tostring(req。uri)。split("")[2]。replace(/\。(||)$/,"")//。returnname}}。 查看全部
jquery抓取网页内容到json格式是javascript与dom结合的技术
jquery抓取网页内容到json格式是javascript与dom结合的技术,如果您还没有了解过dom,请参考:[原创]firefox+jquery+nodejs做网页抓取实战[原创]jquery抓取网页内容用javascript解析我自己用less格式的html文件[原创]如何将请求url转化为nodejs可以解析的array格式{{"name":"crawler","version":1,"selector":"","callback":function(res,req){letname=req。
tostring(req。uri)。split("")[2]。replace(/\。(||)$/,"")。split("")。replace(/\。(||)$/,"")//。returnname}}分页文件可以这样格式:{{"name":"crawler","version":1,"selector":"","callback":function(res,req){letname=req。
tostring(req。uri)。split("")[2]。replace(/\。(||)$/,"")//。returnname}}这样做到目前为止,效果是是这样的:页面分页支持(假设page[1,numof]===0):{{"name":"crawler","version":1,"selector":"/","callback":function(res,req){letname=req。
tostring(req。uri)。split("")[2]。replace(/\。(||)$/,"")//。returnname}}每次发生请求或者http连接超时等情况导致page[1,numof]===0:{{"name":"crawler","version":1,"selector":"/","callback":function(res,req){letname=req。
tostring(req。uri)。split("")[2]。replace(/\。(||)$/,"")//。returnname}}。
跟jQuery那样简单方便操作Html文档的Java工具类,今天我必须要告知你!
网站优化 • 优采云 发表了文章 • 0 个评论 • 78 次浏览 • 2022-05-21 18:18
神器介绍
今天我要介绍一款操作Html文档非常好用的Java插件,强烈安利!因为实在太好用了!
“
Jsoup 是一款纯Java实现,可以非常方便读取和操作Html文档的一款插件。她的API跟jQuery非常相似。我都甚至怀疑创造者是否是jQuery的忠实粉丝。
”读完本文,你能做哪些“坏事”?我选择她的原因
其解析器能够尽最大可能从你提供的HTML文档来创建一个干净完整的解析结果,不管你提供的HTML的格式是否完整。这样就可以完美解决一些不规范的html文档读取的时候,容易报错,太影响开发进度了,并且,影响了程序的健壮性。动不动就报Html格式不规范的错误,想想都怕了。
例如:
代码实操加载html文档
大家可以随便拿个网页来操作一下,我就随手了我自己微信公众号的一篇原创文章作为我的例子去讲解了。为了简化讲解流程,本文就不讲爬虫技术这部分内容了,跳过使用爬虫技术获取网页。
本文简单一点,直接人手保存一个网页Html代替爬虫自动抓取网页动作了。
把html文件加载到Jsoup中
FileReader fileReader = new FileReader("D:\\temp\\demo\\wxPage.html");<br /> String result = fileReader.readString();<br /> Document doc = Jsoup.parse(result);//其实,就一行代码<br />
获取header里面的 meta 标签 <p>Elements metaList = doc.getElementsByTag("meta");<br /> <br /> int metaListSize = metaList.size();<br /> System.out.println("metaList size:"+metaListSize);<br /> for(int i =0 ;i 查看全部
跟jQuery那样简单方便操作Html文档的Java工具类,今天我必须要告知你!
神器介绍
今天我要介绍一款操作Html文档非常好用的Java插件,强烈安利!因为实在太好用了!
“
Jsoup 是一款纯Java实现,可以非常方便读取和操作Html文档的一款插件。她的API跟jQuery非常相似。我都甚至怀疑创造者是否是jQuery的忠实粉丝。
”读完本文,你能做哪些“坏事”?我选择她的原因
其解析器能够尽最大可能从你提供的HTML文档来创建一个干净完整的解析结果,不管你提供的HTML的格式是否完整。这样就可以完美解决一些不规范的html文档读取的时候,容易报错,太影响开发进度了,并且,影响了程序的健壮性。动不动就报Html格式不规范的错误,想想都怕了。
例如:
代码实操加载html文档
大家可以随便拿个网页来操作一下,我就随手了我自己微信公众号的一篇原创文章作为我的例子去讲解了。为了简化讲解流程,本文就不讲爬虫技术这部分内容了,跳过使用爬虫技术获取网页。
本文简单一点,直接人手保存一个网页Html代替爬虫自动抓取网页动作了。
把html文件加载到Jsoup中
FileReader fileReader = new FileReader("D:\\temp\\demo\\wxPage.html");<br /> String result = fileReader.readString();<br /> Document doc = Jsoup.parse(result);//其实,就一行代码<br />
获取header里面的 meta 标签 <p>Elements metaList = doc.getElementsByTag("meta");<br /> <br /> int metaListSize = metaList.size();<br /> System.out.println("metaList size:"+metaListSize);<br /> for(int i =0 ;i
【第1259期】Nerv实战 - 京东首页改版小结
网站优化 • 优采云 发表了文章 • 0 个评论 • 107 次浏览 • 2022-05-15 14:50
前言
项目代码还是要时不时的拿出来重构下,不然面对不定的需求最后那代码可想而知。今日早读文章来自凹凸实验室@Littly授权分享。
正文从这开始~
回想17年的京东首页改版,从上线到现在竟然已经过去了四个多月。这四个多月,除了不曾中断的日常维护需求,对首页孜孜不倦的优化工作,更多的是那些与拖延症抗争的日夜:是今天写,还是等好好休憩回味后再动手?很明显,在这几十上百个日夜里,我基本都选择了第三个选项:不折腾了,先休息吧。现在想起来,关于那一个月白加黑五加二加班生活的印象已经渐渐模糊。到现在依然能清晰记着的,大概是最为深刻的记忆了。
16版的京东首页,在性能、体验、灾备策略等各方面都做到了极致。站在如此高大的巨人肩上,除了满满的自信,我们心里更怕扑街。毫无疑问,我们在接到改版需求的那一刻,立马就敲定了新首页的技术选型:妥妥的jQuery+SeaJS!
但很快,我们就发现这样一点都不酷。jQuery是2006年的框架了,SeaJS停止维护也已经四年。这些项目的诞生都是为了解决当时业界的一些痛点:比如jQuery最开始是为了方便程序员在页面中操作DOM,绑定事件等;SeaJS则是为了在浏览器中实现CMD规范的模块开发和加载。但在各种VirtualDOM框架横飞的现在,程序员已经很少会直接操作DOM元素,而模块的开发和加载也有早已有了别的方案。
就在这时,Nerv项目的作者提出了建议:“不然用Nerv来一发?”我记得,当时他脸上洋溢着淳朴的笑,Nerv也仅仅是部门内部的一个小项目。我们回想了首页这个业务,技术栈已经好几年未曾更新过,开发流程也不够理想。如果再不做出改变,明年的这个时候我们依然会面对一堆陈年老代码头疼不已。抱着试一试的心态,我们接受了他的提议。没想到,这个决定让首页从此摆脱了落后的技术架构,而Nerv现在也已经成长为GitHub上3k+ Star的热门项目。
Q: 为什么不使用React/Preact/Vue?
A: 这三者都是前端圈子中相当流行的项目。React有完善的体系和浓厚的社区氛围,Preact有着羞涩的体积,Vue则使用了先进的html模板和数据绑定机制。但是,上边这三者都 无法兼容IE8。我们在经过相关数据的论证后,发现IE8的用户还是有一定的价值,这才最终激发了我们团队内部自己造轮子的想法。当然,在造轮子的过程中,我们也不忘向上面这些优秀框架的看齐。最终,Nerv在完美兼容React语法的同时,具有着出众的性能表现,在Gzip后也只占用9Kb的体积。
整体架构
在这次的项目中,我们基于上一年久经考验的前端体系,进行了升级:
整体架构图开发模式Athena2.0
1.0版本的Athena,基于vinyl-fs的流操作,或者说是类似于gulp的压缩、编译等等操作的任务流。而到了2017年,webpack早已在前端圈中流行。同行们也早已经习惯在项目中直接基于最新的语言特性去开发,在webpack.config.js加上一个babel-loader就可以完美支持新语法并完成打包。Athena 1.0背着太沉重的历史包袱,已经很难快速实现对babel转译的支持。所以在首页的开发前,我们将Athena升级到了全新的2.0版本。
一如既往,Athena会为项目提供init(初始化),serve(实时预览),build(编译),publish(发布)等功能。除此之外,由于2.0版本的Athena是基于webpack的,所以项目中可以 统一用npm来管理依赖,也可以直接使用最新的ES语言特性来进行开发。
使用Athena2.0开发时,建议的文件架构如下:
文件架构前后端协作
我们依然是采用了前后端分离的协作模式,由后端给出json格式的数据,前端拉取json数据进行渲染。对于大部分的组件来说,都会在constructor中做好组件的初始化工作,在componentDidMount的生命周期中拉取数据写入组件的state,再通过render函数进行渲染。
/* myComponent.js */<br style="box-sizing: border-box;" />import Nerv from 'nervjs'<br style="box-sizing: border-box;" />class MyComponent extends Nerv.Component {<br style="box-sizing: border-box;" /> constructor() {<br style="box-sizing: border-box;" /> super(...arguments)<br style="box-sizing: border-box;" /> this.state = {<br style="box-sizing: border-box;" /> xxx: 'xxx'<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> async requestData() {<br style="box-sizing: border-box;" /> // return await fetch('xxx').then(res => res.json())<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> componentDidMount() {<br style="box-sizing: border-box;" /> this.requestData()<br style="box-sizing: border-box;" /> .then(data => {<br style="box-sizing: border-box;" /> this.setState({<br style="box-sizing: border-box;" /> xxx: 'yyy'<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" /> }).catch(() => {<br style="box-sizing: border-box;" /> this.setState({<br style="box-sizing: border-box;" /> xxx: 'zzz'<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> render() {<br style="box-sizing: border-box;" /> return (<br style="box-sizing: border-box;" /> {this.state.xxx}<br style="box-sizing: border-box;" /> )<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" />}<br style="box-sizing: border-box;" />export default MyComponent
代码规范约束
有一千个读者,就会有一千个哈姆雷特。
上面这句名言,深刻地体现在了16版首页的代码仓库中。同一个组件,如果是基于jQuery+SeaJS的模式,一千个程序猿就会有一千种写法。结果在同一个项目中,代码风格不尽相同,代码质量良莠不齐,多人协作也会无从下手。
何以解忧?唯有统一代码风格了。通过ESLint+Husky,我们对每次代码提交都做了代码风格检查,对是否使用prefer const的变量声明、代码缩进该使用Tab还是空格等等的规则都做了约束。一开始定下规范的时候,团队成员或多或少都会有些不习惯。但通过偷偷在代码里下毒Athena的生成的项目模板中添加对应的规则,潜移默化地,团队成员们也都开始接受、习惯这些约束。
Git提交流程
禁用变量重声明等规则,在一定程度上保证了代码质量;而统一的代码样式风格,则使得项目的多人协作更加便利。
Q: 保证代码质量,促进多人协作的终极好处是什么?
A: 由于项目代码风格统一,通俗易懂容易上手,我们首页的开发团队终于开始妹纸加入了!一群雄性程序猿敲代码能敲出什么火花啊…
对性能优化的探索首屏直出
直出可能是加快首屏加载最行之有效的办法了。它在减少页面加载时间、首屏请求数等方面的好处自然不必再提,结合jQuery,也可以很方便地在直出的DOM上进行更多的操作。
Nerv框架对于它内部的组件、DOM有着良好的操作性,但是对于体系外的DOM节点,却是天生的操作无力。举个例子,比如在页面文件中我们直出一个轮播图:
<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> </a><br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> </a><br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> </a><br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> </a><br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" />
使用Nerv为这段HTML添加轮播逻辑,成为了非常艰难的操作。终极的解决方案,应该是使用 SSR(Server Side Render)的方案,搭建Nerv-server中间层来将组件直出。但现在革命尚未成功,首屏直出尚且依赖后端的研发同学,首页上线又迫在眉睫。被逼急的我们最终选择了比较trick的方式来过渡这个问题:在组件初始化的时候先通过DOM操作获取渲染所需的数据,再将DOM替换成Nerv渲染后的内容。
import Slider from 'path/to/slider'<br style="box-sizing: border-box;" />class Example extends Nerv.Component {<br style="box-sizing: border-box;" /> constructor() {<br style="box-sizing: border-box;" /> const ctn = this.props.ctn<br style="box-sizing: border-box;" /> this.state = {<br style="box-sizing: border-box;" /> data: this.gatherInfos()<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> ctn.innerHTML = ''<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> gatherInfos() {<br style="box-sizing: border-box;" /> // 返回已有DOM中的链接,图片url等信息<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> render() {<br style="box-sizing: border-box;" /> return (<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> {this.data.map(v => )}<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> )<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" />}<br style="box-sizing: border-box;" />const el = document.querySelector('#example')<br style="box-sizing: border-box;" />Nerv.render(, el)
代码分割
在生产环境中,随着代码体积增大,浏览器解压Gzip、执行等操作也会需要更多的开销。在SeaJS的时代,我们尚且会通过SeaJS.use或者require.async异步加载模块代码,避免一次性加载过多内容。但webpack的默认行为却会将整个页面的代码打包为一个单独的文件,这明显不是最佳的实践。对此,webpack给出的解决方案是动态引入(Dynamic Imports)。我们可以通过如下的代码来使用这个便利的特性:
import(<br style="box-sizing: border-box;" /> /* webpackChunkName: ${chunkName} */<br style="box-sizing: border-box;" /> '${chunkName}'<br style="box-sizing: border-box;" />).then((loaded) => {<br style="box-sizing: border-box;" /> /* Do anything to the loaded module */<br style="box-sizing: border-box;" />})
与此同时,webpack会将使用了动态引入的组件从主bundle文件中抽离出来,这就减小了主bundle文件的体积。
构建后的代码
对于我们的具体需求而言,需要做动态引入的一般是Nerv的组件。对于组件的动态引入,业界已经有非常好的实现方案react-loadable。举个栗子,通过下面的代码,我们可以在页面中使用来实现对组件MyComponent的动态引入,并且具有加载超时、错误、加载中等不同状态的展示:
/* loadableMyComponent.js */<br style="box-sizing: border-box;" />import Loadable from 'react-loadable';<br style="box-sizing: border-box;" />const LoadableMyComponent = Loadable({<br style="box-sizing: border-box;" /> loader: () => import('MyComponent'),<br style="box-sizing: border-box;" /> delay: 300,<br style="box-sizing: border-box;" /> loading: (props) => {<br style="box-sizing: border-box;" /> if (props.error) {<br style="box-sizing: border-box;" /> // 加载错误<br style="box-sizing: border-box;" /> return Error!;<br style="box-sizing: border-box;" /> } else if (props.timedOut) {<br style="box-sizing: border-box;" /> // 加载超时<br style="box-sizing: border-box;" /> return TimedOut!; <br style="box-sizing: border-box;" /> } else if (props.pastDelay) {<br style="box-sizing: border-box;" /> // 加载占位符<br style="box-sizing: border-box;" /> return Loading...;<br style="box-sizing: border-box;" /> } else {<br style="box-sizing: border-box;" /> return null;<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> },<br style="box-sizing: border-box;" /> render: (loaded, props) => {<br style="box-sizing: border-box;" /> return ;<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" />});<br style="box-sizing: border-box;" />export default LoadableMyComponent
再进一步,我们希望对于屏幕外的组件,仅仅是在它进入用户视野后再开始加载,这也就是我们常说的滚动懒加载。这可以结合业界已有的懒加载组件react-lazyload来实现。针对上面的,在下面的例子中,只有进入用户屏幕后,MyComponent才会开始加载:
/* myApp.js */<br style="box-sizing: border-box;" />import Nerv from 'nervjs'<br style="box-sizing: border-box;" />import LazyLoad from 'react-lazyload';<br style="box-sizing: border-box;" />import LoadableMyComponent from './loadableMyComponent';<br style="box-sizing: border-box;" />class MyApp extends Nerv.Component {<br style="box-sizing: border-box;" /> render () {<br style="box-sizing: border-box;" /> return (<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> )<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" />}<br style="box-sizing: border-box;" />export default MyApp
上面的例子为lazyload的组件设置了200px的占位高度。并且设定了占位元素的类名,方便设定样式。
代码延后加载
在给首页全面升级技术栈的时候,我们忽略了一个问题:页面上还引用着少量来自兄弟团队的SeaJS模块,我们升级了技术栈是可以,但是强迫兄弟团队也一起去掉SeaJS重构一遍代码,这就有点不合理了。我们也不能仅仅为了这部分模块,就把SeaJS给打包进代码里面,这也是不科学的。
上面讲到的 动态引入功能,帮我们很好地解决了这个问题。我们在代码中单独抽离了一个legacy模块,其中包含了SeaJS、SeaJS-combo等老模块并做了导出。这部分代码在首屏中并不直接引入,而是在需要执行的时候,通过上面的动态引入功能,单独请求下来使用:
import('../legacy')<br style="box-sizing: border-box;" /> .then(({ SeaJS }) => {<br style="box-sizing: border-box;" /> SeaJS.use('xxx', function (XXX) {<br style="box-sizing: border-box;" /> // XXX.init();<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" /> })
打包性能优化
webpack默认会对整个项目引用到的文件进行编译、打包,其中还包括了Nervjs、es5-polyfill等基础的依赖库。这些文件从加入项目开始,基本都不会再有任何更改;然而在每次构建新版本时,webpack打包的这些基础库都会与上一版本有一些细微的区别,这会导致用户浏览器中对应的代码缓存失效。为此,我们考虑将这些基础库分开打包。
针对这种需求,webpack官方建议使用DLL插件来优化。DLL是Dynamic Link Library的简称,是windows系统中对于应用程序依赖的函数库的称呼。对于webpack,我们需要使用一个单独的webpack配置去生成DLL:
/* webpack.dll.config.js */<br style="box-sizing: border-box;" />plugins: [<br style="box-sizing: border-box;" /> new webpack.DllPlugin({<br style="box-sizing: border-box;" /> path: path.join(__dirname, 'dist', 'lib-manifest.json'),<br style="box-sizing: border-box;" /> name: 'lib.dll.js'<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" />]
接下来,在我们的项目的webpack配置中引用DllReferencdPlugin,传入上面生成的json文件:
/* webpack.dev.config.js */<br style="box-sizing: border-box;" />plugins: [<br style="box-sizing: border-box;" /> new webpack.DllReferencePlugin({<br style="box-sizing: border-box;" /> context: __dirname,<br style="box-sizing: border-box;" /> manifest: require('./dist/lib-manifest.json')<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" />]
这样就完成了动态链接库的生成和引用。除了最开始的一次编译,后续开发中如果基础库没有变动,DLL就再也不需要重新编译,这也就解决了上面的代码变动的问题。
体验优化探索兼容IE8
兼容旧版本IE浏览器一直是前端开发人员心中永远的痛。过去,我们使用jQuery去统一不同浏览器的DOM操作和绑定事件,通过jQuery元素实例的map、each等类数组函数批量做JavaScript动画,等等。
但是在使用Nerv之后,从体系外直接操作DOM就显得很不优雅;更推荐的写法,是通过组件的ref属性来访问原生DOM。而map、each等函数,IE9+的浏览器也已经在Array.prototype下有了相应的实现。如果我们在代码中直接引入jQuery,这肯定是不科学的,这将使页面的脚本体积提高许多,同时还引入了很多我们根本用不上的多余功能。
面对这种情况,我们做了一个仅针对ie8的轻量级的兼容库es5-polyfill。它包括这些实现:Object的扩展函数、ES5对Array.prototype的扩充、标准的addEventListener和removeEventListener等。在入口文件顶部使用require('es5-polyfill');引入es5-polyfill后,只需3分钟,你就会甘我一样,爱上这款框架可以在代码中愉快地使用上面说到的那些IE8不支持的API了。
但是,通过上面的CMD方式引入不就意味着对于IE9+的用户都引入了这些代码吗?这并不符合我们“随用随取,避免浪费”的原则。我们更推荐的做法,是在webpack中为配置多个entry,再使用HTMLWebpackPlugin在HTML模板中为es5-polyfill输出一段针对IE8的条件注释。具体实现可以参考nerv-webpack-boilerplate。
SVG Sprite
在页面中使用SVG,可以有效提升小图标在高清屏中的体验。类似于图片Sprite,SVG也可以通过Sprite来减少页面的请求
举个栗子,我们在Nerv中声明svgSprite组件,用以存放页面中用到的svg小图标:
/* svgSprite.js */<br style="box-sizing: border-box;" />const svgSprite = () => {<br style="box-sizing: border-box;" /> return (<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> {/* 更多的 */}<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> )<br style="box-sizing: border-box;" />}<br style="box-sizing: border-box;" />export default svgSprite
接下来,我们可以在页面中动态引入上面的svgSprite组件就可以了:
/* app.js */<br style="box-sizing: border-box;" />import Loadable from 'loadable'<br style="box-sizing: border-box;" />const LoadableSvgSprite = Loadable({<br style="box-sizing: border-box;" /> loader: () => import('./svgSprite'),<br style="box-sizing: border-box;" /> delay: 300,<br style="box-sizing: border-box;" /> render: (loaded, props) => {<br style="box-sizing: border-box;" /> return ;<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" />});
在页面中挂载后,我们即可使用形如的代码去引用相应的图标了。
数据大屏
除了用户,我们同样也关注运营人员的体验。如果可以将运营数据都以直观的图表展示,这对于运营同学、产品同学都是十分幸福的事。这次的首页改版,我们与数据方合作,为首页配套开发了数据大屏项目SEE,用于运营数据的实时滚动展示。
SEE基于Nerv+Redux开发,使用ECharts进行数据的可视化展示。除了线上数据,SEE还有专门针对开发人员需求的性能版大屏,实时展示开发人员关心的页面onload时间,接口成功率,js报错数等指标。我们也希望未来SEE可以在更多的业务中用起来。
页面可用性保障和监控
我们做了许多优化工作来提升页面在性能、体验上的优良表现。但如果页面出现了JS逻辑错误,或者展示有问题,前面的优化工作就都前功尽弃了。所以在保证项目进度的基础上,我们又做了一系列的工作来保证首页的安全与稳定。
统一上线
同一份代码,经过不同版本的开发工具进行编译、压缩,生成的文件可能会天差地别,这种情况在多人协作中是相当致命的。比如:开发人员A的代码使用了新版本开发工具的API,而无辜的开发人员B对此毫不知情,使用了老版本的开发工具进行编译和发布…说多了都是泪,又是一场人间悲剧。
为了消除差异,我们希望不同开发人员的开发环境保持严格统一,但这其实是难以保证的:除了开发工具版本不同,有时候windows下,macOS下甚至是Linux下的表现也是不一样的。
为了解决这个问题,我们将编译的工作挪到了服务器端。开发人员在本地进行开发、自测,联调通过后提交到代码仓库中,确认上线后,上线平台拉取项目的代码,使用服务器端的工具链进行编译、压缩、发布等工作。
此外,上线平台还提供上线代码diff功能,可以将待上线的文件与线上的版本进行diff,待开发人员确认完才能继续上线操作。
接入上线平台后,开发人员再也不必担心开发环境的差异影响了编译结果,也不会误操作将其他同事开发中的分支带上线。就算是出现了线上bug,开发人员也可以轻松地通过上线平台记录的git commitId进行 精确快速的回滚,有效保障了页面的可用性。
自动化测试
我们注意到,每次上线迭代,在经过编译工具的压缩、组合后,都有可能会对代码中其他部分的代码造成影响。如果在测试时只验证了当前迭代的功能点,疏漏了原先其他功能点的验证,就有可能引起一些意想不到的BUG。传统的DOM元素监控并无法满足我们的需求,因为有的bug出现的时机是在一连串特定的操作后。所以我们认为,我们造轮子的时候又到了。我们需要在Athena监控体系中增加一套针对页面中各个功能点的自动进行验证测试的系统。
这个系统基于selenium webdriver搭建。在后台中,开发者针对CSS选择器配置一系列的动作链Actionchain,包括点击、hover、输入文字、拖拽等操作,再通过对指定CSS选择器的HTML属性、样式、值等因素指定一个预期结果。后台服务会定时打开页面,执行预设的操作,如果与预期结果有出入,就会触发告警。
这种单服务器运行的e2e测试,容易碰到一些偶然网络波动的影响而导致乱告警。事实上,我们刚开始跑这套服务的时候,我们经常收到告警,但事实上页面展示并没有任何问题。针对这种偶发情况,我们在验证的过程中加入了失败重试的机制。只有在连续3次测试状态都为fail的情况,才会触发告警。经过优化后,监控的准确性有了质的提升。
素材监控
在生产环境中,除了程序BUG,数据运营的一些不规范操作也有可能影响到用户的体验。举例来说,如果页面中的图片体积过于庞大,会导致页面的加载时间变长,用户等待的时间会更久;如果页面中图片尺寸不合规,会导致 图片展示不正常,出现拉伸/压缩等现象,页面就会给人很山寨的感觉了。
针对这些素材异常情况,我们部署了针对性的监控服务。开发者针对特定的CSS选择器配置图片的标准体积以及尺寸。监控服务定时开启headless浏览器抓取页面中的图片并判断是否符合规则,不符合就触发告警。通过这样的手段,我们可以第一时间知道页面上出现的超限素材,通知运营的同学修改。
实时告警
作为Athena前端体系的一环,我们接入了Athena系统用于收集首页各种性能以及用户环境相关的数据。在这套系统中,我们可以获取到用户的屏幕分辨率占比,浏览器占比,同时还有 页面加载时间、接口成功率等性能数据。
基于这些数据,我们在发现问题时可以进行有针对性的优化,比如调整特定接口的等待时间,或者是调整特定请求的重试策略。但是,并没有谁会一整天对这些数据的仪表盘盯着看;等我们发现问题,可能已经是上线后第二天,在工位上吃着早餐喝着牛奶的时候了。
解决信息滞后的问题,加强消息触达是关键。我们强化了Athena监控平台的功能:除了平台上仪表盘直观的数据展示,还支持配置告警规则。在绑定告警接收人的微信号后,平台就可以通过部门公众号实时推送告警信息,真正做到24小时监控,360度无盲点触达。
更长远的探索
“如何做得更好?”这是一个永不过时的问题。以前我们觉得在页面使用CMD的模块加载体系非常酷,所以后来会在项目中使用SeaJS;去年我们渴求一次架构升级,所以我们今年用上了Nerv。今年我们又会渴望什么呢?
前后端同构
作为提升性能的一个捷径,代码同构、服务器端渲染是目前看来的终极解决方案。我们计划搭建中间层,同样使用Nerv来渲染,从而减少首屏的代码逻辑,这将对页面的加载速度有大幅度的优化。
引入强类型校验
除了更快,我们也希望能够更稳。作为弱类型语言,JavaScript有着强大的灵活性,数据类型的相互转换十分便利;但也由于各种不严谨的类型转换,代码中存在着大量不可预测的分支走向,容易出现undefined的报错,调用不存在的API等等。
强类型语言TypeScript从13年诞生到现在,已经十分成熟了。在类型推断、静态验证上,TypeScript明显会更胜一筹;而在减少了多余的类型转换之后,TypeScript的性能表现也比常规JavaScript更强。我们希望未来可以将TypeScript在项目中用起来,这对于提升页面可靠性和性能,是很有意义的。
总结
这篇文章从整体开发架构与模式,性能、体验优化的探索,页面可用性的保障等方面对京东首页的开发过程做了简单的介绍。之所以说简单,是因为短短的篇幅完全无法说完我们在开发期间的故事和感悟:许多问题的解决并不像上面讲的那样水到渠成;除此之外更是有一大堆深夜加班撸串的故事没有地方讲。
最后,为你推荐
关于本文 查看全部
【第1259期】Nerv实战 - 京东首页改版小结
前言
项目代码还是要时不时的拿出来重构下,不然面对不定的需求最后那代码可想而知。今日早读文章来自凹凸实验室@Littly授权分享。
正文从这开始~
回想17年的京东首页改版,从上线到现在竟然已经过去了四个多月。这四个多月,除了不曾中断的日常维护需求,对首页孜孜不倦的优化工作,更多的是那些与拖延症抗争的日夜:是今天写,还是等好好休憩回味后再动手?很明显,在这几十上百个日夜里,我基本都选择了第三个选项:不折腾了,先休息吧。现在想起来,关于那一个月白加黑五加二加班生活的印象已经渐渐模糊。到现在依然能清晰记着的,大概是最为深刻的记忆了。
16版的京东首页,在性能、体验、灾备策略等各方面都做到了极致。站在如此高大的巨人肩上,除了满满的自信,我们心里更怕扑街。毫无疑问,我们在接到改版需求的那一刻,立马就敲定了新首页的技术选型:妥妥的jQuery+SeaJS!
但很快,我们就发现这样一点都不酷。jQuery是2006年的框架了,SeaJS停止维护也已经四年。这些项目的诞生都是为了解决当时业界的一些痛点:比如jQuery最开始是为了方便程序员在页面中操作DOM,绑定事件等;SeaJS则是为了在浏览器中实现CMD规范的模块开发和加载。但在各种VirtualDOM框架横飞的现在,程序员已经很少会直接操作DOM元素,而模块的开发和加载也有早已有了别的方案。
就在这时,Nerv项目的作者提出了建议:“不然用Nerv来一发?”我记得,当时他脸上洋溢着淳朴的笑,Nerv也仅仅是部门内部的一个小项目。我们回想了首页这个业务,技术栈已经好几年未曾更新过,开发流程也不够理想。如果再不做出改变,明年的这个时候我们依然会面对一堆陈年老代码头疼不已。抱着试一试的心态,我们接受了他的提议。没想到,这个决定让首页从此摆脱了落后的技术架构,而Nerv现在也已经成长为GitHub上3k+ Star的热门项目。
Q: 为什么不使用React/Preact/Vue?
A: 这三者都是前端圈子中相当流行的项目。React有完善的体系和浓厚的社区氛围,Preact有着羞涩的体积,Vue则使用了先进的html模板和数据绑定机制。但是,上边这三者都 无法兼容IE8。我们在经过相关数据的论证后,发现IE8的用户还是有一定的价值,这才最终激发了我们团队内部自己造轮子的想法。当然,在造轮子的过程中,我们也不忘向上面这些优秀框架的看齐。最终,Nerv在完美兼容React语法的同时,具有着出众的性能表现,在Gzip后也只占用9Kb的体积。
整体架构
在这次的项目中,我们基于上一年久经考验的前端体系,进行了升级:
整体架构图开发模式Athena2.0
1.0版本的Athena,基于vinyl-fs的流操作,或者说是类似于gulp的压缩、编译等等操作的任务流。而到了2017年,webpack早已在前端圈中流行。同行们也早已经习惯在项目中直接基于最新的语言特性去开发,在webpack.config.js加上一个babel-loader就可以完美支持新语法并完成打包。Athena 1.0背着太沉重的历史包袱,已经很难快速实现对babel转译的支持。所以在首页的开发前,我们将Athena升级到了全新的2.0版本。
一如既往,Athena会为项目提供init(初始化),serve(实时预览),build(编译),publish(发布)等功能。除此之外,由于2.0版本的Athena是基于webpack的,所以项目中可以 统一用npm来管理依赖,也可以直接使用最新的ES语言特性来进行开发。
使用Athena2.0开发时,建议的文件架构如下:
文件架构前后端协作
我们依然是采用了前后端分离的协作模式,由后端给出json格式的数据,前端拉取json数据进行渲染。对于大部分的组件来说,都会在constructor中做好组件的初始化工作,在componentDidMount的生命周期中拉取数据写入组件的state,再通过render函数进行渲染。
/* myComponent.js */<br style="box-sizing: border-box;" />import Nerv from 'nervjs'<br style="box-sizing: border-box;" />class MyComponent extends Nerv.Component {<br style="box-sizing: border-box;" /> constructor() {<br style="box-sizing: border-box;" /> super(...arguments)<br style="box-sizing: border-box;" /> this.state = {<br style="box-sizing: border-box;" /> xxx: 'xxx'<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> async requestData() {<br style="box-sizing: border-box;" /> // return await fetch('xxx').then(res => res.json())<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> componentDidMount() {<br style="box-sizing: border-box;" /> this.requestData()<br style="box-sizing: border-box;" /> .then(data => {<br style="box-sizing: border-box;" /> this.setState({<br style="box-sizing: border-box;" /> xxx: 'yyy'<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" /> }).catch(() => {<br style="box-sizing: border-box;" /> this.setState({<br style="box-sizing: border-box;" /> xxx: 'zzz'<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> render() {<br style="box-sizing: border-box;" /> return (<br style="box-sizing: border-box;" /> {this.state.xxx}<br style="box-sizing: border-box;" /> )<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" />}<br style="box-sizing: border-box;" />export default MyComponent
代码规范约束
有一千个读者,就会有一千个哈姆雷特。
上面这句名言,深刻地体现在了16版首页的代码仓库中。同一个组件,如果是基于jQuery+SeaJS的模式,一千个程序猿就会有一千种写法。结果在同一个项目中,代码风格不尽相同,代码质量良莠不齐,多人协作也会无从下手。
何以解忧?唯有统一代码风格了。通过ESLint+Husky,我们对每次代码提交都做了代码风格检查,对是否使用prefer const的变量声明、代码缩进该使用Tab还是空格等等的规则都做了约束。一开始定下规范的时候,团队成员或多或少都会有些不习惯。但通过偷偷在代码里下毒Athena的生成的项目模板中添加对应的规则,潜移默化地,团队成员们也都开始接受、习惯这些约束。
Git提交流程
禁用变量重声明等规则,在一定程度上保证了代码质量;而统一的代码样式风格,则使得项目的多人协作更加便利。
Q: 保证代码质量,促进多人协作的终极好处是什么?
A: 由于项目代码风格统一,通俗易懂容易上手,我们首页的开发团队终于开始妹纸加入了!一群雄性程序猿敲代码能敲出什么火花啊…
对性能优化的探索首屏直出
直出可能是加快首屏加载最行之有效的办法了。它在减少页面加载时间、首屏请求数等方面的好处自然不必再提,结合jQuery,也可以很方便地在直出的DOM上进行更多的操作。
Nerv框架对于它内部的组件、DOM有着良好的操作性,但是对于体系外的DOM节点,却是天生的操作无力。举个例子,比如在页面文件中我们直出一个轮播图:
<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> </a><br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> </a><br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> </a><br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> </a><br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" />
使用Nerv为这段HTML添加轮播逻辑,成为了非常艰难的操作。终极的解决方案,应该是使用 SSR(Server Side Render)的方案,搭建Nerv-server中间层来将组件直出。但现在革命尚未成功,首屏直出尚且依赖后端的研发同学,首页上线又迫在眉睫。被逼急的我们最终选择了比较trick的方式来过渡这个问题:在组件初始化的时候先通过DOM操作获取渲染所需的数据,再将DOM替换成Nerv渲染后的内容。
import Slider from 'path/to/slider'<br style="box-sizing: border-box;" />class Example extends Nerv.Component {<br style="box-sizing: border-box;" /> constructor() {<br style="box-sizing: border-box;" /> const ctn = this.props.ctn<br style="box-sizing: border-box;" /> this.state = {<br style="box-sizing: border-box;" /> data: this.gatherInfos()<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> ctn.innerHTML = ''<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> gatherInfos() {<br style="box-sizing: border-box;" /> // 返回已有DOM中的链接,图片url等信息<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> render() {<br style="box-sizing: border-box;" /> return (<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> {this.data.map(v => )}<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> )<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" />}<br style="box-sizing: border-box;" />const el = document.querySelector('#example')<br style="box-sizing: border-box;" />Nerv.render(, el)
代码分割
在生产环境中,随着代码体积增大,浏览器解压Gzip、执行等操作也会需要更多的开销。在SeaJS的时代,我们尚且会通过SeaJS.use或者require.async异步加载模块代码,避免一次性加载过多内容。但webpack的默认行为却会将整个页面的代码打包为一个单独的文件,这明显不是最佳的实践。对此,webpack给出的解决方案是动态引入(Dynamic Imports)。我们可以通过如下的代码来使用这个便利的特性:
import(<br style="box-sizing: border-box;" /> /* webpackChunkName: ${chunkName} */<br style="box-sizing: border-box;" /> '${chunkName}'<br style="box-sizing: border-box;" />).then((loaded) => {<br style="box-sizing: border-box;" /> /* Do anything to the loaded module */<br style="box-sizing: border-box;" />})
与此同时,webpack会将使用了动态引入的组件从主bundle文件中抽离出来,这就减小了主bundle文件的体积。
构建后的代码
对于我们的具体需求而言,需要做动态引入的一般是Nerv的组件。对于组件的动态引入,业界已经有非常好的实现方案react-loadable。举个栗子,通过下面的代码,我们可以在页面中使用来实现对组件MyComponent的动态引入,并且具有加载超时、错误、加载中等不同状态的展示:
/* loadableMyComponent.js */<br style="box-sizing: border-box;" />import Loadable from 'react-loadable';<br style="box-sizing: border-box;" />const LoadableMyComponent = Loadable({<br style="box-sizing: border-box;" /> loader: () => import('MyComponent'),<br style="box-sizing: border-box;" /> delay: 300,<br style="box-sizing: border-box;" /> loading: (props) => {<br style="box-sizing: border-box;" /> if (props.error) {<br style="box-sizing: border-box;" /> // 加载错误<br style="box-sizing: border-box;" /> return Error!;<br style="box-sizing: border-box;" /> } else if (props.timedOut) {<br style="box-sizing: border-box;" /> // 加载超时<br style="box-sizing: border-box;" /> return TimedOut!; <br style="box-sizing: border-box;" /> } else if (props.pastDelay) {<br style="box-sizing: border-box;" /> // 加载占位符<br style="box-sizing: border-box;" /> return Loading...;<br style="box-sizing: border-box;" /> } else {<br style="box-sizing: border-box;" /> return null;<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> },<br style="box-sizing: border-box;" /> render: (loaded, props) => {<br style="box-sizing: border-box;" /> return ;<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" />});<br style="box-sizing: border-box;" />export default LoadableMyComponent
再进一步,我们希望对于屏幕外的组件,仅仅是在它进入用户视野后再开始加载,这也就是我们常说的滚动懒加载。这可以结合业界已有的懒加载组件react-lazyload来实现。针对上面的,在下面的例子中,只有进入用户屏幕后,MyComponent才会开始加载:
/* myApp.js */<br style="box-sizing: border-box;" />import Nerv from 'nervjs'<br style="box-sizing: border-box;" />import LazyLoad from 'react-lazyload';<br style="box-sizing: border-box;" />import LoadableMyComponent from './loadableMyComponent';<br style="box-sizing: border-box;" />class MyApp extends Nerv.Component {<br style="box-sizing: border-box;" /> render () {<br style="box-sizing: border-box;" /> return (<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> )<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" />}<br style="box-sizing: border-box;" />export default MyApp
上面的例子为lazyload的组件设置了200px的占位高度。并且设定了占位元素的类名,方便设定样式。
代码延后加载
在给首页全面升级技术栈的时候,我们忽略了一个问题:页面上还引用着少量来自兄弟团队的SeaJS模块,我们升级了技术栈是可以,但是强迫兄弟团队也一起去掉SeaJS重构一遍代码,这就有点不合理了。我们也不能仅仅为了这部分模块,就把SeaJS给打包进代码里面,这也是不科学的。
上面讲到的 动态引入功能,帮我们很好地解决了这个问题。我们在代码中单独抽离了一个legacy模块,其中包含了SeaJS、SeaJS-combo等老模块并做了导出。这部分代码在首屏中并不直接引入,而是在需要执行的时候,通过上面的动态引入功能,单独请求下来使用:
import('../legacy')<br style="box-sizing: border-box;" /> .then(({ SeaJS }) => {<br style="box-sizing: border-box;" /> SeaJS.use('xxx', function (XXX) {<br style="box-sizing: border-box;" /> // XXX.init();<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" /> })
打包性能优化
webpack默认会对整个项目引用到的文件进行编译、打包,其中还包括了Nervjs、es5-polyfill等基础的依赖库。这些文件从加入项目开始,基本都不会再有任何更改;然而在每次构建新版本时,webpack打包的这些基础库都会与上一版本有一些细微的区别,这会导致用户浏览器中对应的代码缓存失效。为此,我们考虑将这些基础库分开打包。
针对这种需求,webpack官方建议使用DLL插件来优化。DLL是Dynamic Link Library的简称,是windows系统中对于应用程序依赖的函数库的称呼。对于webpack,我们需要使用一个单独的webpack配置去生成DLL:
/* webpack.dll.config.js */<br style="box-sizing: border-box;" />plugins: [<br style="box-sizing: border-box;" /> new webpack.DllPlugin({<br style="box-sizing: border-box;" /> path: path.join(__dirname, 'dist', 'lib-manifest.json'),<br style="box-sizing: border-box;" /> name: 'lib.dll.js'<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" />]
接下来,在我们的项目的webpack配置中引用DllReferencdPlugin,传入上面生成的json文件:
/* webpack.dev.config.js */<br style="box-sizing: border-box;" />plugins: [<br style="box-sizing: border-box;" /> new webpack.DllReferencePlugin({<br style="box-sizing: border-box;" /> context: __dirname,<br style="box-sizing: border-box;" /> manifest: require('./dist/lib-manifest.json')<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" />]
这样就完成了动态链接库的生成和引用。除了最开始的一次编译,后续开发中如果基础库没有变动,DLL就再也不需要重新编译,这也就解决了上面的代码变动的问题。
体验优化探索兼容IE8
兼容旧版本IE浏览器一直是前端开发人员心中永远的痛。过去,我们使用jQuery去统一不同浏览器的DOM操作和绑定事件,通过jQuery元素实例的map、each等类数组函数批量做JavaScript动画,等等。
但是在使用Nerv之后,从体系外直接操作DOM就显得很不优雅;更推荐的写法,是通过组件的ref属性来访问原生DOM。而map、each等函数,IE9+的浏览器也已经在Array.prototype下有了相应的实现。如果我们在代码中直接引入jQuery,这肯定是不科学的,这将使页面的脚本体积提高许多,同时还引入了很多我们根本用不上的多余功能。
面对这种情况,我们做了一个仅针对ie8的轻量级的兼容库es5-polyfill。它包括这些实现:Object的扩展函数、ES5对Array.prototype的扩充、标准的addEventListener和removeEventListener等。在入口文件顶部使用require('es5-polyfill');引入es5-polyfill后,只需3分钟,你就会甘我一样,爱上这款框架可以在代码中愉快地使用上面说到的那些IE8不支持的API了。
但是,通过上面的CMD方式引入不就意味着对于IE9+的用户都引入了这些代码吗?这并不符合我们“随用随取,避免浪费”的原则。我们更推荐的做法,是在webpack中为配置多个entry,再使用HTMLWebpackPlugin在HTML模板中为es5-polyfill输出一段针对IE8的条件注释。具体实现可以参考nerv-webpack-boilerplate。
SVG Sprite
在页面中使用SVG,可以有效提升小图标在高清屏中的体验。类似于图片Sprite,SVG也可以通过Sprite来减少页面的请求
举个栗子,我们在Nerv中声明svgSprite组件,用以存放页面中用到的svg小图标:
/* svgSprite.js */<br style="box-sizing: border-box;" />const svgSprite = () => {<br style="box-sizing: border-box;" /> return (<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> {/* 更多的 */}<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> )<br style="box-sizing: border-box;" />}<br style="box-sizing: border-box;" />export default svgSprite
接下来,我们可以在页面中动态引入上面的svgSprite组件就可以了:
/* app.js */<br style="box-sizing: border-box;" />import Loadable from 'loadable'<br style="box-sizing: border-box;" />const LoadableSvgSprite = Loadable({<br style="box-sizing: border-box;" /> loader: () => import('./svgSprite'),<br style="box-sizing: border-box;" /> delay: 300,<br style="box-sizing: border-box;" /> render: (loaded, props) => {<br style="box-sizing: border-box;" /> return ;<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" />});
在页面中挂载后,我们即可使用形如的代码去引用相应的图标了。
数据大屏
除了用户,我们同样也关注运营人员的体验。如果可以将运营数据都以直观的图表展示,这对于运营同学、产品同学都是十分幸福的事。这次的首页改版,我们与数据方合作,为首页配套开发了数据大屏项目SEE,用于运营数据的实时滚动展示。
SEE基于Nerv+Redux开发,使用ECharts进行数据的可视化展示。除了线上数据,SEE还有专门针对开发人员需求的性能版大屏,实时展示开发人员关心的页面onload时间,接口成功率,js报错数等指标。我们也希望未来SEE可以在更多的业务中用起来。
页面可用性保障和监控
我们做了许多优化工作来提升页面在性能、体验上的优良表现。但如果页面出现了JS逻辑错误,或者展示有问题,前面的优化工作就都前功尽弃了。所以在保证项目进度的基础上,我们又做了一系列的工作来保证首页的安全与稳定。
统一上线
同一份代码,经过不同版本的开发工具进行编译、压缩,生成的文件可能会天差地别,这种情况在多人协作中是相当致命的。比如:开发人员A的代码使用了新版本开发工具的API,而无辜的开发人员B对此毫不知情,使用了老版本的开发工具进行编译和发布…说多了都是泪,又是一场人间悲剧。
为了消除差异,我们希望不同开发人员的开发环境保持严格统一,但这其实是难以保证的:除了开发工具版本不同,有时候windows下,macOS下甚至是Linux下的表现也是不一样的。
为了解决这个问题,我们将编译的工作挪到了服务器端。开发人员在本地进行开发、自测,联调通过后提交到代码仓库中,确认上线后,上线平台拉取项目的代码,使用服务器端的工具链进行编译、压缩、发布等工作。
此外,上线平台还提供上线代码diff功能,可以将待上线的文件与线上的版本进行diff,待开发人员确认完才能继续上线操作。
接入上线平台后,开发人员再也不必担心开发环境的差异影响了编译结果,也不会误操作将其他同事开发中的分支带上线。就算是出现了线上bug,开发人员也可以轻松地通过上线平台记录的git commitId进行 精确快速的回滚,有效保障了页面的可用性。
自动化测试
我们注意到,每次上线迭代,在经过编译工具的压缩、组合后,都有可能会对代码中其他部分的代码造成影响。如果在测试时只验证了当前迭代的功能点,疏漏了原先其他功能点的验证,就有可能引起一些意想不到的BUG。传统的DOM元素监控并无法满足我们的需求,因为有的bug出现的时机是在一连串特定的操作后。所以我们认为,我们造轮子的时候又到了。我们需要在Athena监控体系中增加一套针对页面中各个功能点的自动进行验证测试的系统。
这个系统基于selenium webdriver搭建。在后台中,开发者针对CSS选择器配置一系列的动作链Actionchain,包括点击、hover、输入文字、拖拽等操作,再通过对指定CSS选择器的HTML属性、样式、值等因素指定一个预期结果。后台服务会定时打开页面,执行预设的操作,如果与预期结果有出入,就会触发告警。
这种单服务器运行的e2e测试,容易碰到一些偶然网络波动的影响而导致乱告警。事实上,我们刚开始跑这套服务的时候,我们经常收到告警,但事实上页面展示并没有任何问题。针对这种偶发情况,我们在验证的过程中加入了失败重试的机制。只有在连续3次测试状态都为fail的情况,才会触发告警。经过优化后,监控的准确性有了质的提升。
素材监控
在生产环境中,除了程序BUG,数据运营的一些不规范操作也有可能影响到用户的体验。举例来说,如果页面中的图片体积过于庞大,会导致页面的加载时间变长,用户等待的时间会更久;如果页面中图片尺寸不合规,会导致 图片展示不正常,出现拉伸/压缩等现象,页面就会给人很山寨的感觉了。
针对这些素材异常情况,我们部署了针对性的监控服务。开发者针对特定的CSS选择器配置图片的标准体积以及尺寸。监控服务定时开启headless浏览器抓取页面中的图片并判断是否符合规则,不符合就触发告警。通过这样的手段,我们可以第一时间知道页面上出现的超限素材,通知运营的同学修改。
实时告警
作为Athena前端体系的一环,我们接入了Athena系统用于收集首页各种性能以及用户环境相关的数据。在这套系统中,我们可以获取到用户的屏幕分辨率占比,浏览器占比,同时还有 页面加载时间、接口成功率等性能数据。
基于这些数据,我们在发现问题时可以进行有针对性的优化,比如调整特定接口的等待时间,或者是调整特定请求的重试策略。但是,并没有谁会一整天对这些数据的仪表盘盯着看;等我们发现问题,可能已经是上线后第二天,在工位上吃着早餐喝着牛奶的时候了。
解决信息滞后的问题,加强消息触达是关键。我们强化了Athena监控平台的功能:除了平台上仪表盘直观的数据展示,还支持配置告警规则。在绑定告警接收人的微信号后,平台就可以通过部门公众号实时推送告警信息,真正做到24小时监控,360度无盲点触达。
更长远的探索
“如何做得更好?”这是一个永不过时的问题。以前我们觉得在页面使用CMD的模块加载体系非常酷,所以后来会在项目中使用SeaJS;去年我们渴求一次架构升级,所以我们今年用上了Nerv。今年我们又会渴望什么呢?
前后端同构
作为提升性能的一个捷径,代码同构、服务器端渲染是目前看来的终极解决方案。我们计划搭建中间层,同样使用Nerv来渲染,从而减少首屏的代码逻辑,这将对页面的加载速度有大幅度的优化。
引入强类型校验
除了更快,我们也希望能够更稳。作为弱类型语言,JavaScript有着强大的灵活性,数据类型的相互转换十分便利;但也由于各种不严谨的类型转换,代码中存在着大量不可预测的分支走向,容易出现undefined的报错,调用不存在的API等等。
强类型语言TypeScript从13年诞生到现在,已经十分成熟了。在类型推断、静态验证上,TypeScript明显会更胜一筹;而在减少了多余的类型转换之后,TypeScript的性能表现也比常规JavaScript更强。我们希望未来可以将TypeScript在项目中用起来,这对于提升页面可靠性和性能,是很有意义的。
总结
这篇文章从整体开发架构与模式,性能、体验优化的探索,页面可用性的保障等方面对京东首页的开发过程做了简单的介绍。之所以说简单,是因为短短的篇幅完全无法说完我们在开发期间的故事和感悟:许多问题的解决并不像上面讲的那样水到渠成;除此之外更是有一大堆深夜加班撸串的故事没有地方讲。
最后,为你推荐
关于本文
抓包分析、json数据解析(一)|抓包数据分析
网站优化 • 优采云 发表了文章 • 0 个评论 • 94 次浏览 • 2022-05-15 06:00
jquery抓取网页内容之json数据前言先来说说http的文档格式。浏览器保存http页面上的内容,只能保存静态内容,即post或get。而json本质是一个对象格式,其中可以包含任意数据,比如对象对象函数属性等。本文主要通过抓包分析、json数据解析进行讲解。
一、抓包分析百度json包的抓包抓包分析http::8080/json/static/static/web/public/json/blob,抓取代码如下varurl='/'varstatic_json={}static_json.push({json:{'name':'amethyst','learned':1}})static_json.send({'name':'adman','learned':6}),methods:{'get':url,'post':static_json},varflag=false是因为static_json是预处理的json数据。
flag的值可以是
0、1,如果flag=0是获取无值的json,flag=1则获取内部已经赋值了的json。(flag值为1则数据无效)举例:前两张图就可以看到,抓取的结果并不是post或get的格式,而是一个纯文本格式。
二、解析json数据其实解析json数据的方法很多,传统json是利用json+json.length等单个key值来实现。其实可以利用lookup.split来实现数据的去重,其代码如下functionlookup(value){varpresize=length;lookup(value,value);returnfalse;}其实,下面我们抓包看看再说。
三、代码的实现。a.抓包工具vs-xml-cookie这个抓包工具在网页数据抓取上还是挺好用的,下面演示一下这个抓包工具和xmlcookie进行的数据交互。其实这里用到的process.protocol对于我们来说也用不上,因为不是当前浏览器可以抓取的,这里简单演示一下。首先,打开抓包工具:接着,看页面内容,利用浏览器的network里对ajax的跳转,找到本地的method:type:1,即是:json的前缀:0x00。
之后在process.protocol对象对应的属性处加上:82464,即为抓取工具的端口号,就变成本地的ajax了:可以看到,http网站的数据就变成了一个json数据。b.json的格式c.数据的解析实现json的数据解析,通常要先解析json格式的内容,而不是解析xml格式的内容,因为xml数据分析的时候需要考虑单元格或列表的格式,这里我们就选用了解析json的方法,具体详见源码。
其中分解json数据的数据结构,数据转换方法,如object.values,function.apply等等。而xml数据解析的方法,都是简单粗暴的:复制flag=false.slice,然后简单进行替换操作即可。来自已经实现的csv数据分析练习。该代码。 查看全部
抓包分析、json数据解析(一)|抓包数据分析
jquery抓取网页内容之json数据前言先来说说http的文档格式。浏览器保存http页面上的内容,只能保存静态内容,即post或get。而json本质是一个对象格式,其中可以包含任意数据,比如对象对象函数属性等。本文主要通过抓包分析、json数据解析进行讲解。
一、抓包分析百度json包的抓包抓包分析http::8080/json/static/static/web/public/json/blob,抓取代码如下varurl='/'varstatic_json={}static_json.push({json:{'name':'amethyst','learned':1}})static_json.send({'name':'adman','learned':6}),methods:{'get':url,'post':static_json},varflag=false是因为static_json是预处理的json数据。
flag的值可以是
0、1,如果flag=0是获取无值的json,flag=1则获取内部已经赋值了的json。(flag值为1则数据无效)举例:前两张图就可以看到,抓取的结果并不是post或get的格式,而是一个纯文本格式。
二、解析json数据其实解析json数据的方法很多,传统json是利用json+json.length等单个key值来实现。其实可以利用lookup.split来实现数据的去重,其代码如下functionlookup(value){varpresize=length;lookup(value,value);returnfalse;}其实,下面我们抓包看看再说。
三、代码的实现。a.抓包工具vs-xml-cookie这个抓包工具在网页数据抓取上还是挺好用的,下面演示一下这个抓包工具和xmlcookie进行的数据交互。其实这里用到的process.protocol对于我们来说也用不上,因为不是当前浏览器可以抓取的,这里简单演示一下。首先,打开抓包工具:接着,看页面内容,利用浏览器的network里对ajax的跳转,找到本地的method:type:1,即是:json的前缀:0x00。
之后在process.protocol对象对应的属性处加上:82464,即为抓取工具的端口号,就变成本地的ajax了:可以看到,http网站的数据就变成了一个json数据。b.json的格式c.数据的解析实现json的数据解析,通常要先解析json格式的内容,而不是解析xml格式的内容,因为xml数据分析的时候需要考虑单元格或列表的格式,这里我们就选用了解析json的方法,具体详见源码。
其中分解json数据的数据结构,数据转换方法,如object.values,function.apply等等。而xml数据解析的方法,都是简单粗暴的:复制flag=false.slice,然后简单进行替换操作即可。来自已经实现的csv数据分析练习。该代码。
jquery抓取网页内容 技巧:二.主要设计操作
网站优化 • 优采云 发表了文章 • 0 个评论 • 177 次浏览 • 2022-09-24 11:14
一.基本信息
jQuery 于 2006 年发布,目前是前端寿命最长的库。它是世界上使用最广泛的图书馆,拥有全球80%以上的网站。
二.主要设计操作
1.获取网页元素
一种是通过jQuery选择器选择元素,可以直接获取单个或批量元素;
另一种是通过jQuery遍历关联来选择元素,常用于获取层级比较复杂的页面中的元素。
2.jQuery 链接
jQuery的第三个设计思路是网页元素最终被选中后,可以对其进行一系列的操作,所有的操作可以连在一起,写成链的形式。
例子:
$('div').find('h3').eq(2).html('Hello');
3.jQuery 创建元素
(1)使用$函数创建新元素
var $newElement=$('<p>段落');//创建元素,返回jQuery对象</p>
(2)添加子元素
使用追加方法
使用 appendTo 方法
4.移动元素
操纵网页中元素的位置和移动。一组方法是直接移动元素,另一组方法是移动其他元素,使目标元素在我们想要的位置。
假设我们选择了一个 div 元素,需要将它移到 p 元素的后面。
第一种方法是使用.insertAfter()将div元素移到p元素后面:
$('div').insertAfter($('p'));
第二种方法是使用.after()将p元素添加到div元素的前面:
$('p').after($('div'));
从表面上看,两种方法的效果是一样的,唯一的区别似乎是操作的视角不同。
5.修改元素属性
在jquery中,可以使用attr()方法来修改元素的属性和内容。
例子:
$("button").click(function(){
$("#w3s").attr("href","http:www.123.com.cn");
});
attr() 也提供回调函数。
回调函数有两个参数:当前元素在被选元素列表中的索引,以及原创(旧)值。然后返回你希望使用的字符串和函数的新值。
例子:
$("button").click(function(){
$("#s").attr("href", function(i,Value){
return Value + "/jquery";
});
});
测评:百度链接提交与自动抓取的区别,该如何选择?
那么,百度链接提交和自动抓取有什么区别,如何选择?
根据以往百度网站提交的经验,T3模板网将通过以下内容进行讲解:
1、主动提交
对于普通收录和快速收录权限,我们认为它具有以下特点:
①提高搜索引擎发现新链接的时间。
2)快速进入百度搜索指数评测频道,简单理解就是增加指数量。
③节省目标页面被百度发现的成本,如:外链建设,引蜘蛛的成本。
但同时,根据我们广泛的测试,这种形式的数据提交仍然受到以下因素的影响,例如:
①网站链接提交的时间节点。
②网站链接提交次数和频率。
当搜索引擎的爬取通道太忙时,很容易造成一些URL地址的遗漏,也就是在不同的时间点提交URL,收录的数量可能会有很大的变化。
其次,如果网站链接提交的次数和频率过于密集,整个站点的链接率也会有一定的波动。
2、自动爬取
一般来说,所谓搜索引擎自动爬取主要是指百度蜘蛛主动爬取你的页面内容,主要受以下因素影响:
①优质外链的数量和增长的频率。
②网站优质内容的更新次数占整个网站内容的比例。
③页面内容的更新频率。
一般来说,如果能在某个时间节点保持一定程度的活跃度,自动爬取是非常有规律的,而且随着整个站点质量的提高,网站很容易进入“秒收录”的状态。
这个时候,我们根本不需要考虑。对于链接提交的问题,只要更新内容,我们就可以继续得到收录。
同时,我们根据一些日常操作做了基本的判断,发现:
如果你主动提交的链接质量比例不高,是对搜索引擎的质量评价,可以很快得到。如果长期处于低质量状态,很容易出现链接不良收录,同时引起自动爬取的页面虽然是收录,但大部分会进入低质量库,即检索相关的新页面,没有任何排名。
相比之下,我们认为自动抓取在这方面可能相对广泛。
3、合理的选择
综合以上因素,我们认为如果你有能力,我们还是建议你选择网站让百度搜索自动爬取和收录,并且可以适当减少< @网站 链接,除非:
①你的外链资源有限,很难建立一些比较优质的链接。
②您的目标页面在全站目录层次较深,搜索引擎难以发现和抓取。
③您的网站是新推出的,尚未经过沙盒或质量评估。 (使用链接提交,可以快速通过这个循环,前提是结构和内容质量一定要好。)
总结:seo是一项细致的工作,我们应该善于发现百度搜索产品的差异,当然以上内容只是经验,仅供参考。 查看全部
jquery抓取网页内容 技巧:二.主要设计操作
一.基本信息
jQuery 于 2006 年发布,目前是前端寿命最长的库。它是世界上使用最广泛的图书馆,拥有全球80%以上的网站。
二.主要设计操作
1.获取网页元素
一种是通过jQuery选择器选择元素,可以直接获取单个或批量元素;
另一种是通过jQuery遍历关联来选择元素,常用于获取层级比较复杂的页面中的元素。
2.jQuery 链接
jQuery的第三个设计思路是网页元素最终被选中后,可以对其进行一系列的操作,所有的操作可以连在一起,写成链的形式。
例子:
$('div').find('h3').eq(2).html('Hello');
3.jQuery 创建元素
(1)使用$函数创建新元素
var $newElement=$('<p>段落');//创建元素,返回jQuery对象</p>

(2)添加子元素
使用追加方法
使用 appendTo 方法
4.移动元素
操纵网页中元素的位置和移动。一组方法是直接移动元素,另一组方法是移动其他元素,使目标元素在我们想要的位置。
假设我们选择了一个 div 元素,需要将它移到 p 元素的后面。
第一种方法是使用.insertAfter()将div元素移到p元素后面:
$('div').insertAfter($('p'));
第二种方法是使用.after()将p元素添加到div元素的前面:
$('p').after($('div'));
从表面上看,两种方法的效果是一样的,唯一的区别似乎是操作的视角不同。
5.修改元素属性

在jquery中,可以使用attr()方法来修改元素的属性和内容。
例子:
$("button").click(function(){
$("#w3s").attr("href","http:www.123.com.cn");
});
attr() 也提供回调函数。
回调函数有两个参数:当前元素在被选元素列表中的索引,以及原创(旧)值。然后返回你希望使用的字符串和函数的新值。
例子:
$("button").click(function(){
$("#s").attr("href", function(i,Value){
return Value + "/jquery";
});
});
测评:百度链接提交与自动抓取的区别,该如何选择?
那么,百度链接提交和自动抓取有什么区别,如何选择?
根据以往百度网站提交的经验,T3模板网将通过以下内容进行讲解:
1、主动提交
对于普通收录和快速收录权限,我们认为它具有以下特点:
①提高搜索引擎发现新链接的时间。
2)快速进入百度搜索指数评测频道,简单理解就是增加指数量。
③节省目标页面被百度发现的成本,如:外链建设,引蜘蛛的成本。
但同时,根据我们广泛的测试,这种形式的数据提交仍然受到以下因素的影响,例如:
①网站链接提交的时间节点。

②网站链接提交次数和频率。
当搜索引擎的爬取通道太忙时,很容易造成一些URL地址的遗漏,也就是在不同的时间点提交URL,收录的数量可能会有很大的变化。
其次,如果网站链接提交的次数和频率过于密集,整个站点的链接率也会有一定的波动。
2、自动爬取
一般来说,所谓搜索引擎自动爬取主要是指百度蜘蛛主动爬取你的页面内容,主要受以下因素影响:
①优质外链的数量和增长的频率。
②网站优质内容的更新次数占整个网站内容的比例。
③页面内容的更新频率。
一般来说,如果能在某个时间节点保持一定程度的活跃度,自动爬取是非常有规律的,而且随着整个站点质量的提高,网站很容易进入“秒收录”的状态。
这个时候,我们根本不需要考虑。对于链接提交的问题,只要更新内容,我们就可以继续得到收录。

同时,我们根据一些日常操作做了基本的判断,发现:
如果你主动提交的链接质量比例不高,是对搜索引擎的质量评价,可以很快得到。如果长期处于低质量状态,很容易出现链接不良收录,同时引起自动爬取的页面虽然是收录,但大部分会进入低质量库,即检索相关的新页面,没有任何排名。
相比之下,我们认为自动抓取在这方面可能相对广泛。
3、合理的选择
综合以上因素,我们认为如果你有能力,我们还是建议你选择网站让百度搜索自动爬取和收录,并且可以适当减少< @网站 链接,除非:
①你的外链资源有限,很难建立一些比较优质的链接。
②您的目标页面在全站目录层次较深,搜索引擎难以发现和抓取。
③您的网站是新推出的,尚未经过沙盒或质量评估。 (使用链接提交,可以快速通过这个循环,前提是结构和内容质量一定要好。)
总结:seo是一项细致的工作,我们应该善于发现百度搜索产品的差异,当然以上内容只是经验,仅供参考。
免费获取:网页数据抓取-免费网站爬虫数据抓取软件自动抓取发布软件
网站优化 • 优采云 发表了文章 • 0 个评论 • 94 次浏览 • 2022-09-23 11:10
网页数据抓取,什么是网页抓取。如何快速抓取网络数据。今天给大家分享一款免费的网页数据抓取软件,只需要输入网站域名自动抓取网站页面数据,自动抓取网页数据+自动网站< @k6@ > 发帖,详情请看图片。
选择域名很重要,因为这是网站优化的第一步。注册域名时,我们首先要找一个与主题相关的域名。首选是中文拼音,其次是英文。如果不是,请选择一个域名较短的域名。它是衡量SEO效果的必要因素,虽然它对SEO的效果影响不大。很大,但我们还是要跟着,一定的水平有利于后期的优化和传播。
1)注册的域名越短越好。域名越短,越容易记住。域名注册可以去万网或者其他大品牌,因为安全性会更好。
2)为网站的主题选择一个域名
注册域名时需要定位网站主题,根据网站主题选择相关域名
3)关键字优先使用中文拼音,关键字优先使用英文。现在指定的域名不用直接选择双拼了,推荐双拼+号的方式。另外,建议一次性注册一组域名,以免其他域名被别人注册,日后要天价。 , 如果英语语言注册最好针对目标群体,如果是大众群体,不利于优化交流。网页数据抓取,例如站长组,对英文包括bbs、news、blog等比较熟悉,好记。
4).Com .Cn .Org . . . .net .gov .edu
域名的后缀对于SEO也很重要。一般来说,.gov .edu 非政府和教育机构无权注册,但.gov .edu 域名拥有最高权限,.com .org .gov 国际域名更适合国内域名。 . . ,主要是声誉成本问题。
注意:国际域名升值空间更大。 .cc .tv .me 等其他域名使用相对较少。不建议注册一些不常见的声誉后缀,除非你是专门做域名研究的,否则就是浪费钱。
5)域名注册时间越长越好
搜索引擎无论是否抓取网络,都会抓取域名的whois信息。网页数据爬取包括域名的whois信息,如注册时间、过期时间等。
既然seo是网站的收录,那么它和数量有直接关系,原因是由于网站的搜索效果的概率。更何况长尾关键词,长尾关键词的排名优化会直接影响长尾关键词的排名次数,网页数据抓取而网站的排名有数百个因素影响网站收录。
那我给大家讲讲影响网站收录的因素,首先是网站的开启速度,网站的开启速度大家都知道速度不仅影响你的网站,对用户体验也不利。网页数据爬取,那你觉得如果网站打开慢会影响蜘蛛的阅读,所以网站打开速度很重要,关于影响网站@打开速度> 原因包括服务器提供商、服务器宽带速度、服务器硬件质量、服务器操作系统、服务器软件操作、DNS等。
接下来是网站权重的影响。每个人可能都不知道网站的重量。这个权重问题将直接关系到搜索引擎对网站的信誉值的评价。 网站 如果权重高,那么搜索引擎的抓取会更及时,有时会秒到。这也是由于该站点的权重相对较高。相反,如果网站的权重很低,则证明搜索引擎对网站的声誉值的评价很低。蜘蛛对站点的爬取频率会低,一旦站点正常,灰腕很可能直接被K驻扎。
其次要说一下网站结构的结构设置,结构外观美观,小场地多规划为平面结构,大场地多规划为树结构。网页数据抓取是我们的网站结构和规划复杂,复杂的内部链规划会导致网站收录慢,尤其是二级栏目和三级内容页面收录 甚至更慢甚至没有网站 @收录。比如我们要建一栋楼,每一层都要一样,每一层的结构都要非常坚固。因此,当我们规划网站的构建时,不同的列可以直接链接内部链。同时网站的目录深度最好限制在三层,以免影响蜘蛛对收录爬得太深。
免费:如何推广网站维护托管蜘蛛抓取爬虫
如果一个网站想要排名好,肯定有蜘蛛在爬。蜘蛛爬网以获取它想要的信息。 网站 的链接起着非常重要的作用。如果要增加网站,就必须增加蜘蛛爬行的频率。那么如何在网站维护、托管和维护中增加蜘蛛爬取的频率呢?首先我们要了解影响蜘蛛爬取频率的因素: 1网站结构:网站在构造时,首先选择短域名,简化目录层次,避免网址过长或动态参数太多,短的更容易记住。 2 页面速度:百度多次提到的移动优先级指标,最重要的是页面的首次加载速度,必须控制在3秒以内。 3 入站链接:理论上只要是外部链接,无论其质量和形状如何,都可以引导爬虫进行爬取。 4 主动提交:主动提交网站地图、JS访问、官方API等,接下来看看如何提高网站维护主机维护百度蜘蛛爬虫抓取频率:1、网站更新频率:网站只要更新频率越高,爬虫就会越多。 2. 网站重量:重 网站 轨道也经常抓取。 3. 网站内容质量:网站内容新颖优质,可以解决用户的问题,提高爬虫的爬取频率。 4、外链和内链的构建:链接是页面的入口,优质的外链和内链可以更好的引导蜘蛛进入和捕获。希望小编分享的关于网站维护主机维护增加蜘蛛爬取频率的内容可以对大家有所帮助。
查看全部
免费获取:网页数据抓取-免费网站爬虫数据抓取软件自动抓取发布软件
网页数据抓取,什么是网页抓取。如何快速抓取网络数据。今天给大家分享一款免费的网页数据抓取软件,只需要输入网站域名自动抓取网站页面数据,自动抓取网页数据+自动网站< @k6@ > 发帖,详情请看图片。
选择域名很重要,因为这是网站优化的第一步。注册域名时,我们首先要找一个与主题相关的域名。首选是中文拼音,其次是英文。如果不是,请选择一个域名较短的域名。它是衡量SEO效果的必要因素,虽然它对SEO的效果影响不大。很大,但我们还是要跟着,一定的水平有利于后期的优化和传播。
1)注册的域名越短越好。域名越短,越容易记住。域名注册可以去万网或者其他大品牌,因为安全性会更好。
2)为网站的主题选择一个域名
注册域名时需要定位网站主题,根据网站主题选择相关域名

3)关键字优先使用中文拼音,关键字优先使用英文。现在指定的域名不用直接选择双拼了,推荐双拼+号的方式。另外,建议一次性注册一组域名,以免其他域名被别人注册,日后要天价。 , 如果英语语言注册最好针对目标群体,如果是大众群体,不利于优化交流。网页数据抓取,例如站长组,对英文包括bbs、news、blog等比较熟悉,好记。
4).Com .Cn .Org . . . .net .gov .edu
域名的后缀对于SEO也很重要。一般来说,.gov .edu 非政府和教育机构无权注册,但.gov .edu 域名拥有最高权限,.com .org .gov 国际域名更适合国内域名。 . . ,主要是声誉成本问题。
注意:国际域名升值空间更大。 .cc .tv .me 等其他域名使用相对较少。不建议注册一些不常见的声誉后缀,除非你是专门做域名研究的,否则就是浪费钱。
5)域名注册时间越长越好
搜索引擎无论是否抓取网络,都会抓取域名的whois信息。网页数据爬取包括域名的whois信息,如注册时间、过期时间等。

既然seo是网站的收录,那么它和数量有直接关系,原因是由于网站的搜索效果的概率。更何况长尾关键词,长尾关键词的排名优化会直接影响长尾关键词的排名次数,网页数据抓取而网站的排名有数百个因素影响网站收录。
那我给大家讲讲影响网站收录的因素,首先是网站的开启速度,网站的开启速度大家都知道速度不仅影响你的网站,对用户体验也不利。网页数据爬取,那你觉得如果网站打开慢会影响蜘蛛的阅读,所以网站打开速度很重要,关于影响网站@打开速度> 原因包括服务器提供商、服务器宽带速度、服务器硬件质量、服务器操作系统、服务器软件操作、DNS等。
接下来是网站权重的影响。每个人可能都不知道网站的重量。这个权重问题将直接关系到搜索引擎对网站的信誉值的评价。 网站 如果权重高,那么搜索引擎的抓取会更及时,有时会秒到。这也是由于该站点的权重相对较高。相反,如果网站的权重很低,则证明搜索引擎对网站的声誉值的评价很低。蜘蛛对站点的爬取频率会低,一旦站点正常,灰腕很可能直接被K驻扎。
其次要说一下网站结构的结构设置,结构外观美观,小场地多规划为平面结构,大场地多规划为树结构。网页数据抓取是我们的网站结构和规划复杂,复杂的内部链规划会导致网站收录慢,尤其是二级栏目和三级内容页面收录 甚至更慢甚至没有网站 @收录。比如我们要建一栋楼,每一层都要一样,每一层的结构都要非常坚固。因此,当我们规划网站的构建时,不同的列可以直接链接内部链。同时网站的目录深度最好限制在三层,以免影响蜘蛛对收录爬得太深。
免费:如何推广网站维护托管蜘蛛抓取爬虫

如果一个网站想要排名好,肯定有蜘蛛在爬。蜘蛛爬网以获取它想要的信息。 网站 的链接起着非常重要的作用。如果要增加网站,就必须增加蜘蛛爬行的频率。那么如何在网站维护、托管和维护中增加蜘蛛爬取的频率呢?首先我们要了解影响蜘蛛爬取频率的因素: 1网站结构:网站在构造时,首先选择短域名,简化目录层次,避免网址过长或动态参数太多,短的更容易记住。 2 页面速度:百度多次提到的移动优先级指标,最重要的是页面的首次加载速度,必须控制在3秒以内。 3 入站链接:理论上只要是外部链接,无论其质量和形状如何,都可以引导爬虫进行爬取。 4 主动提交:主动提交网站地图、JS访问、官方API等,接下来看看如何提高网站维护主机维护百度蜘蛛爬虫抓取频率:1、网站更新频率:网站只要更新频率越高,爬虫就会越多。 2. 网站重量:重 网站 轨道也经常抓取。 3. 网站内容质量:网站内容新颖优质,可以解决用户的问题,提高爬虫的爬取频率。 4、外链和内链的构建:链接是页面的入口,优质的外链和内链可以更好的引导蜘蛛进入和捕获。希望小编分享的关于网站维护主机维护增加蜘蛛爬取频率的内容可以对大家有所帮助。

解决方案:搜索引擎页面抓取方式?(附网站结构应该怎样部署才能实现双赢呢)
网站优化 • 优采云 发表了文章 • 0 个评论 • 94 次浏览 • 2022-09-23 11:09
8.2 搜索引擎抓取重要页面 搜索引擎如何抓取网站中比较重要的页面。
由于互联网信息量巨大,为了向用户展示更有价值的信息,搜索引擎会优先抓取每个网站@中比较重要的页面(即权限较高的页面) >。但是,搜索引擎是如何发现这些相对重要的页面的呢?根据重要页面的链接指向的页面也可能是重要页面的思路,搜索引擎会首先从权限比较高的页面(即源页面1)开始,跟踪其中的链接,从而去爬取其他比较重要的页面(即目标页面1)页面1)。
1 在链接关系中,我们称存储链接的页面为源页面,链接指向的页面为目标页面。
1 在链接关系中,我们称存储链接的页面为源页面,链接指向的页面为目标页面。
例如,一般情况下,搜索引擎会从网站的首页开始,跟随其中的链接,爬取网站中其他比较重要的页面。
由此我们了解到,增加页面成为收录的机会的最佳方法是缩短页面与重要页面之间的链接路径。
站长在搭建网站的时候,会接触到网站的各种元素。其中,网站结构是一个非常重要的环节。关于网站结构不否认有些人会直接忽略,但是网站结构可以涉及到网站的很多方面,在网站的优化中,关于网站结构的优化也是必不可少的。有的站长甚至将结构优化划分为单独的优化重点。可见,网站结构的重要性我们就不用多说了。 网站后期收录包括用户体验好不好很大程度上取决于网站的结构。如果网站遵循用户的操作习惯,符合搜索引擎的爬取状态,那么网站自然会受到用户的喜爱,所以网站应该结构清晰,导航简洁,方便用户找到在最短的时间内获得相应的内容,还可以促进搜索引擎对站点信息和收录的爬取。聊了这么久,我们应该如何部署网站结构才能实现双赢呢?别着急,往下看。
首先要说的是网站构造前的排版问题。每个站长对于网站的排版都有自己的想法,但有些是为了突出网站的区别个性化,违反用户操作习惯在网站上部署,这样的行为会适得其反首先要保证用户打开页面的第一印象不会恶心,这样网站才有机会留住用户,包括前期的页面加载时间。如果时间过长,用户可以直接关闭页面。电路板的布局应根据用户习惯进行设计。一个与用户体验背道而驰的网站,必然要进行有效的改进。
我们在上面谈到了页面加载速度的问题。造成这种现象的因素很多。其中最重要的因素之一是在 网站 页面中部署了大量的 image flash 格式的文件。虽然网站的视觉效果有了一定的提升,但是由于文件比较大,网页的加载时间也受到了影响,包括网站JS等,建议减少尽可能多的这样的文件。使用div+css更好的迎合搜索引擎的爬取,包括网站的404页等,可以在一定程度上提升网站的用户体验。
接下来说一个常见的,就是网站的面包屑导航。相信大家都接触过这种导航方式。面包屑导航有优点也有缺点。 网站简洁明了的结构是为了让用户在最短的时间内找到自己需要的信息,所以面包屑导航可以引导用户方便快捷地浏览网站,告知用户当前位置等信息,这样用户就不会在网站上迷路。通过面包屑导航的部署,用户可以清楚的知道自己的页面在整个网站中的具体位置,也方便用户返回上一级。另一方面,也能很好地迎合搜索引擎对网站信息的抓取。蜘蛛可以通过面包屑导航路径一一抓取网站信息,从而达到促进网站收录改进的目的,所以面包屑导航也是一个重要且不可缺少的功能站长优化网站.
<p>最后说一下网站框架的问题。大家一定听说过,在网站的优化中尽量避免框架的出现会影响收录的问题。错了,对于页面中的某个block,frame是固定的,其他部分的信息可以通过滚动点来显示。虽然框架使用起来很方便,但是在这样的页面中,搜索引擎无法抓取到更多的信息,所以会影响到网站整体收录,建议在 查看全部
解决方案:搜索引擎页面抓取方式?(附网站结构应该怎样部署才能实现双赢呢)
8.2 搜索引擎抓取重要页面 搜索引擎如何抓取网站中比较重要的页面。
由于互联网信息量巨大,为了向用户展示更有价值的信息,搜索引擎会优先抓取每个网站@中比较重要的页面(即权限较高的页面) >。但是,搜索引擎是如何发现这些相对重要的页面的呢?根据重要页面的链接指向的页面也可能是重要页面的思路,搜索引擎会首先从权限比较高的页面(即源页面1)开始,跟踪其中的链接,从而去爬取其他比较重要的页面(即目标页面1)页面1)。
1 在链接关系中,我们称存储链接的页面为源页面,链接指向的页面为目标页面。
1 在链接关系中,我们称存储链接的页面为源页面,链接指向的页面为目标页面。
例如,一般情况下,搜索引擎会从网站的首页开始,跟随其中的链接,爬取网站中其他比较重要的页面。

由此我们了解到,增加页面成为收录的机会的最佳方法是缩短页面与重要页面之间的链接路径。
站长在搭建网站的时候,会接触到网站的各种元素。其中,网站结构是一个非常重要的环节。关于网站结构不否认有些人会直接忽略,但是网站结构可以涉及到网站的很多方面,在网站的优化中,关于网站结构的优化也是必不可少的。有的站长甚至将结构优化划分为单独的优化重点。可见,网站结构的重要性我们就不用多说了。 网站后期收录包括用户体验好不好很大程度上取决于网站的结构。如果网站遵循用户的操作习惯,符合搜索引擎的爬取状态,那么网站自然会受到用户的喜爱,所以网站应该结构清晰,导航简洁,方便用户找到在最短的时间内获得相应的内容,还可以促进搜索引擎对站点信息和收录的爬取。聊了这么久,我们应该如何部署网站结构才能实现双赢呢?别着急,往下看。
首先要说的是网站构造前的排版问题。每个站长对于网站的排版都有自己的想法,但有些是为了突出网站的区别个性化,违反用户操作习惯在网站上部署,这样的行为会适得其反首先要保证用户打开页面的第一印象不会恶心,这样网站才有机会留住用户,包括前期的页面加载时间。如果时间过长,用户可以直接关闭页面。电路板的布局应根据用户习惯进行设计。一个与用户体验背道而驰的网站,必然要进行有效的改进。
我们在上面谈到了页面加载速度的问题。造成这种现象的因素很多。其中最重要的因素之一是在 网站 页面中部署了大量的 image flash 格式的文件。虽然网站的视觉效果有了一定的提升,但是由于文件比较大,网页的加载时间也受到了影响,包括网站JS等,建议减少尽可能多的这样的文件。使用div+css更好的迎合搜索引擎的爬取,包括网站的404页等,可以在一定程度上提升网站的用户体验。

接下来说一个常见的,就是网站的面包屑导航。相信大家都接触过这种导航方式。面包屑导航有优点也有缺点。 网站简洁明了的结构是为了让用户在最短的时间内找到自己需要的信息,所以面包屑导航可以引导用户方便快捷地浏览网站,告知用户当前位置等信息,这样用户就不会在网站上迷路。通过面包屑导航的部署,用户可以清楚的知道自己的页面在整个网站中的具体位置,也方便用户返回上一级。另一方面,也能很好地迎合搜索引擎对网站信息的抓取。蜘蛛可以通过面包屑导航路径一一抓取网站信息,从而达到促进网站收录改进的目的,所以面包屑导航也是一个重要且不可缺少的功能站长优化网站.
<p>最后说一下网站框架的问题。大家一定听说过,在网站的优化中尽量避免框架的出现会影响收录的问题。错了,对于页面中的某个block,frame是固定的,其他部分的信息可以通过滚动点来显示。虽然框架使用起来很方便,但是在这样的页面中,搜索引擎无法抓取到更多的信息,所以会影响到网站整体收录,建议在
jquery抓取网页内容有两种实现:+axios来网页
网站优化 • 优采云 发表了文章 • 0 个评论 • 147 次浏览 • 2022-09-20 12:18
jquery抓取网页内容有两种实现:
1、继承webqueryinternet.gethtmlstream('js')的prequery方法,根据传入的代码请求页面内容。
2、第三方js库,把页面内容以html格式合成到js格式中,再合成回string。从而实现网页抓取功能。抓取js部分要抓取jquery源码,这个太难了,一般是要自己写脚本去解析,这样的效率并不高。现在可以用ajax实现,但ajax抓取只抓取js,对css提取没有办法。所以采用phantomjs+axios来抓取网页内容。
首先我们要先在浏览器中创建一个窗口,我将创建一个名叫phantomjs的浏览器,代码为constphantomjs=newwebdriver。phantomjs({profileurl:phantomjs。url。create('phantomjs'),pages:[],documentopen:function(request,response){console。log('请求'+request。url);}});然后在web浏览器中编写网页内容抓取方法。
1、网址采用get方法:constqueryobject={//获取本页所有的html元素//1个object.link结尾是指该内容是一个html元素。不支持长锚链接}constresult={};//数据url是指网页urlconstdataurl={//获取网页源码信息fileurl:'/webstorm/sharp_xml.js'}constmyheader='title'constresultheader={}//获取网页全部内容fromjson.node_env.array.map(constobj=newdate('y-m-d-1'),consttype='string',constpageslink={maxratio:type.getratio(),maxrows:0,maxly:0,alllocations:type.getratio(),origin:type.getratio(),relativeregion:0,followregion:1,origin:type.getratio(),dropload:type.getratio(),nocached:null,useragent:null,scrollfilename:'',//获取地址filename:'',//去掉隐藏的parent字段usercontent:'',//去掉默认图片字段pageurl:''})//获取第一页内容fromjson.node_env.array.map(constobj=newdate('y-m-d-1'),constclienturl='',constcontenturl='')//设置username=''exports.clienturl='/webstorm/sharp_xml.js'exports.clienturl=''exports.myheader=''exports.m。 查看全部
jquery抓取网页内容有两种实现:+axios来网页
jquery抓取网页内容有两种实现:

1、继承webqueryinternet.gethtmlstream('js')的prequery方法,根据传入的代码请求页面内容。
2、第三方js库,把页面内容以html格式合成到js格式中,再合成回string。从而实现网页抓取功能。抓取js部分要抓取jquery源码,这个太难了,一般是要自己写脚本去解析,这样的效率并不高。现在可以用ajax实现,但ajax抓取只抓取js,对css提取没有办法。所以采用phantomjs+axios来抓取网页内容。

首先我们要先在浏览器中创建一个窗口,我将创建一个名叫phantomjs的浏览器,代码为constphantomjs=newwebdriver。phantomjs({profileurl:phantomjs。url。create('phantomjs'),pages:[],documentopen:function(request,response){console。log('请求'+request。url);}});然后在web浏览器中编写网页内容抓取方法。
1、网址采用get方法:constqueryobject={//获取本页所有的html元素//1个object.link结尾是指该内容是一个html元素。不支持长锚链接}constresult={};//数据url是指网页urlconstdataurl={//获取网页源码信息fileurl:'/webstorm/sharp_xml.js'}constmyheader='title'constresultheader={}//获取网页全部内容fromjson.node_env.array.map(constobj=newdate('y-m-d-1'),consttype='string',constpageslink={maxratio:type.getratio(),maxrows:0,maxly:0,alllocations:type.getratio(),origin:type.getratio(),relativeregion:0,followregion:1,origin:type.getratio(),dropload:type.getratio(),nocached:null,useragent:null,scrollfilename:'',//获取地址filename:'',//去掉隐藏的parent字段usercontent:'',//去掉默认图片字段pageurl:''})//获取第一页内容fromjson.node_env.array.map(constobj=newdate('y-m-d-1'),constclienturl='',constcontenturl='')//设置username=''exports.clienturl='/webstorm/sharp_xml.js'exports.clienturl=''exports.myheader=''exports.m。
用Python轻松制作一个股票K线图网站
网站优化 • 优采云 发表了文章 • 0 个评论 • 148 次浏览 • 2022-09-18 10:48
在前面的文章中,我们学习了如何使用 Tkinter 构建股票数据抓取以及展示K线图功能,虽然大致的功能已经具备,但是在当今这个人手一个 Web 服务的年代,GUI 程序还是没有 Web 服务来的香啊。
我们需要用到的知识包括 PyEcharts 的使用,tushare 库获取股票数据的方法以及 Flask 的基本用法。
获取股票数据
我们先来看下 tushare 的使用,这个应该是当前最为流行的股票数据库了吧,一行代码,就能轻松获取某支股票的历史数据
import tushare as ts<br />df = ts.get_hist_data('000001')<br />print(df)<br />
现在股票的历史数据有了,我们还需要一份股票名称和股票代码的对应表,同样通过 tushare 来获取
stock_list = ts.get_stock_basics()<br />stock_list.reset_index(inplace=True)<br />stock_list[['code', 'name']].to_csv('stock_code_name.csv')<br />
这样就成功保存了一份股票名称和股票代码的对应数据
PyEcharts 作图
下面再来看看如何通过 PyEcharts 来制作 K 线图,其实官网上的例子已经非常具体了,我们只需要把拿到的历史股票数据做些简单处理即可,我这里直接给我的数据处理过程
mydate = df[:30].index.tolist()<br />mydata = df[:30][['open', 'close', 'low', 'high']].values.tolist()<br /><br /><br />def kline_base(mydate, data) -> Kline:<br /> c = (<br /> Kline()<br /> .add_xaxis(mydate)<br /> .add_yaxis("kline", data)<br /> .set_global_opts(<br /> yaxis_opts=opts.AxisOpts(is_scale=True,<br /> splitarea_opts=opts.SplitAreaOpts(<br /> is_show=True, areastyle_opts=opts.AreaStyleOpts(opacity=1)<br /> ),<br /> ),<br /> xaxis_opts=opts.AxisOpts(is_scale=True,<br /> axislabel_opts=opts.LabelOpts(rotate=-30)),<br /> title_opts=opts.TitleOpts(title="Kline-基本示例"),<br /> datazoom_opts=[opts.DataZoomOpts()],<br /> )<br /> )<br /> return c<br /><br />kline_base(mydate, mydata).render_notebook()<br />
这样就可以得到一个不错的 K 线图了
下面我们就可以着手完成 Flask 的代码啦
构建 Web 框架
首先我们先来完成 Web 框架的整体编写,为了页面的美观与编码的方便,直接使用 bootstrap 来构建前端页面
视图函数编写
首先完成初始化工作,在项目目录下创建一个 app.py 文件
from flask import Flask, render_template, request<br />from pyecharts import options as opts<br />from pyecharts.charts import Kline<br />import tushare as ts<br />import pandas as pd<br />from flask_bootstrap import Bootstrap<br /><br />app = Flask(__name__)<br />bootstrap = Bootstrap(app)<br />
导入需要用到的库,并完成 flask app 的初始化工作。
接下来再写一个 404 的视图函数,统一处理所有的 Not Found 页面
@app.errorhandler(404)<br />def page_not_found(e):<br /> return render_template("404.html"), 404<br />
接着我们绑定根地址到 index 视图函数上,返回到 index.html 模板文件上
@app.route("/")<br />def index():<br /> return render_template("index.html")<br />
模板编写
在同级目录创建一个 templates 文件夹,创建三个 HTML 文件,分别为 404.html,base.html 和 index.html
base.html 是所有其他页面 HTML 模板的母模板
{% extends "bootstrap/base.html" %}<br /><br />{% block title %}我的股票走势网站{% endblock %}<br /><br /><br />{% block navbar %}<br /><br /> <br /> <br /> <br /> Toggle navigation<br /> <br /> <br /> <br /> <br /> Stock-Data<br /> <br /> <br /> <br /> Home<br /> <br /> <br /> <br /><br />{% endblock %}<br /><br />{% block content %}<br /><br /> {% block page_content %}<br /> {% endblock %}<br /><br />{% endblock %}<br />
创建一个导航栏,并定义相关的 block 内容
接下来编写 404.html 文件,展示非法 url 请求地址时的页面
{% extends "base.html" %}<br /><br />{% block title %}Page Not Found{% endblock %}<br /><br />{% block page_content %}<br /><br /> Not Found<br /><br />{% endblock %}<br />
对于 index.html 文件,就是我们需要展示 K 线图的页面,我们后面再处理。
编辑主逻辑
首先编写一个检查股票正确性的函数
def check_stock(code):<br /> n = 0<br /> l = []<br /> stock_code = pd.read_csv("stock_code_name.csv", dtype=object)<br /> stock_code.drop('Unnamed: 0', axis=1, inplace=True)<br /> stock_list = stock_code.values.tolist()<br /> for i in stock_list:<br /> if code in i:<br /> n += 1<br /> l = i<br /> else:<br /> continue<br /> return n, l<br />
如果股票正确,则返回 n=1,否则返回 n=0
接下来再编写获取股票数据的函数
def get_stock_data(code, ctime):<br /> df = ts.get_hist_data(code)<br /> mydate = df[:ctime].index.tolist()<br /> mydata = df[:ctime][['open', 'close', 'low', 'high']].values.tolist()<br /> return [mydate, mydata]<br />
下面就是把 PyEcharts 集成到 Flask 应用了,可以按照官方的教程走,把 PyEcharts 的样式文件等拷贝到自己的 templates 目录下,再编写一个用于调用 kline_base() 函数的视图函数
@app.route("/Kline", methods=['GET', 'POST'])<br />def get_kline_chart():<br /> stock_name = request.form.get('stockName')<br /> query_time = request.form.get('queryTime')<br /> if not stock_name:<br /> stock_name = '平安银行'<br /> if not query_time:<br /> query_time = 30<br /> status, stock_code = check_stock(stock_name)<br /> if status == 0:<br /> return 'error stock code or name'<br /> mydate, mydata = get_stock_data(stock_code[0], int(query_time))<br /> c = kline_base(mydate, mydata, stock_code[1])<br /> return c.dump_options()<br />
首先通过 request 变量获取到前端传递过来的数据,分别为 stockName 和 queryTime,如果这两个参数是空值时,则赋予它们一个默认值。
接着判断股票代码的正确性并获取股票历史数据。
最后调用 kline_base 函数画出 K 线图,并渲染到前端页面上。
前端页面编写
最后我们来完成前端页面的工作
首先定义一个表单,用于传递股票名称,查询时间
<br /> 股票名称:<br /> <br /> <br /> 查询时间:<br /> <br /> <br /> <br /> <br />
然后就是通过 JQuery 来动态获取数据
function getData() {<br /> var chart = echarts.init(document.getElementById('kline'), 'white', {renderer: 'canvas'});<br /> $.ajax({<br /> type: "POST",<br /> dataType: "json",<br /> url: "/Kline" ,<br /> data: $('#form1').serialize(),<br /> success: function (result) {<br /> chart.setOption(result);<br /> },<br /> error: function() {<br /> alert("错误的股票代码!");<br /> }<br /> });<br /> }<br />
最后我们看下整体的效果
是不是效果还不错呢,后面还可以继续添加功能来完善我们的小小网站!
【室长原理课】系列在不正经地科普一些互联网小知识,没有太多高深的内容,把这个系列分享给你的朋友吧!
喜欢此内容的人还喜欢
主线教程系列(有更新):
番外系列:
Pandas、Numpy系列:
数据可视化系列:
【Python教程】让图表的色彩更丰富!matplotlib中的colormap
办公自动化系列: 查看全部
用Python轻松制作一个股票K线图网站
在前面的文章中,我们学习了如何使用 Tkinter 构建股票数据抓取以及展示K线图功能,虽然大致的功能已经具备,但是在当今这个人手一个 Web 服务的年代,GUI 程序还是没有 Web 服务来的香啊。
我们需要用到的知识包括 PyEcharts 的使用,tushare 库获取股票数据的方法以及 Flask 的基本用法。
获取股票数据
我们先来看下 tushare 的使用,这个应该是当前最为流行的股票数据库了吧,一行代码,就能轻松获取某支股票的历史数据
import tushare as ts<br />df = ts.get_hist_data('000001')<br />print(df)<br />
现在股票的历史数据有了,我们还需要一份股票名称和股票代码的对应表,同样通过 tushare 来获取
stock_list = ts.get_stock_basics()<br />stock_list.reset_index(inplace=True)<br />stock_list[['code', 'name']].to_csv('stock_code_name.csv')<br />
这样就成功保存了一份股票名称和股票代码的对应数据
PyEcharts 作图
下面再来看看如何通过 PyEcharts 来制作 K 线图,其实官网上的例子已经非常具体了,我们只需要把拿到的历史股票数据做些简单处理即可,我这里直接给我的数据处理过程
mydate = df[:30].index.tolist()<br />mydata = df[:30][['open', 'close', 'low', 'high']].values.tolist()<br /><br /><br />def kline_base(mydate, data) -> Kline:<br /> c = (<br /> Kline()<br /> .add_xaxis(mydate)<br /> .add_yaxis("kline", data)<br /> .set_global_opts(<br /> yaxis_opts=opts.AxisOpts(is_scale=True,<br /> splitarea_opts=opts.SplitAreaOpts(<br /> is_show=True, areastyle_opts=opts.AreaStyleOpts(opacity=1)<br /> ),<br /> ),<br /> xaxis_opts=opts.AxisOpts(is_scale=True,<br /> axislabel_opts=opts.LabelOpts(rotate=-30)),<br /> title_opts=opts.TitleOpts(title="Kline-基本示例"),<br /> datazoom_opts=[opts.DataZoomOpts()],<br /> )<br /> )<br /> return c<br /><br />kline_base(mydate, mydata).render_notebook()<br />
这样就可以得到一个不错的 K 线图了
下面我们就可以着手完成 Flask 的代码啦
构建 Web 框架
首先我们先来完成 Web 框架的整体编写,为了页面的美观与编码的方便,直接使用 bootstrap 来构建前端页面
视图函数编写

首先完成初始化工作,在项目目录下创建一个 app.py 文件
from flask import Flask, render_template, request<br />from pyecharts import options as opts<br />from pyecharts.charts import Kline<br />import tushare as ts<br />import pandas as pd<br />from flask_bootstrap import Bootstrap<br /><br />app = Flask(__name__)<br />bootstrap = Bootstrap(app)<br />
导入需要用到的库,并完成 flask app 的初始化工作。
接下来再写一个 404 的视图函数,统一处理所有的 Not Found 页面
@app.errorhandler(404)<br />def page_not_found(e):<br /> return render_template("404.html"), 404<br />
接着我们绑定根地址到 index 视图函数上,返回到 index.html 模板文件上
@app.route("/")<br />def index():<br /> return render_template("index.html")<br />
模板编写
在同级目录创建一个 templates 文件夹,创建三个 HTML 文件,分别为 404.html,base.html 和 index.html
base.html 是所有其他页面 HTML 模板的母模板
{% extends "bootstrap/base.html" %}<br /><br />{% block title %}我的股票走势网站{% endblock %}<br /><br /><br />{% block navbar %}<br /><br /> <br /> <br /> <br /> Toggle navigation<br /> <br /> <br /> <br /> <br /> Stock-Data<br /> <br /> <br /> <br /> Home<br /> <br /> <br /> <br /><br />{% endblock %}<br /><br />{% block content %}<br /><br /> {% block page_content %}<br /> {% endblock %}<br /><br />{% endblock %}<br />
创建一个导航栏,并定义相关的 block 内容
接下来编写 404.html 文件,展示非法 url 请求地址时的页面
{% extends "base.html" %}<br /><br />{% block title %}Page Not Found{% endblock %}<br /><br />{% block page_content %}<br /><br /> Not Found<br /><br />{% endblock %}<br />
对于 index.html 文件,就是我们需要展示 K 线图的页面,我们后面再处理。
编辑主逻辑
首先编写一个检查股票正确性的函数
def check_stock(code):<br /> n = 0<br /> l = []<br /> stock_code = pd.read_csv("stock_code_name.csv", dtype=object)<br /> stock_code.drop('Unnamed: 0', axis=1, inplace=True)<br /> stock_list = stock_code.values.tolist()<br /> for i in stock_list:<br /> if code in i:<br /> n += 1<br /> l = i<br /> else:<br /> continue<br /> return n, l<br />
如果股票正确,则返回 n=1,否则返回 n=0
接下来再编写获取股票数据的函数
def get_stock_data(code, ctime):<br /> df = ts.get_hist_data(code)<br /> mydate = df[:ctime].index.tolist()<br /> mydata = df[:ctime][['open', 'close', 'low', 'high']].values.tolist()<br /> return [mydate, mydata]<br />

下面就是把 PyEcharts 集成到 Flask 应用了,可以按照官方的教程走,把 PyEcharts 的样式文件等拷贝到自己的 templates 目录下,再编写一个用于调用 kline_base() 函数的视图函数
@app.route("/Kline", methods=['GET', 'POST'])<br />def get_kline_chart():<br /> stock_name = request.form.get('stockName')<br /> query_time = request.form.get('queryTime')<br /> if not stock_name:<br /> stock_name = '平安银行'<br /> if not query_time:<br /> query_time = 30<br /> status, stock_code = check_stock(stock_name)<br /> if status == 0:<br /> return 'error stock code or name'<br /> mydate, mydata = get_stock_data(stock_code[0], int(query_time))<br /> c = kline_base(mydate, mydata, stock_code[1])<br /> return c.dump_options()<br />
首先通过 request 变量获取到前端传递过来的数据,分别为 stockName 和 queryTime,如果这两个参数是空值时,则赋予它们一个默认值。
接着判断股票代码的正确性并获取股票历史数据。
最后调用 kline_base 函数画出 K 线图,并渲染到前端页面上。
前端页面编写
最后我们来完成前端页面的工作
首先定义一个表单,用于传递股票名称,查询时间
<br /> 股票名称:<br /> <br /> <br /> 查询时间:<br /> <br /> <br /> <br /> <br />
然后就是通过 JQuery 来动态获取数据
function getData() {<br /> var chart = echarts.init(document.getElementById('kline'), 'white', {renderer: 'canvas'});<br /> $.ajax({<br /> type: "POST",<br /> dataType: "json",<br /> url: "/Kline" ,<br /> data: $('#form1').serialize(),<br /> success: function (result) {<br /> chart.setOption(result);<br /> },<br /> error: function() {<br /> alert("错误的股票代码!");<br /> }<br /> });<br /> }<br />
最后我们看下整体的效果
是不是效果还不错呢,后面还可以继续添加功能来完善我们的小小网站!
【室长原理课】系列在不正经地科普一些互联网小知识,没有太多高深的内容,把这个系列分享给你的朋友吧!
喜欢此内容的人还喜欢
主线教程系列(有更新):
番外系列:
Pandas、Numpy系列:
数据可视化系列:
【Python教程】让图表的色彩更丰富!matplotlib中的colormap
办公自动化系列:
jqueryjquery的方法有问题,我记得有个jquery,
网站优化 • 优采云 发表了文章 • 0 个评论 • 81 次浏览 • 2022-09-17 01:04
jquery抓取网页内容::$("#title").find("span").innerhtml()如果你想用bs3:$("#title").find("span").innerhtml().strip()
$('#title').find('span').children()
$('#title').find('span').children()即可。
如果你所想要抓取的数据不需要数据验证,那么建议你考虑用bs4来做,比较快。如果你不懂什么bs,那么可以使用jquery生成对象然后使用对象的属性接受值。
chrome...
jqueryscriptsrcexperimental下id为jquerylistdata的文件
用jquery就够了,
$('#title').find('span').strip()
jquery的方法有问题吧,我记得有个jquery,jquerytitleforlocalf...好像差不多都是这个意思,这个好像是google做的一个开源的解决你这个问题的框架。
$('#title').find('span').children()如果要防止抓取掉像github这样的网站的话建议上reverser。
$('#title').find('text').children()
html5里面id很重要的一个属性好像叫src 查看全部
jqueryjquery的方法有问题,我记得有个jquery,
jquery抓取网页内容::$("#title").find("span").innerhtml()如果你想用bs3:$("#title").find("span").innerhtml().strip()
$('#title').find('span').children()
$('#title').find('span').children()即可。

如果你所想要抓取的数据不需要数据验证,那么建议你考虑用bs4来做,比较快。如果你不懂什么bs,那么可以使用jquery生成对象然后使用对象的属性接受值。
chrome...
jqueryscriptsrcexperimental下id为jquerylistdata的文件
用jquery就够了,

$('#title').find('span').strip()
jquery的方法有问题吧,我记得有个jquery,jquerytitleforlocalf...好像差不多都是这个意思,这个好像是google做的一个开源的解决你这个问题的框架。
$('#title').find('span').children()如果要防止抓取掉像github这样的网站的话建议上reverser。
$('#title').find('text').children()
html5里面id很重要的一个属性好像叫src
jquery抓取网页内容用jquery抓取(标题1)_
网站优化 • 优采云 发表了文章 • 0 个评论 • 156 次浏览 • 2022-08-27 21:01
jquery抓取网页内容用jquery抓取网页内容,当用jquery。page()。getelementsbytagname('网页标题')或者jquery。page()。getelementsbytagname('标题1')。getelementsbytagname('标题2')即可获取到页面中所有标题,或使用jquery。
getelementsbytagname('标题1')。getelementsbytagname('标题2');。
简单说,jquery等,可以通过搜索一个关键字获取前面三页网页。更换关键字后每页只能抓取一页。
用jqueryscrapy抓取百度网页可以这样:1.定位页面,获取到每一个页面的url地址,然后用这个url开始构造urlhtml文件,这里我写一个简单的:#!/usr/bin/envpython#_*_coding:utf-8_*_importurllib.requestfrombs4importbeautifulsoupurl='/'netdata=urllib.request.urlopen(url)text=netdata.read()page=netdata.request(url=url)data=json.loads(text)2.html文件构造javascript文件构造:html.parser在web浏览器下可以用nodejs或者golang开发,可以通过文件构造实现抓取每一个html元素和css、js文件等。
<p>web页面抓取可以先用nodejs下的webpack插件,构建一个网页:'''@...defget_url(self):'''printstr(self.url)'''@...defget_html(self):'''printstr(self.html)'''page_list=json.loads(urllib.request.urlopen(self.url))'''@...defget_js(self):'''printstr(self.js)'''page_list=json.loads(urllib.request.urlopen(self.url))'''@...defget_xxx(self):'''printstr(self.xxx)'''returnstr(self.page_list)data=json.loads(text)'''可以利用javascript去加密data,然后存在一个js文件中:'''#def_w._webpage_page_policy(self):'''returnjsondata'''#def_w._webpage_policy(self):'''returnjsonjsdata_w=_w._webpage_policy(self)return_w3.接下来写一个模板语言'''/''' 查看全部
jquery抓取网页内容用jquery抓取(标题1)_
jquery抓取网页内容用jquery抓取网页内容,当用jquery。page()。getelementsbytagname('网页标题')或者jquery。page()。getelementsbytagname('标题1')。getelementsbytagname('标题2')即可获取到页面中所有标题,或使用jquery。

getelementsbytagname('标题1')。getelementsbytagname('标题2');。
简单说,jquery等,可以通过搜索一个关键字获取前面三页网页。更换关键字后每页只能抓取一页。

用jqueryscrapy抓取百度网页可以这样:1.定位页面,获取到每一个页面的url地址,然后用这个url开始构造urlhtml文件,这里我写一个简单的:#!/usr/bin/envpython#_*_coding:utf-8_*_importurllib.requestfrombs4importbeautifulsoupurl='/'netdata=urllib.request.urlopen(url)text=netdata.read()page=netdata.request(url=url)data=json.loads(text)2.html文件构造javascript文件构造:html.parser在web浏览器下可以用nodejs或者golang开发,可以通过文件构造实现抓取每一个html元素和css、js文件等。
<p>web页面抓取可以先用nodejs下的webpack插件,构建一个网页:'''@...defget_url(self):'''printstr(self.url)'''@...defget_html(self):'''printstr(self.html)'''page_list=json.loads(urllib.request.urlopen(self.url))'''@...defget_js(self):'''printstr(self.js)'''page_list=json.loads(urllib.request.urlopen(self.url))'''@...defget_xxx(self):'''printstr(self.xxx)'''returnstr(self.page_list)data=json.loads(text)'''可以利用javascript去加密data,然后存在一个js文件中:'''#def_w._webpage_page_policy(self):'''returnjsondata'''#def_w._webpage_policy(self):'''returnjsonjsdata_w=_w._webpage_policy(self)return_w3.接下来写一个模板语言'''/'''
豆瓣记者关爱:jquery抓取网页内容的一些技巧,放出链接
网站优化 • 优采云 发表了文章 • 0 个评论 • 74 次浏览 • 2022-08-27 06:05
jquery抓取网页内容是比较普遍常见的,但是对于javascript来说,也不是很熟悉,下面就由豆瓣记者关爱的匿名用户给我们讲解javascript抓取网页内容的一些技巧,这里就放出链接,请大家参看。
你说的是jquery里面的$('#book');的实现方式吧jquery$('#book').html();
这个问题其实是这样:是否存在一个链接,能够把左边文字链接到右边结构不一样的页面?分两种情况:1.如果左边是一个独立的html结构,而右边是一个html页面,那么就可以通过pjax爬取。2.如果左边是单页面的html页面,右边是页面中的js代码,
如果想获取链接为"javascript如何抓取页面url?"的话,
我知道dom4j官方提供jsobject可以实现你要的答案。如下图jsobject是否更改我代码这是下图可以看到jsobject是定义一对index和mainkey建立一对连接,每次循环其中一个去修改index,另一个获取页面。您可以自己验证,但是我更推荐您试试还有jqueryjquery=json={"index":"edit","mainkey":"/pjax/ajax_push"};jquery=json={"index":"ajax_push","mainkey":"public"};。 查看全部
豆瓣记者关爱:jquery抓取网页内容的一些技巧,放出链接
jquery抓取网页内容是比较普遍常见的,但是对于javascript来说,也不是很熟悉,下面就由豆瓣记者关爱的匿名用户给我们讲解javascript抓取网页内容的一些技巧,这里就放出链接,请大家参看。

你说的是jquery里面的$('#book');的实现方式吧jquery$('#book').html();
这个问题其实是这样:是否存在一个链接,能够把左边文字链接到右边结构不一样的页面?分两种情况:1.如果左边是一个独立的html结构,而右边是一个html页面,那么就可以通过pjax爬取。2.如果左边是单页面的html页面,右边是页面中的js代码,

如果想获取链接为"javascript如何抓取页面url?"的话,
我知道dom4j官方提供jsobject可以实现你要的答案。如下图jsobject是否更改我代码这是下图可以看到jsobject是定义一对index和mainkey建立一对连接,每次循环其中一个去修改index,另一个获取页面。您可以自己验证,但是我更推荐您试试还有jqueryjquery=json={"index":"edit","mainkey":"/pjax/ajax_push"};jquery=json={"index":"ajax_push","mainkey":"public"};。
jquery抓取网页内容文本分析(抓取)(图)
网站优化 • 优采云 发表了文章 • 0 个评论 • 122 次浏览 • 2022-08-25 07:02
jquery抓取网页内容文本分析打开网页看看需要爬取的文本,然后利用js实现代码解析用js去解析后面的代码,把爬取到的文本放到json格式中不同的li元素id代表着不同的文本元素,我们抓取一个文本是要抓取文本的一部分,可以把代码放到extends和meta中jsondataurllib(jsonobject,jsonfile)-jsonfileparser-pythononlinejsonfiledatamodel-pythondjangowebframework。
不需要nodejs,前端你自己去搞swiperjs可以爬取图片中的数据图片中的数据存入html文件中,直接用phpjavascriptsqlalchemy可以实现前端数据库库操作,
如果你不管什么语言,不管什么库,不管用过什么库。那你永远不可能获取数据。下面是我处理的图片地址。requests抓包看看。可以获取到基本数据图片内容里的头像imgurl,整体地址表格数据,
我可以知道,但是保证不了是否能获取出来,取决于你爬取数据的目的。如果你是针对一个服务器发起爬取请求,服务器是可以识别,并进行返回有效请求的。爬取到的数据可以为正则表达式或者html解析后的text,结构化的html数据。如果是要返回给客户端,因为ua(浏览器),ip等因素和ip是分段获取,爬取到的数据结构可能是随机,对一些网站来说,如果请求没有返回数据,可能会直接报错。所以,要深入理解一门语言,是否完全掌握要看你的爬取目的。仅此而已。谢谢。 查看全部
jquery抓取网页内容文本分析(抓取)(图)
jquery抓取网页内容文本分析打开网页看看需要爬取的文本,然后利用js实现代码解析用js去解析后面的代码,把爬取到的文本放到json格式中不同的li元素id代表着不同的文本元素,我们抓取一个文本是要抓取文本的一部分,可以把代码放到extends和meta中jsondataurllib(jsonobject,jsonfile)-jsonfileparser-pythononlinejsonfiledatamodel-pythondjangowebframework。

不需要nodejs,前端你自己去搞swiperjs可以爬取图片中的数据图片中的数据存入html文件中,直接用phpjavascriptsqlalchemy可以实现前端数据库库操作,

如果你不管什么语言,不管什么库,不管用过什么库。那你永远不可能获取数据。下面是我处理的图片地址。requests抓包看看。可以获取到基本数据图片内容里的头像imgurl,整体地址表格数据,
我可以知道,但是保证不了是否能获取出来,取决于你爬取数据的目的。如果你是针对一个服务器发起爬取请求,服务器是可以识别,并进行返回有效请求的。爬取到的数据可以为正则表达式或者html解析后的text,结构化的html数据。如果是要返回给客户端,因为ua(浏览器),ip等因素和ip是分段获取,爬取到的数据结构可能是随机,对一些网站来说,如果请求没有返回数据,可能会直接报错。所以,要深入理解一门语言,是否完全掌握要看你的爬取目的。仅此而已。谢谢。
jquery抓取网页内容原生支持模块化开发支持
网站优化 • 优采云 发表了文章 • 0 个评论 • 72 次浏览 • 2022-08-01 08:01
jquery抓取网页内容,原生支持模块化开发支持爬虫内置ajax,websocket设置jsonheader返回json数据jsheader简化js调试模块化就是没有一个插件的概念,也就是可以是动态加载的js文件,也可以是脚本,可以是全局变量或者对象等等。有axios组件,做动态连接。不要设置json地址,只获取json字符串,如果需要json字符串,用。json的json读取库加载数据,可以用其他库如json。parse。json。dump。
html新手入门数据在js里面设置,不在页面。 查看全部
jquery抓取网页内容原生支持模块化开发支持

jquery抓取网页内容,原生支持模块化开发支持爬虫内置ajax,websocket设置jsonheader返回json数据jsheader简化js调试模块化就是没有一个插件的概念,也就是可以是动态加载的js文件,也可以是脚本,可以是全局变量或者对象等等。有axios组件,做动态连接。不要设置json地址,只获取json字符串,如果需要json字符串,用。json的json读取库加载数据,可以用其他库如json。parse。json。dump。

html新手入门数据在js里面设置,不在页面。
jquery抓取网页内容 我质朴的梦想:个人网站
网站优化 • 优采云 发表了文章 • 0 个评论 • 74 次浏览 • 2022-07-30 14:29
我对个人网站的向往,大概就跟想要拥有一套自己的房子的心情是相通的:既可以随自己折腾安排,还可以接待客人——毕竟搞设计总得整理作品和履历。
虽然做网页不是什么很新鲜的事情,但如果想高度自定义,对于我这种既不是计算机专业的、也不是从小就热爱编程的人来说,仍然还是有一道技术的大山横亘在面前。
不过从大学时就多多少少有一些写Processing的经验,所以前阵子下决心学一学Web前端的东西,尽自己力所能及的程度实际地动手搭建自己的网站。
本文就是一篇关于我从初学状态开始,到做出阶段性成果来的开发过程记录。
心路历程与实现思路参半。
结尾有过程中参考资料的总结,可按需跳转。
后续还要填其他部分的坑,以后完成了再开放访问吧。
先放放做出来的东西
除了搭网站框架,主要就是做了主页部分的设计。
设计的意图很单纯:因为是“开水的主页”,所以想要给网页注入自己的灵魂(注水hhh),作品自然是水里的内容。以此想法为基础,考虑了“水”的感受如何表现,同时也想试着打破一下规矩的网格排列方式,所以做了这样的表现尝试。
实现上主要用到了HEXO搭网站框架,然后基于P5.js和Matter.js实现交互效果。
第1阶段:搭框架
怎样搭出一个网站来?
起初,把基础教程过了一遍之后,虽然理解了单个网页的效果是怎样实现的,但我有一个疑问仍然没有被解答:一个网站是由多个互相关联的网页组成的,总不能都一个一个写吧?
(WEB开发三剑客的分工)
记得一位做前端的朋友跟我说过,他们会用到VUE写框架。但我并没有立刻投入精力去了解学习VUE,因为我猜想专业开发者在工作中用到的东西,可能对我来说有点太牛刀小试了。
无非就是个人博客的程度,正想着应该有更精简一点能满足我的需求的做法,B站的算法就给我推了基于HEXO搭建个人博客,并部署到GitHubPage的内容——反正也不用花钱,于是我就抱着好奇心,跟着人家装上了。
整个安装和使用过程用的是命令行工具,装好之后就会生成一个默认的博客网站了。
(HEXO预览网页效果)
创建新文章、生成页面预览、部署到GitHub上等都是要通过在终端敲指令来操作,我还是第一次体验。好在以前也稍稍了解过一点MAC终端常用操作和Git的知识,所以到这里为止,还不算太懵逼。
(HEXO创建新文章)
很好,借助HEXO就可以简单地解决“未来需要不断地增加新内容”这个需求。
那么下一步当然就是搞装修了!
满头问号的旅程也随即真正开始。
第2阶段:从文件到效果
如何控制HEXO的主题风格?
首先,不管怎么说先换个干净顺眼的主题吧。
然后把内容填一填。
(只是把封面放进去,就还挺有样子了)
接着便打算研究研究主题文件,看看它是怎么弄出来这些效果的。
但当时我的认知程度仅仅是“定性的”:source文件夹里装的是文章内容,themes里装的是主题文件,其他全都不知道。
(当时我对HEXO的浅薄认知)
我原以为主题就是css的事情而已,然而一打开themes文件夹——小问号你是否有许多朋友。
layout 里的文件全是.ejs 格式,这是个什么格式?
为什么里面写的好像是HTML的内容,但又被一些不认识的语法格式包裹着??
为什么每一个文件都只有几行内容???
于是补充知识
HEXO的主题模板是怎么写出来的?
不知编程世界里是不是都默认大家知道各种东西的
HEXO官方文档只写出了文章变量的引用方法。
我主要靠跟着某篇文章实操了一遍+在油管上看了个细讲HEXO的教程,才理解了这些ejs文件的作用:ejs是一个模板语言(其实用的都是js语言),用它可以把网页的各个部分写成一个个单独的模块,然后按需引用模块来组合成不同的页面。(例如页头、页脚、内容分开写成三个文件)
(HEXO的主题文件夹里,ejs文件的结构关系示意)
此外读别人写的主题文件时,高频出现了类似于 col-xs-6 之类长得很像分子链,但意义不明的class表示方法。
继续补充知识
col-xs-6 这些是什么东西?
搜了搜,好家伙,原来这就是网页开发中用来控制内容排版的栅格系统工具!
终于出现了亲切的名词,忽然就get到了平面设计中学的网格系统和实际开发的联系。
这是一个名为Bootstrap的css框架,相当于别人提前写好了很多css样式,封装起来供开发者使用。
它最大的好处在于提高排版的效率,你不需要每次都从0开始写css样式来安排内容的位置了。只要在html里按规则写类名,就可以直接达成效果。这对响应式设计非常有帮助。
字母都是有语义的,比如col是列,xs是超小屏,最后的数字表示占多少列。col-xs-6就表示“在超小屏里,这部分内容占6列”。由于容器默认被分成12列,所以占6列相当于从左到右,占了一半的位置。
(响应式设计,内容的排版会根据窗口大小改变)
了解了这些,就基本搞懂了主题文件和网页效果之间的关系。
接着自己动动手实现了一些小需求:
说起来简单,实际做起来光是要搞懂要给哪个div标签加背景色,我也是弄了半个上午
(论夯实基础的重要性)
第3阶段:创意和实现怎样实现我想要的交互?搞明白主题文件和网页效果的关系之后,终于到了可以让创意介入的、让人开心的交互部分!网页的交互就要用上JavaScript,但js基础教程里搞的那些对我好像不太直接有用,我就把注意力转移到了P5.js上。P5.js是js的一个库,写法和Processing很像,可以很方便地“绘制”元素。
构想所以要做一个怎样的首页呢?开头也说过,因为是“开水的主页”,所以想要给网页注入灵魂(注水)。作品自然是水里的内容。所以如果能让它们像冰块那样拥有体积,会发生碰撞效果,可能会挺有意思的。
(灵魂草图)
所以从需求开始考虑,在大方向上,我知道至少需要做到这3件事情:将P5的画布插入到现在的主页里,且不影响原有的导航栏功能。要想让作品们能在P5的画布里被显示和打开,需要读到HTML里的数据(图片、标题、链接等),才能做进一步的操作。
要实现有体积碰撞的效果,需要借助物理库。
But我从来没写过p5,所以还是要补习一下。
稍微补充知识:P5.js的基础
教程当然就是去看Daniel Shiffman,看他敲代码真是快乐哈哈。顺便在这里推荐一下他的那个网站,除了P5和Processing,还有关于模拟自然系统的算法、机器学习等专题内容。整合得很好,比油管频道更方便查阅或开启新的学习。
()
弄懂p5怎么用之后,随便写个“随机Design”效果
试试把画板放进网页里,好,可以正常运行。
(好简陋hhhh没关系,千里之行始于足下。)如果有朋友也想试试自己用HEXO+P5.js来玩玩看,在这里顺便提醒一下HEXO里引用js文件的方法(引用css也同理):
接着就是要往画板里加入内容了。琢磨:如何在P5的画板里显示出这些作品?思路就是读取HTML文档,然后抓取需要的图片路径,再加载到P5里。我一开始以为这很顺利就能解决,但实操起来发现P5里竟然没有能直接读到HTML元素的src的方法
(如果有请告诉我)
于是就想到,既然这是是基于js的语言,那我在外面先用jQuery来获取一下数据,再通过全局变量来传入P5应该行得通。
试了之后确实可行,开心。于是之后的标题、时间、详情页链接我都是用这个方式来获取的。
(用jQuery主要是因为我觉得它的语法比较好读…但调用一个库却只干一件事情感觉挺铺张浪费的)
补充新知识:物理库Matter.js的用法。想要让我的图片们能发生自然的物理碰撞效果,得借助一下物理库的力量。Matter.js就是一个js的物理库,它可以在平面的世界里做出自然的物理效果。Daniel Shiffman也有对Matter.js的入门专题,总之就是继续和他学学学。
( matter.js官方动画 )
然后结合着实现了这样的效果。
不过物体落下来这种方式,显然很不“水”。所以稍微设置了一下重力,让这些块块都能往上漂,营造一种液体里的感觉。
定义“如何打开作品详情?”做到这里,就不得不开始考虑这个问题了。因为想要用户在首页可以点击拖拽这些块块随便玩玩,那“单击”就不能作为打开链接的触发指令。所以就想到使用拖拽到某个区域后松开手即打开的方式。顺便,考虑到访客不可能通过那小小的图片就知道每个块是什么内容,至少要把作品标题和时间信息传达出来。初步实现了这样的效果。
该有的信息和功能基本都有了,接下来就是增强反馈的表现效果。
反馈增强1 · 标题信息特效静止的标题出现和消失都太生硬了。我想要做出那种点击之后,读取数据,最后才变成最终信息的感觉。先另开了新文件里写了一个试试。
出来的效果并不复杂,但是编程逻辑上还挺锻炼到我的。(1) 编写标题特效以单个字符为单位写了类,然后以要显示的标题为基础创建实例,储存在数组里。动画分成入场、切换、退场三个状态,并且为了更细腻一点,字符之间的进退场还设定了一些随机的时间差。
(2)节约魔法消耗(运算资源)
Prototype在这个过程中还另外去补习了js里特有的prototype的概念。因为每个特效字的update和show的过程都是一样的,所以把这些function都写进了这个类的prototype里,就可以让实例们在演算时调用同一个function,从而达到节约运算资源的目的。和Processing里有的“继承”的概念很像,不过以前我都没用过,现学现用了。
整合代码因为特效字的代码是先在别的地方写的,所以还要整合到网页里并调用。整合有两种思路:显然第二种方法更为轻巧,节省资源。(实际上起初用了第一种方法,一运行就卡了…照理说就算这样粒子数也没有很多,或许是我还有其他的bug
)
(3)标题字大小自适应窗口宽度算法本身没啥好特别说的,只是有些细节在这里才留心注意到了。textSize( )表示的是字符的高度,单位是px。textWidth()测量的字符串宽度,是基于最近一次被应用的textSize算出来的。
反馈增强2·底部打开提示关于先前写好的这个功能,点击图片,窗口下方会弹出一个“拖拽至此处打开”的提示。
这个部分觉得可以做得更灵动一点,想到了既然是“水”的主题,不如就做个水波好了。水波的实现逻辑不难,核心就是用sin函数来绘制要波动的点,然后把这些顶点连成一个封闭图形就好。
研究三角函数为了更准确地理解并控制波形,还是单独花了一些时间去复习学习三角函数在编程绘图里的实际应用。果然自己归纳一遍还是很重要,考虑之后写一篇关于三角函数和PerlineNoise的理解(开始挖坑)。
优化后的提示栏,更有情绪。提示 “拖拽至此查看详情” 时比较平静淡定,
可以“松手打开”时比较活泼,有种雀跃的感觉哈哈。
把标题效果和底部提示整合进去之后就基本上也是最后的效果了。
反馈增强3·点按后,其他未激活物体缩小或半透明(技能点不足,未解决)图片的缩放是很简单,但没搞懂跟图片绑定的Matter.js的Body的缩放,到底是参照着什么来缩的。图片和Body的边界一直对不上
好吧,既然缩放我走不通,那做做图片透明度吧。结果搞透明要用的tint函数一加进去,程序就变得很卡,去查了查,别人还会提前创建一个graphic来装……有点嫌麻烦。
最后曲线救国,给了个半透明背景色的蒙版,意思一下它们没有被选中。算了算了,来日方长。
反馈增强4·点按后,在背景中显示大图(效果不满意,弃用)试了下给点按之后在大背景中也显示大图,还特地花了些功夫写了充满窗口尺寸和淡出淡入的效果,不过出来的效果感觉视线不知道该放哪里,于是弃用了。就单纯一点吧。
其他调试除了视觉效果之外,有些东西是要考虑到浏览器环境的问题。这部分主要做了图片块、物理边界、波浪宽度等自适应浏览器窗口大小。以及mouseReleased(松开鼠标)事件在手机端似乎不被识别,所以还要另外补写touch事件。
快乐结语主页开发的过程基本上就是这样了。新手如我,想要实现一个需求,就得去学一个新的东西,天天和问号Bug做朋友。要做成一件事情就是得不停地寻找答案。很需要耐心,解决的时候也真的很快乐。非专业或许在知识储备上有很多漏洞,但正因为这不是自己的专业,也没有人要求自己这么干,由兴趣驱动的钻研就很快乐了。
参考资料:
【油管web前端入门教程-优质熟肉】LearnCode.academy,翻译搬运:鱼C-小甲鱼,
【HEXO搭建教程】CodeSheep,手把手教你从0开始搭建自己的个人博客,
【油管优质HEXO教程】Mike Dane, Hexo - Static Site Generator Tutorial,【HEXO官方文档】【Bootstrap介绍】全局css样式 ,
【快乐学习创意编程TheCodingTrain】Daniel Shiffman,P5.js专题教程,Daniel Shiffman,Matter.js专题教程, Daniel Shiffman,简单正弦波运动,
真的有人看到了这里吗,太让我感动了。才知道2018年后注册的公众号不带留言功能了
如果你乐意给我按个赞,或是通过别的方式给我反馈,我会很开心~这也会成为我继续更新的动力之一。
感谢阅读,谢谢! 查看全部
jquery抓取网页内容 我质朴的梦想:个人网站
我对个人网站的向往,大概就跟想要拥有一套自己的房子的心情是相通的:既可以随自己折腾安排,还可以接待客人——毕竟搞设计总得整理作品和履历。
虽然做网页不是什么很新鲜的事情,但如果想高度自定义,对于我这种既不是计算机专业的、也不是从小就热爱编程的人来说,仍然还是有一道技术的大山横亘在面前。
不过从大学时就多多少少有一些写Processing的经验,所以前阵子下决心学一学Web前端的东西,尽自己力所能及的程度实际地动手搭建自己的网站。
本文就是一篇关于我从初学状态开始,到做出阶段性成果来的开发过程记录。
心路历程与实现思路参半。
结尾有过程中参考资料的总结,可按需跳转。
后续还要填其他部分的坑,以后完成了再开放访问吧。
先放放做出来的东西
除了搭网站框架,主要就是做了主页部分的设计。
设计的意图很单纯:因为是“开水的主页”,所以想要给网页注入自己的灵魂(注水hhh),作品自然是水里的内容。以此想法为基础,考虑了“水”的感受如何表现,同时也想试着打破一下规矩的网格排列方式,所以做了这样的表现尝试。
实现上主要用到了HEXO搭网站框架,然后基于P5.js和Matter.js实现交互效果。
第1阶段:搭框架
怎样搭出一个网站来?
起初,把基础教程过了一遍之后,虽然理解了单个网页的效果是怎样实现的,但我有一个疑问仍然没有被解答:一个网站是由多个互相关联的网页组成的,总不能都一个一个写吧?
(WEB开发三剑客的分工)
记得一位做前端的朋友跟我说过,他们会用到VUE写框架。但我并没有立刻投入精力去了解学习VUE,因为我猜想专业开发者在工作中用到的东西,可能对我来说有点太牛刀小试了。
无非就是个人博客的程度,正想着应该有更精简一点能满足我的需求的做法,B站的算法就给我推了基于HEXO搭建个人博客,并部署到GitHubPage的内容——反正也不用花钱,于是我就抱着好奇心,跟着人家装上了。
整个安装和使用过程用的是命令行工具,装好之后就会生成一个默认的博客网站了。
(HEXO预览网页效果)
创建新文章、生成页面预览、部署到GitHub上等都是要通过在终端敲指令来操作,我还是第一次体验。好在以前也稍稍了解过一点MAC终端常用操作和Git的知识,所以到这里为止,还不算太懵逼。
(HEXO创建新文章)
很好,借助HEXO就可以简单地解决“未来需要不断地增加新内容”这个需求。
那么下一步当然就是搞装修了!
满头问号的旅程也随即真正开始。
第2阶段:从文件到效果
如何控制HEXO的主题风格?
首先,不管怎么说先换个干净顺眼的主题吧。
然后把内容填一填。
(只是把封面放进去,就还挺有样子了)
接着便打算研究研究主题文件,看看它是怎么弄出来这些效果的。
但当时我的认知程度仅仅是“定性的”:source文件夹里装的是文章内容,themes里装的是主题文件,其他全都不知道。
(当时我对HEXO的浅薄认知)
我原以为主题就是css的事情而已,然而一打开themes文件夹——小问号你是否有许多朋友。
layout 里的文件全是.ejs 格式,这是个什么格式?
为什么里面写的好像是HTML的内容,但又被一些不认识的语法格式包裹着??
为什么每一个文件都只有几行内容???
于是补充知识

HEXO的主题模板是怎么写出来的?
不知编程世界里是不是都默认大家知道各种东西的
HEXO官方文档只写出了文章变量的引用方法。
我主要靠跟着某篇文章实操了一遍+在油管上看了个细讲HEXO的教程,才理解了这些ejs文件的作用:ejs是一个模板语言(其实用的都是js语言),用它可以把网页的各个部分写成一个个单独的模块,然后按需引用模块来组合成不同的页面。(例如页头、页脚、内容分开写成三个文件)
(HEXO的主题文件夹里,ejs文件的结构关系示意)
此外读别人写的主题文件时,高频出现了类似于 col-xs-6 之类长得很像分子链,但意义不明的class表示方法。
继续补充知识
col-xs-6 这些是什么东西?
搜了搜,好家伙,原来这就是网页开发中用来控制内容排版的栅格系统工具!
终于出现了亲切的名词,忽然就get到了平面设计中学的网格系统和实际开发的联系。
这是一个名为Bootstrap的css框架,相当于别人提前写好了很多css样式,封装起来供开发者使用。
它最大的好处在于提高排版的效率,你不需要每次都从0开始写css样式来安排内容的位置了。只要在html里按规则写类名,就可以直接达成效果。这对响应式设计非常有帮助。
字母都是有语义的,比如col是列,xs是超小屏,最后的数字表示占多少列。col-xs-6就表示“在超小屏里,这部分内容占6列”。由于容器默认被分成12列,所以占6列相当于从左到右,占了一半的位置。
(响应式设计,内容的排版会根据窗口大小改变)
了解了这些,就基本搞懂了主题文件和网页效果之间的关系。
接着自己动动手实现了一些小需求:
说起来简单,实际做起来光是要搞懂要给哪个div标签加背景色,我也是弄了半个上午
(论夯实基础的重要性)
第3阶段:创意和实现怎样实现我想要的交互?搞明白主题文件和网页效果的关系之后,终于到了可以让创意介入的、让人开心的交互部分!网页的交互就要用上JavaScript,但js基础教程里搞的那些对我好像不太直接有用,我就把注意力转移到了P5.js上。P5.js是js的一个库,写法和Processing很像,可以很方便地“绘制”元素。
构想所以要做一个怎样的首页呢?开头也说过,因为是“开水的主页”,所以想要给网页注入灵魂(注水)。作品自然是水里的内容。所以如果能让它们像冰块那样拥有体积,会发生碰撞效果,可能会挺有意思的。
(灵魂草图)
所以从需求开始考虑,在大方向上,我知道至少需要做到这3件事情:将P5的画布插入到现在的主页里,且不影响原有的导航栏功能。要想让作品们能在P5的画布里被显示和打开,需要读到HTML里的数据(图片、标题、链接等),才能做进一步的操作。
要实现有体积碰撞的效果,需要借助物理库。
But我从来没写过p5,所以还是要补习一下。
稍微补充知识:P5.js的基础
教程当然就是去看Daniel Shiffman,看他敲代码真是快乐哈哈。顺便在这里推荐一下他的那个网站,除了P5和Processing,还有关于模拟自然系统的算法、机器学习等专题内容。整合得很好,比油管频道更方便查阅或开启新的学习。
()
弄懂p5怎么用之后,随便写个“随机Design”效果
试试把画板放进网页里,好,可以正常运行。
(好简陋hhhh没关系,千里之行始于足下。)如果有朋友也想试试自己用HEXO+P5.js来玩玩看,在这里顺便提醒一下HEXO里引用js文件的方法(引用css也同理):
接着就是要往画板里加入内容了。琢磨:如何在P5的画板里显示出这些作品?思路就是读取HTML文档,然后抓取需要的图片路径,再加载到P5里。我一开始以为这很顺利就能解决,但实操起来发现P5里竟然没有能直接读到HTML元素的src的方法
(如果有请告诉我)
于是就想到,既然这是是基于js的语言,那我在外面先用jQuery来获取一下数据,再通过全局变量来传入P5应该行得通。

试了之后确实可行,开心。于是之后的标题、时间、详情页链接我都是用这个方式来获取的。
(用jQuery主要是因为我觉得它的语法比较好读…但调用一个库却只干一件事情感觉挺铺张浪费的)
补充新知识:物理库Matter.js的用法。想要让我的图片们能发生自然的物理碰撞效果,得借助一下物理库的力量。Matter.js就是一个js的物理库,它可以在平面的世界里做出自然的物理效果。Daniel Shiffman也有对Matter.js的入门专题,总之就是继续和他学学学。
( matter.js官方动画 )
然后结合着实现了这样的效果。
不过物体落下来这种方式,显然很不“水”。所以稍微设置了一下重力,让这些块块都能往上漂,营造一种液体里的感觉。
定义“如何打开作品详情?”做到这里,就不得不开始考虑这个问题了。因为想要用户在首页可以点击拖拽这些块块随便玩玩,那“单击”就不能作为打开链接的触发指令。所以就想到使用拖拽到某个区域后松开手即打开的方式。顺便,考虑到访客不可能通过那小小的图片就知道每个块是什么内容,至少要把作品标题和时间信息传达出来。初步实现了这样的效果。
该有的信息和功能基本都有了,接下来就是增强反馈的表现效果。
反馈增强1 · 标题信息特效静止的标题出现和消失都太生硬了。我想要做出那种点击之后,读取数据,最后才变成最终信息的感觉。先另开了新文件里写了一个试试。
出来的效果并不复杂,但是编程逻辑上还挺锻炼到我的。(1) 编写标题特效以单个字符为单位写了类,然后以要显示的标题为基础创建实例,储存在数组里。动画分成入场、切换、退场三个状态,并且为了更细腻一点,字符之间的进退场还设定了一些随机的时间差。
(2)节约魔法消耗(运算资源)
Prototype在这个过程中还另外去补习了js里特有的prototype的概念。因为每个特效字的update和show的过程都是一样的,所以把这些function都写进了这个类的prototype里,就可以让实例们在演算时调用同一个function,从而达到节约运算资源的目的。和Processing里有的“继承”的概念很像,不过以前我都没用过,现学现用了。
整合代码因为特效字的代码是先在别的地方写的,所以还要整合到网页里并调用。整合有两种思路:显然第二种方法更为轻巧,节省资源。(实际上起初用了第一种方法,一运行就卡了…照理说就算这样粒子数也没有很多,或许是我还有其他的bug
)
(3)标题字大小自适应窗口宽度算法本身没啥好特别说的,只是有些细节在这里才留心注意到了。textSize( )表示的是字符的高度,单位是px。textWidth()测量的字符串宽度,是基于最近一次被应用的textSize算出来的。
反馈增强2·底部打开提示关于先前写好的这个功能,点击图片,窗口下方会弹出一个“拖拽至此处打开”的提示。
这个部分觉得可以做得更灵动一点,想到了既然是“水”的主题,不如就做个水波好了。水波的实现逻辑不难,核心就是用sin函数来绘制要波动的点,然后把这些顶点连成一个封闭图形就好。
研究三角函数为了更准确地理解并控制波形,还是单独花了一些时间去复习学习三角函数在编程绘图里的实际应用。果然自己归纳一遍还是很重要,考虑之后写一篇关于三角函数和PerlineNoise的理解(开始挖坑)。
优化后的提示栏,更有情绪。提示 “拖拽至此查看详情” 时比较平静淡定,
可以“松手打开”时比较活泼,有种雀跃的感觉哈哈。
把标题效果和底部提示整合进去之后就基本上也是最后的效果了。
反馈增强3·点按后,其他未激活物体缩小或半透明(技能点不足,未解决)图片的缩放是很简单,但没搞懂跟图片绑定的Matter.js的Body的缩放,到底是参照着什么来缩的。图片和Body的边界一直对不上
好吧,既然缩放我走不通,那做做图片透明度吧。结果搞透明要用的tint函数一加进去,程序就变得很卡,去查了查,别人还会提前创建一个graphic来装……有点嫌麻烦。
最后曲线救国,给了个半透明背景色的蒙版,意思一下它们没有被选中。算了算了,来日方长。
反馈增强4·点按后,在背景中显示大图(效果不满意,弃用)试了下给点按之后在大背景中也显示大图,还特地花了些功夫写了充满窗口尺寸和淡出淡入的效果,不过出来的效果感觉视线不知道该放哪里,于是弃用了。就单纯一点吧。
其他调试除了视觉效果之外,有些东西是要考虑到浏览器环境的问题。这部分主要做了图片块、物理边界、波浪宽度等自适应浏览器窗口大小。以及mouseReleased(松开鼠标)事件在手机端似乎不被识别,所以还要另外补写touch事件。
快乐结语主页开发的过程基本上就是这样了。新手如我,想要实现一个需求,就得去学一个新的东西,天天和问号Bug做朋友。要做成一件事情就是得不停地寻找答案。很需要耐心,解决的时候也真的很快乐。非专业或许在知识储备上有很多漏洞,但正因为这不是自己的专业,也没有人要求自己这么干,由兴趣驱动的钻研就很快乐了。
参考资料:
【油管web前端入门教程-优质熟肉】LearnCode.academy,翻译搬运:鱼C-小甲鱼,
【HEXO搭建教程】CodeSheep,手把手教你从0开始搭建自己的个人博客,
【油管优质HEXO教程】Mike Dane, Hexo - Static Site Generator Tutorial,【HEXO官方文档】【Bootstrap介绍】全局css样式 ,
【快乐学习创意编程TheCodingTrain】Daniel Shiffman,P5.js专题教程,Daniel Shiffman,Matter.js专题教程, Daniel Shiffman,简单正弦波运动,
真的有人看到了这里吗,太让我感动了。才知道2018年后注册的公众号不带留言功能了
如果你乐意给我按个赞,或是通过别的方式给我反馈,我会很开心~这也会成为我继续更新的动力之一。
感谢阅读,谢谢!
用Node抓站(一):怎么写出自己满意的代码
网站优化 • 优采云 发表了文章 • 0 个评论 • 79 次浏览 • 2022-07-09 01:26
如果只写怎么抓取网页,肯定会被吐槽太水,满足不了读者的逼格要求,所以本文会通过不断的审视代码,做到令自己满意(撸码也要不断迸发新想法!
本文目标:抓取什么值得买网站国内优惠的最新商品,并且作为对象输出出来,方便后续入库等操作
抓取常用到的npm模块
本文就介绍两个:request和cheerio,另外lodash是个工具库,不做介绍,后面篇幅会继续介绍其他用到的npm库。
request 示例
<p>var request = require('request');
request('http://www.smzdm.com/youhui/', (err, req)=>{
if(!err){
console.log(Object.keys(req))
}
})</p>
通过上面的代码就看到req实际是个response对象,包括headers、statusCode、body等,我们用body就是网站的html内容
cheerio 示例
<p>var request = require('request')
var cheerio = require('cheerio')
cheerio.prototype.removeTagText = function () {
var html = this.html()
return html.replace(/ {
if (!err) {
var body = req.body
var $ = cheerio.load(body, {
decodeEntities: false
})
$('.list.list_preferential').each((i, item) => {
var $title = $('.itemName a', item)
var url = $title.attr('href')
var title = $title.removeTagText().trim()
var hl = $title.children().text().trim()
var img = $('img', item).attr('src')
var desc = $('.lrInfo', item).html().trim()
desc = desc.replace(/阅读全文/g, '')
var mall = $('.botPart a.mall', item).text().trim()
console.log({title, hl, url, img, desc, mall})
})
}
})</p>
简单解释下,removeTagText是直接扩展了cheerio的一个方法,目的是去掉类似
<p>再特价:QuanU 全友 布艺沙发组合2798元包邮(需定金99元,3.1付尾款)</p>
里面span之后的文字。执行后得到下面的结果:

怎么写出自己满意的代码
从上面需求来看,只需要提取列表页面的商品信息,而取到数据之后,使用cheerio进行了解析,然后通过一些「选择器」对数据进行「提取加工」,得到想要的数据。
重点是选择器和提取加工,如果想要的字段多了,那么代码会越写越多,维护困难,最重要的是「不环保」,今天抓什么值得买,明天抓惠惠网,代码还要copy一份改一改!一来二去,抓的越多,那么代码越乱,想想哪天不用request了,是不是要挨个修改呢?所以要抓重点,从最后需要的数据结构入手,关注选择器和提取加工。
handlerMap
从最后需要的数据结构入手,关注选择器和提取加工。我设计一种对象结构,作为参数传入,这个参数我起名:handlerMap,最后实现一个spider的函数,用法如下:
<p>spider(url, callback, handlerMap)</p>
从目标数据结构出发,最后数据什么样子,那么handlerMap结构就是什么样子,key就是最后输出数据的key,是由selector和handler两个key组成的对象,类似我们需要最后产出的数据是:
<p>[{
title: '',
ht: '',
url: '',
img: '',
mall: '',
desc: ''
}, {item2..}...]</p>
那么需要的handlerMap就是:
<p>{
title: {
selector: '.itemName a',
handler: 'removeTagText'
},
ht: {
selector: '.itemName a span',
handler: 'text'
},
url: {
selector: '.itemName a',
handler: 'atrr:href'
},
img: {
selector: 'img',
handler: 'attr:src'
},
mall: {
selector: '.botPart a.mall',
handler: 'text'
},
desc: {
selector: '.lrInfo',
handler: function (data){
return data.replace(/阅读全文/g, '')
}
}
}</p>
再酷一点,就是简写方法:url:".itemName a!attr:href”,另外再加上如果抓取的是JSON数据,也要一起处理的情况。经过分析之后,开始改造代码,代码最后分为了两个模块:
虽然增加不少代码工作量,但是抽象后的代码在使用的时候就更加方便了,自己还是别人在使用的时候,不用关心代码实现,只需要关注抓取的页面url、要提取的页面内容和数据得到后的继续操作即可,使用起来要比之前混杂在一起的代码更加清晰简洁;并且抓取任意页面都不需要动核心的代码,只需要填写前面提到的handlerMap。
总结
其实Node抓取页面很简单,本文只是通过一个简单的抓取任务,不断深入思考,进行抽象,写出自己满意的代码,以小见大,希望本文对读者有所启发
今天到此结束,完成一个基础抓取的库,有空继续介绍Node抓站的知识,欢迎大家交流讨论
本文的完整代码,在github/ksky521/mpdemo/ 对应文章名文件夹下可以找到
EOF三水清
2017年02月25日 查看全部
用Node抓站(一):怎么写出自己满意的代码
如果只写怎么抓取网页,肯定会被吐槽太水,满足不了读者的逼格要求,所以本文会通过不断的审视代码,做到令自己满意(撸码也要不断迸发新想法!
本文目标:抓取什么值得买网站国内优惠的最新商品,并且作为对象输出出来,方便后续入库等操作
抓取常用到的npm模块
本文就介绍两个:request和cheerio,另外lodash是个工具库,不做介绍,后面篇幅会继续介绍其他用到的npm库。
request 示例
<p>var request = require('request');
request('http://www.smzdm.com/youhui/', (err, req)=>{
if(!err){
console.log(Object.keys(req))
}
})</p>
通过上面的代码就看到req实际是个response对象,包括headers、statusCode、body等,我们用body就是网站的html内容
cheerio 示例
<p>var request = require('request')
var cheerio = require('cheerio')
cheerio.prototype.removeTagText = function () {
var html = this.html()
return html.replace(/ {
if (!err) {
var body = req.body
var $ = cheerio.load(body, {
decodeEntities: false
})
$('.list.list_preferential').each((i, item) => {
var $title = $('.itemName a', item)
var url = $title.attr('href')
var title = $title.removeTagText().trim()
var hl = $title.children().text().trim()
var img = $('img', item).attr('src')
var desc = $('.lrInfo', item).html().trim()
desc = desc.replace(/阅读全文/g, '')
var mall = $('.botPart a.mall', item).text().trim()
console.log({title, hl, url, img, desc, mall})
})
}
})</p>
简单解释下,removeTagText是直接扩展了cheerio的一个方法,目的是去掉类似

<p>再特价:QuanU 全友 布艺沙发组合2798元包邮(需定金99元,3.1付尾款)</p>
里面span之后的文字。执行后得到下面的结果:

怎么写出自己满意的代码
从上面需求来看,只需要提取列表页面的商品信息,而取到数据之后,使用cheerio进行了解析,然后通过一些「选择器」对数据进行「提取加工」,得到想要的数据。
重点是选择器和提取加工,如果想要的字段多了,那么代码会越写越多,维护困难,最重要的是「不环保」,今天抓什么值得买,明天抓惠惠网,代码还要copy一份改一改!一来二去,抓的越多,那么代码越乱,想想哪天不用request了,是不是要挨个修改呢?所以要抓重点,从最后需要的数据结构入手,关注选择器和提取加工。
handlerMap
从最后需要的数据结构入手,关注选择器和提取加工。我设计一种对象结构,作为参数传入,这个参数我起名:handlerMap,最后实现一个spider的函数,用法如下:
<p>spider(url, callback, handlerMap)</p>
从目标数据结构出发,最后数据什么样子,那么handlerMap结构就是什么样子,key就是最后输出数据的key,是由selector和handler两个key组成的对象,类似我们需要最后产出的数据是:

<p>[{
title: '',
ht: '',
url: '',
img: '',
mall: '',
desc: ''
}, {item2..}...]</p>
那么需要的handlerMap就是:
<p>{
title: {
selector: '.itemName a',
handler: 'removeTagText'
},
ht: {
selector: '.itemName a span',
handler: 'text'
},
url: {
selector: '.itemName a',
handler: 'atrr:href'
},
img: {
selector: 'img',
handler: 'attr:src'
},
mall: {
selector: '.botPart a.mall',
handler: 'text'
},
desc: {
selector: '.lrInfo',
handler: function (data){
return data.replace(/阅读全文/g, '')
}
}
}</p>
再酷一点,就是简写方法:url:".itemName a!attr:href”,另外再加上如果抓取的是JSON数据,也要一起处理的情况。经过分析之后,开始改造代码,代码最后分为了两个模块:
虽然增加不少代码工作量,但是抽象后的代码在使用的时候就更加方便了,自己还是别人在使用的时候,不用关心代码实现,只需要关注抓取的页面url、要提取的页面内容和数据得到后的继续操作即可,使用起来要比之前混杂在一起的代码更加清晰简洁;并且抓取任意页面都不需要动核心的代码,只需要填写前面提到的handlerMap。
总结
其实Node抓取页面很简单,本文只是通过一个简单的抓取任务,不断深入思考,进行抽象,写出自己满意的代码,以小见大,希望本文对读者有所启发
今天到此结束,完成一个基础抓取的库,有空继续介绍Node抓站的知识,欢迎大家交流讨论
本文的完整代码,在github/ksky521/mpdemo/ 对应文章名文件夹下可以找到
EOF三水清
2017年02月25日
新世纪Nerv战士 - 京东首页补完计划
网站优化 • 优采云 发表了文章 • 0 个评论 • 69 次浏览 • 2022-06-20 09:13
16版的京东首页,在性能、体验、灾备策略等各方面都做到了极致。站在如此高大的巨人肩上,除了满满的自信,我们心里更怕扑街。毫无疑问,我们在接到改版需求的那一刻,立马就敲定了新首页的技术选型:妥妥的jQuery+SeaJS!
但很快,我们就发现这样一点都不酷。jQuery是2006年的框架了,SeaJS停止维护也已经四年。这些项目的诞生都是为了解决当时业界的一些痛点:比如jQuery最开始是为了方便程序员在页面中操作DOM,绑定事件等;SeaJS则是为了在浏览器中实现CMD规范的模块开发和加载。但在各种VirtualDOM框架横飞的现在,程序员已经很少会直接操作DOM元素,而模块的开发和加载也有早已有了别的方案。
就在这时,Nerv项目的作者提出了建议:“不然用Nerv来一发?”我记得,当时他脸上洋溢着淳朴的笑,Nerv也仅仅是部门内部的一个小项目。我们回想了首页这个业务,技术栈已经好几年未曾更新过,开发流程也不够理想。如果再不做出改变,明年的这个时候我们依然会面对一堆陈年老代码头疼不已。抱着试一试的心态,我们接受了他的提议。没想到,这个决定让首页从此摆脱了落后的技术架构,而Nerv现在也已经成长为GitHub上3k+ Star的热门项目。
Q: 为什么不使用React/Preact/Vue?
A: 这三者都是前端圈子中相当流行的项目。React有完善的体系和浓厚的社区氛围,Preact有着羞涩的体积,Vue则使用了先进的html模板和数据绑定机制。但是,上边这三者都无法兼容IE8。我们在经过相关数据的论证后,发现IE8的用户还是有一定的价值,这才最终激发了我们团队内部自己造轮子的想法。当然,在造轮子的过程中,我们也不忘向上面这些优秀框架的看齐。最终,Nerv在完美兼容React语法的同时,具有着出众的性能表现,在Gzip后也只占用9Kb的体积。
整体架构
在这次的项目中,我们基于上一年久经考验的前端体系(详细介绍),进行了升级:
开发模式Athena2.0
1.0版本的Athena,基于vinyl-fs的流操作,或者说是类似于gulp的压缩、编译等等操作的任务流。而到了2017年,webpack早已在前端圈中流行。同行们也早已经习惯在项目中直接基于最新的语言特性去开发,在webpack.config.js加上一个babel-loader就可以完美支持新语法并完成打包。Athena 1.0背着太沉重的历史包袱,已经很难快速实现对babel转译的支持。所以在首页的开发前,我们将Athena升级到了全新的2.0版本。
一如既往,Athena会为项目提供init(初始化),serve(实时预览),build(编译),publish(发布)等功能。除此之外,由于2.0版本的Athena是基于webpack的,所以项目中可以统一用npm来管理依赖,也可以直接使用最新的ES语言特性来进行开发。
使用Athena2.0开发时,建议的文件架构如下:
前后端协作
我们依然是采用了前后端分离的协作模式,由后端给出json格式的数据,前端拉取json数据进行渲染。对于大部分的组件来说,都会在constructor中做好组件的初始化工作,在componentDidMount的生命周期中拉取数据写入组件的state,再通过render函数进行渲染。
代码规范约束
有一千个读者,就会有一千个哈姆雷特。
上面这句名言,深刻地体现在了16版首页的代码仓库中。同一个组件,如果是基于jQuery+SeaJS的模式,一千个程序猿就会有一千种写法。结果在同一个项目中,代码风格不尽相同,代码质量良莠不齐,多人协作也会无从下手。
何以解忧?唯有统一代码风格了。通过ESLint+Husky,我们对每次代码提交都做了代码风格检查,对是否使用prefer const的变量声明、代码缩进该使用Tab还是空格等等的规则都做了约束。一开始定下规范的时候,团队成员或多或少都会有些不习惯。但通过偷偷在代码里下毒Athena的生成的项目模板中添加对应的规则,潜移默化地,团队成员们也都开始接受、习惯这些约束。
禁用变量重声明等规则,在一定程度上保证了代码质量;而统一的代码样式风格,则使得项目的多人协作更加便利。
Q: 保证代码质量,促进多人协作的终极好处是什么?
A: 由于项目代码风格统一,通俗易懂容易上手,我们首页的开发团队终于开始有妹纸加入了!一群雄性程序猿敲代码能敲出什么火花啊…
对性能优化的探索首屏直出
直出可能是加快首屏加载最行之有效的办法了。它在减少页面加载时间、首屏请求数等方面的好处自然不必再提,结合jQuery,也可以很方便地在直出的DOM上进行更多的操作。
Nerv框架对于它内部的组件、DOM有着良好的操作性,但是对于体系外的DOM节点,却是天生的操作无力。举个例子,比如在页面文件中我们直出一个轮播图:
使用Nerv为这段HTML添加轮播逻辑,成为了非常艰难的操作。终极的解决方案,应该是使用SSR(Server Side Render)的方案,搭建Nerv-server中间层来将组件直出。但现在革命尚未成功,首屏直出尚且依赖后端的研发同学,首页上线又迫在眉睫。被逼急的我们最终选择了比较trick的方式来过渡这个问题:在组件初始化的时候先通过DOM操作获取渲染所需的数据,再将DOM替换成Nerv渲染后的内容。
代码分割
在生产环境中,随着代码体积增大,浏览器解压Gzip、执行等操作也会需要更多的开销。在SeaJS的时代,我们尚且会通过SeaJS.use或者require.async异步加载模块代码,避免一次性加载过多内容。但webpack的默认行为却会将整个页面的代码打包为一个单独的文件,这明显不是最佳的实践。对此,webpack给出的解决方案是动态引入(Dynamic Imports)。我们可以通过如下的代码来使用这个便利的特性:
与此同时,webpack会将使用了动态引入的组件从主bundle文件中抽离出来,这就减小了主bundle文件的体积。
对于我们的具体需求而言,需要做动态引入的一般是Nerv的组件。对于组件的动态引入,业界已经有非常好的实现方案react-loadable。
举个栗子,通过下面的代码,我们可以在页面中使用来实现对组件MyComponent的动态引入,并且具有加载超时、错误、加载中等不同状态的展示:
再进一步,我们希望对于屏幕外的组件,仅仅是在它进入用户视野后再开始加载,这也就是我们常说的滚动懒加载。这可以结合业界已有的懒加载组件react-lazyload来实现。针对上面的,在下面的例子中,只有进入用户屏幕后,MyComponent才会开始加载:
上面的例子为lazyload的组件设置了200px的占位高度。并且设定了占位元素的类名,方便设定样式。
代码延后加载
在给首页全面升级技术栈的时候,我们忽略了一个问题:页面上还引用着少量来自兄弟团队的SeaJS模块,我们升级了技术栈是可以,但是强迫兄弟团队也一起去掉SeaJS重构一遍代码,这就有点不合理了。我们也不能仅仅为了这部分模块,就把SeaJS给打包进代码里面,这也是不科学的。
上面讲到的动态引入功能,帮我们很好地解决了这个问题。我们在代码中单独抽离了一个legacy模块,其中包含了SeaJS、SeaJS-combo等老模块并做了导出。这部分代码在首屏中并不直接引入,而是在需要执行的时候,通过上面的动态引入功能,单独请求下来使用:
打包性能优化
webpack默认会对整个项目引用到的文件进行编译、打包,其中还包括了Nervjs、es5-polyfill等基础的依赖库。这些文件从加入项目开始,基本都不会再有任何更改;然而在每次构建新版本时,webpack打包的这些基础库都会与上一版本有一些细微的区别,这会导致用户浏览器中对应的代码缓存失效。为此,我们考虑将这些基础库分开打包。
针对这种需求,webpack官方建议使用DLL插件来优化。DLL是Dynamic Link Library的简称,是windows系统中对于应用程序依赖的函数库的称呼。对于webpack,我们需要使用一个单独的webpack配置去生成DLL:
接下来,在我们的项目的webpack配置中引用DllReferencdPlugin,传入上面生成的json文件:
这样就完成了动态链接库的生成和引用。除了最开始的一次编译,后续开发中如果基础库没有变动,DLL就再也不需要重新编译,这也就解决了上面的代码变动的问题。
体验优化探索兼容IE8
兼容旧版本IE浏览器一直是前端开发人员心中永远的痛。过去,我们使用jQuery去统一不同浏览器的DOM操作和绑定事件,通过jQuery元素实例的map、each等类数组函数批量做JavaScript动画,等等。
但是在使用Nerv之后,从体系外直接操作DOM就显得很不优雅;更推荐的写法,是通过组件的ref属性来访问原生DOM。而map、each等函数,IE9+的浏览器也已经在Array.prototype下有了相应的实现。如果我们在代码中直接引入jQuery,这肯定是不科学的,这将使页面的脚本体积提高许多,同时还引入了很多我们根本用不上的多余功能。
面对这种情况,我们做了一个仅针对ie8的轻量级的兼容库es5-polyfill。它包括这些实现:Object的扩展函数、ES5对Array.prototype的扩充、标准的addEventListener和removeEventListener等。在入口文件顶部使用require('es5-polyfill');引入es5-polyfill后,只需3分钟,你就会和我一样,爱上这款框架可以在代码中愉快地使用上面说到的那些IE8不支持的API了。
但是,通过上面的CMD方式引入不就意味着对于IE9+的用户都引入了这些代码吗?这并不符合我们“随用随取,避免浪费”的原则。我们更推荐的做法,是在webpack中为配置多个entry,再使用HTMLWebpackPlugin在HTML模板中为es5-polyfill输出一段针对IE8的条件注释。具体实现可以参考nerv-webpack-boilerplate。
SVG Sprite
在页面中使用SVG,可以有效提升小图标在高清屏中的体验。类似于图片Sprite,SVG也可以通过Sprite来减少页面的请求(参考文章:拥抱Web设计新趋势:SVG Sprites实践应用)。
举个栗子,我们在Nerv中声明svgSprite组件,用以存放页面中用到的svg小图标:
接下来,我们可以在页面中动态引入上面的svgSprite组件就可以了:
在页面中挂载后,我们即可使用形如的代码去引用相应的图标了。
数据大屏
除了用户,我们同样也关注运营人员的体验。如果可以将运营数据都以直观的图表展示,这对于运营同学、产品同学都是十分幸福的事。这次的首页改版,我们与数据方合作,为首页配套开发了数据大屏项目SEE,用于运营数据的实时滚动展示。
SEE基于Nerv+Redux开发,使用ECharts进行数据的可视化展示。除了线上数据,SEE还有专门针对开发人员需求的性能版大屏,实时展示开发人员关心的页面onload时间,接口成功率,js报错数等指标。我们也希望未来SEE可以在更多的业务中用起来。
页面可用性保障和监控
我们做了许多优化工作来提升页面在性能、体验上的优良表现。但如果页面出现了JS逻辑错误,或者展示有问题,前面的优化工作就都前功尽弃了。所以在保证项目进度的基础上,我们又做了一系列的工作来保证首页的安全与稳定。
统一上线
同一份代码,经过不同版本的开发工具进行编译、压缩,生成的文件可能会天差地别,这种情况在多人协作中是相当致命的。比如:开发人员A的代码使用了新版本开发工具的API,而无辜的开发人员B对此毫不知情,使用了老版本的开发工具进行编译和发布…说多了都是泪,又是一场人间悲剧。
为了消除差异,我们希望不同开发人员的开发环境保持严格统一,但这其实是难以保证的:除了开发工具版本不同,有时候windows下,macOS下甚至是Linux下的表现也是不一样的。
为了解决这个问题,我们将编译的工作挪到了服务器端。开发人员在本地进行开发、自测,联调通过后提交到代码仓库中,确认上线后,上线平台拉取项目的代码,使用服务器端的工具链进行编译、压缩、发布等工作。
此外,上线平台还提供上线代码diff功能,可以将待上线的文件与线上的版本进行diff,待开发人员确认完才能继续上线操作。
接入上线平台后,开发人员再也不必担心开发环境的差异影响了编译结果,也不会误操作将其他同事开发中的分支带上线。就算是出现了线上bug,开发人员也可以轻松地通过上线平台记录的git commitId进行精确快速的回滚,有效保障了页面的可用性。
自动化测试
我们注意到,每次上线迭代,在经过编译工具的压缩、组合后,都有可能会对代码中其他部分的代码造成影响。如果在测试时只验证了当前迭代的功能点,疏漏了原先其他功能点的验证,就有可能引起一些意想不到的BUG。传统的DOM元素监控并无法满足我们的需求,因为有的bug出现的时机是在一连串特定的操作后。所以我们认为,我们造轮子的时候又到了。我们需要在Athena监控体系中增加一套针对页面中各个功能点的自动进行验证测试的系统。
这个系统基于selenium webdriver搭建。在后台中,开发者针对CSS选择器配置一系列的动作链Actionchain,包括点击、hover、输入文字、拖拽等操作,再通过对指定CSS选择器的HTML属性、样式、值等因素指定一个预期结果。后台服务会定时打开页面,执行预设的操作,如果与预期结果有出入,就会触发告警。
这种单服务器运行的e2e测试,容易碰到一些偶然网络波动的影响而导致乱告警。事实上,我们刚开始跑这套服务的时候,我们经常收到告警,但事实上页面展示并没有任何问题。针对这种偶发情况,我们在验证的过程中加入了失败重试的机制。只有在连续3次测试状态都为fail的情况,才会触发告警。经过优化后,监控的准确性有了质的提升。
素材监控
在生产环境中,除了程序BUG,数据运营的一些不规范操作也有可能影响到用户的体验。举例来说,如果页面中的图片体积过于庞大,会导致页面的加载时间变长,用户等待的时间会更久;如果页面中图片尺寸不合规,会导致 图片展示不正常,出现拉伸/压缩等现象,页面就会给人很山寨的感觉了。
针对这些素材异常情况,我们部署了针对性的监控服务。开发者针对特定的CSS选择器配置图片的标准体积以及尺寸。监控服务定时开启headless浏览器抓取页面中的图片并判断是否符合规则,不符合就触发告警。通过这样的手段,我们可以第一时间知道页面上出现的超限素材,通知运营的同学修改。
实时告警
作为Athena前端体系的一环,我们接入了Athena系统用于收集首页各种性能以及用户环境相关的数据。在这套系统中,我们可以获取到用户的屏幕分辨率占比,浏览器占比,同时还有页面加载时间、接口成功率等性能数据。
基于这些数据,我们在发现问题时可以进行有针对性的优化,比如调整特定接口的等待时间,或者是调整特定请求的重试策略。但是,并没有谁会一整天对这些数据的仪表盘盯着看;等我们发现问题,可能已经是上线后第二天,在工位上吃着早餐喝着牛奶的时候了。
解决信息滞后的问题,加强消息触达是关键。我们强化了Athena监控平台的功能:除了平台上仪表盘直观的数据展示,还支持配置告警规则。在绑定告警接收人的微信号后,平台就可以通过部门公众号实时推送告警信息,真正做到24小时监控,360度无盲点触达。
更长远的探索
“如何做得更好?”这是一个永不过时的问题。以前我们觉得在页面使用CMD的模块加载体系非常酷,所以后来会在项目中使用SeaJS;去年我们渴求一次架构升级,所以我们今年用上了Nerv。今年我们又会渴望什么呢?
前后端同构
作为提升性能的一个捷径,代码同构、服务器端渲染是目前看来的终极解决方案。我们计划搭建中间层,同样使用Nerv来渲染,从而减少首屏的代码逻辑,这将对页面的加载速度有大幅度的优化。
引入强类型校验
除了更快,我们也希望能够更稳。作为弱类型语言,JavaScript有着强大的灵活性,数据类型的相互转换十分便利;但也由于各种不严谨的类型转换,代码中存在着大量不可预测的分支走向,容易出现undefined的报错,调用不存在的API等等。
强类型语言TypeScript从13年诞生到现在,已经十分成熟了。在类型推断、静态验证上,TypeScript明显会更胜一筹;而在减少了多余的类型转换之后,TypeScript的性能表现也比常规JavaScript更强。我们希望未来可以将TypeScript在项目中用起来,这对于提升页面可靠性和性能,是很有意义的。
总结
这篇文章从整体开发架构与模式,性能、体验优化的探索,页面可用性的保障等方面对京东首页的开发过程做了简单的介绍。之所以说简单,是因为短短的篇幅完全无法说完我们在开发期间的故事和感悟:许多问题的解决并不像上面讲的那样水到渠成;除此之外更是有一大堆深夜加班撸串的故事没有地方讲。
最后献上个照片,这是项目上线成功之后在公司拍的通宵证明。虽然现在会觉得这拍得真……丑,但是项目成功上线的喜悦之情,我相信屏幕前的你也一样可以感受到。
查看全部
新世纪Nerv战士 - 京东首页补完计划
16版的京东首页,在性能、体验、灾备策略等各方面都做到了极致。站在如此高大的巨人肩上,除了满满的自信,我们心里更怕扑街。毫无疑问,我们在接到改版需求的那一刻,立马就敲定了新首页的技术选型:妥妥的jQuery+SeaJS!
但很快,我们就发现这样一点都不酷。jQuery是2006年的框架了,SeaJS停止维护也已经四年。这些项目的诞生都是为了解决当时业界的一些痛点:比如jQuery最开始是为了方便程序员在页面中操作DOM,绑定事件等;SeaJS则是为了在浏览器中实现CMD规范的模块开发和加载。但在各种VirtualDOM框架横飞的现在,程序员已经很少会直接操作DOM元素,而模块的开发和加载也有早已有了别的方案。
就在这时,Nerv项目的作者提出了建议:“不然用Nerv来一发?”我记得,当时他脸上洋溢着淳朴的笑,Nerv也仅仅是部门内部的一个小项目。我们回想了首页这个业务,技术栈已经好几年未曾更新过,开发流程也不够理想。如果再不做出改变,明年的这个时候我们依然会面对一堆陈年老代码头疼不已。抱着试一试的心态,我们接受了他的提议。没想到,这个决定让首页从此摆脱了落后的技术架构,而Nerv现在也已经成长为GitHub上3k+ Star的热门项目。
Q: 为什么不使用React/Preact/Vue?
A: 这三者都是前端圈子中相当流行的项目。React有完善的体系和浓厚的社区氛围,Preact有着羞涩的体积,Vue则使用了先进的html模板和数据绑定机制。但是,上边这三者都无法兼容IE8。我们在经过相关数据的论证后,发现IE8的用户还是有一定的价值,这才最终激发了我们团队内部自己造轮子的想法。当然,在造轮子的过程中,我们也不忘向上面这些优秀框架的看齐。最终,Nerv在完美兼容React语法的同时,具有着出众的性能表现,在Gzip后也只占用9Kb的体积。
整体架构
在这次的项目中,我们基于上一年久经考验的前端体系(详细介绍),进行了升级:
开发模式Athena2.0
1.0版本的Athena,基于vinyl-fs的流操作,或者说是类似于gulp的压缩、编译等等操作的任务流。而到了2017年,webpack早已在前端圈中流行。同行们也早已经习惯在项目中直接基于最新的语言特性去开发,在webpack.config.js加上一个babel-loader就可以完美支持新语法并完成打包。Athena 1.0背着太沉重的历史包袱,已经很难快速实现对babel转译的支持。所以在首页的开发前,我们将Athena升级到了全新的2.0版本。
一如既往,Athena会为项目提供init(初始化),serve(实时预览),build(编译),publish(发布)等功能。除此之外,由于2.0版本的Athena是基于webpack的,所以项目中可以统一用npm来管理依赖,也可以直接使用最新的ES语言特性来进行开发。
使用Athena2.0开发时,建议的文件架构如下:
前后端协作
我们依然是采用了前后端分离的协作模式,由后端给出json格式的数据,前端拉取json数据进行渲染。对于大部分的组件来说,都会在constructor中做好组件的初始化工作,在componentDidMount的生命周期中拉取数据写入组件的state,再通过render函数进行渲染。
代码规范约束
有一千个读者,就会有一千个哈姆雷特。
上面这句名言,深刻地体现在了16版首页的代码仓库中。同一个组件,如果是基于jQuery+SeaJS的模式,一千个程序猿就会有一千种写法。结果在同一个项目中,代码风格不尽相同,代码质量良莠不齐,多人协作也会无从下手。
何以解忧?唯有统一代码风格了。通过ESLint+Husky,我们对每次代码提交都做了代码风格检查,对是否使用prefer const的变量声明、代码缩进该使用Tab还是空格等等的规则都做了约束。一开始定下规范的时候,团队成员或多或少都会有些不习惯。但通过偷偷在代码里下毒Athena的生成的项目模板中添加对应的规则,潜移默化地,团队成员们也都开始接受、习惯这些约束。
禁用变量重声明等规则,在一定程度上保证了代码质量;而统一的代码样式风格,则使得项目的多人协作更加便利。
Q: 保证代码质量,促进多人协作的终极好处是什么?
A: 由于项目代码风格统一,通俗易懂容易上手,我们首页的开发团队终于开始有妹纸加入了!一群雄性程序猿敲代码能敲出什么火花啊…
对性能优化的探索首屏直出
直出可能是加快首屏加载最行之有效的办法了。它在减少页面加载时间、首屏请求数等方面的好处自然不必再提,结合jQuery,也可以很方便地在直出的DOM上进行更多的操作。
Nerv框架对于它内部的组件、DOM有着良好的操作性,但是对于体系外的DOM节点,却是天生的操作无力。举个例子,比如在页面文件中我们直出一个轮播图:
使用Nerv为这段HTML添加轮播逻辑,成为了非常艰难的操作。终极的解决方案,应该是使用SSR(Server Side Render)的方案,搭建Nerv-server中间层来将组件直出。但现在革命尚未成功,首屏直出尚且依赖后端的研发同学,首页上线又迫在眉睫。被逼急的我们最终选择了比较trick的方式来过渡这个问题:在组件初始化的时候先通过DOM操作获取渲染所需的数据,再将DOM替换成Nerv渲染后的内容。
代码分割
在生产环境中,随着代码体积增大,浏览器解压Gzip、执行等操作也会需要更多的开销。在SeaJS的时代,我们尚且会通过SeaJS.use或者require.async异步加载模块代码,避免一次性加载过多内容。但webpack的默认行为却会将整个页面的代码打包为一个单独的文件,这明显不是最佳的实践。对此,webpack给出的解决方案是动态引入(Dynamic Imports)。我们可以通过如下的代码来使用这个便利的特性:
与此同时,webpack会将使用了动态引入的组件从主bundle文件中抽离出来,这就减小了主bundle文件的体积。
对于我们的具体需求而言,需要做动态引入的一般是Nerv的组件。对于组件的动态引入,业界已经有非常好的实现方案react-loadable。
举个栗子,通过下面的代码,我们可以在页面中使用来实现对组件MyComponent的动态引入,并且具有加载超时、错误、加载中等不同状态的展示:
再进一步,我们希望对于屏幕外的组件,仅仅是在它进入用户视野后再开始加载,这也就是我们常说的滚动懒加载。这可以结合业界已有的懒加载组件react-lazyload来实现。针对上面的,在下面的例子中,只有进入用户屏幕后,MyComponent才会开始加载:
上面的例子为lazyload的组件设置了200px的占位高度。并且设定了占位元素的类名,方便设定样式。
代码延后加载
在给首页全面升级技术栈的时候,我们忽略了一个问题:页面上还引用着少量来自兄弟团队的SeaJS模块,我们升级了技术栈是可以,但是强迫兄弟团队也一起去掉SeaJS重构一遍代码,这就有点不合理了。我们也不能仅仅为了这部分模块,就把SeaJS给打包进代码里面,这也是不科学的。
上面讲到的动态引入功能,帮我们很好地解决了这个问题。我们在代码中单独抽离了一个legacy模块,其中包含了SeaJS、SeaJS-combo等老模块并做了导出。这部分代码在首屏中并不直接引入,而是在需要执行的时候,通过上面的动态引入功能,单独请求下来使用:
打包性能优化
webpack默认会对整个项目引用到的文件进行编译、打包,其中还包括了Nervjs、es5-polyfill等基础的依赖库。这些文件从加入项目开始,基本都不会再有任何更改;然而在每次构建新版本时,webpack打包的这些基础库都会与上一版本有一些细微的区别,这会导致用户浏览器中对应的代码缓存失效。为此,我们考虑将这些基础库分开打包。
针对这种需求,webpack官方建议使用DLL插件来优化。DLL是Dynamic Link Library的简称,是windows系统中对于应用程序依赖的函数库的称呼。对于webpack,我们需要使用一个单独的webpack配置去生成DLL:
接下来,在我们的项目的webpack配置中引用DllReferencdPlugin,传入上面生成的json文件:
这样就完成了动态链接库的生成和引用。除了最开始的一次编译,后续开发中如果基础库没有变动,DLL就再也不需要重新编译,这也就解决了上面的代码变动的问题。
体验优化探索兼容IE8
兼容旧版本IE浏览器一直是前端开发人员心中永远的痛。过去,我们使用jQuery去统一不同浏览器的DOM操作和绑定事件,通过jQuery元素实例的map、each等类数组函数批量做JavaScript动画,等等。
但是在使用Nerv之后,从体系外直接操作DOM就显得很不优雅;更推荐的写法,是通过组件的ref属性来访问原生DOM。而map、each等函数,IE9+的浏览器也已经在Array.prototype下有了相应的实现。如果我们在代码中直接引入jQuery,这肯定是不科学的,这将使页面的脚本体积提高许多,同时还引入了很多我们根本用不上的多余功能。
面对这种情况,我们做了一个仅针对ie8的轻量级的兼容库es5-polyfill。它包括这些实现:Object的扩展函数、ES5对Array.prototype的扩充、标准的addEventListener和removeEventListener等。在入口文件顶部使用require('es5-polyfill');引入es5-polyfill后,只需3分钟,你就会和我一样,爱上这款框架可以在代码中愉快地使用上面说到的那些IE8不支持的API了。
但是,通过上面的CMD方式引入不就意味着对于IE9+的用户都引入了这些代码吗?这并不符合我们“随用随取,避免浪费”的原则。我们更推荐的做法,是在webpack中为配置多个entry,再使用HTMLWebpackPlugin在HTML模板中为es5-polyfill输出一段针对IE8的条件注释。具体实现可以参考nerv-webpack-boilerplate。
SVG Sprite
在页面中使用SVG,可以有效提升小图标在高清屏中的体验。类似于图片Sprite,SVG也可以通过Sprite来减少页面的请求(参考文章:拥抱Web设计新趋势:SVG Sprites实践应用)。
举个栗子,我们在Nerv中声明svgSprite组件,用以存放页面中用到的svg小图标:
接下来,我们可以在页面中动态引入上面的svgSprite组件就可以了:
在页面中挂载后,我们即可使用形如的代码去引用相应的图标了。
数据大屏
除了用户,我们同样也关注运营人员的体验。如果可以将运营数据都以直观的图表展示,这对于运营同学、产品同学都是十分幸福的事。这次的首页改版,我们与数据方合作,为首页配套开发了数据大屏项目SEE,用于运营数据的实时滚动展示。
SEE基于Nerv+Redux开发,使用ECharts进行数据的可视化展示。除了线上数据,SEE还有专门针对开发人员需求的性能版大屏,实时展示开发人员关心的页面onload时间,接口成功率,js报错数等指标。我们也希望未来SEE可以在更多的业务中用起来。
页面可用性保障和监控
我们做了许多优化工作来提升页面在性能、体验上的优良表现。但如果页面出现了JS逻辑错误,或者展示有问题,前面的优化工作就都前功尽弃了。所以在保证项目进度的基础上,我们又做了一系列的工作来保证首页的安全与稳定。
统一上线
同一份代码,经过不同版本的开发工具进行编译、压缩,生成的文件可能会天差地别,这种情况在多人协作中是相当致命的。比如:开发人员A的代码使用了新版本开发工具的API,而无辜的开发人员B对此毫不知情,使用了老版本的开发工具进行编译和发布…说多了都是泪,又是一场人间悲剧。
为了消除差异,我们希望不同开发人员的开发环境保持严格统一,但这其实是难以保证的:除了开发工具版本不同,有时候windows下,macOS下甚至是Linux下的表现也是不一样的。
为了解决这个问题,我们将编译的工作挪到了服务器端。开发人员在本地进行开发、自测,联调通过后提交到代码仓库中,确认上线后,上线平台拉取项目的代码,使用服务器端的工具链进行编译、压缩、发布等工作。
此外,上线平台还提供上线代码diff功能,可以将待上线的文件与线上的版本进行diff,待开发人员确认完才能继续上线操作。
接入上线平台后,开发人员再也不必担心开发环境的差异影响了编译结果,也不会误操作将其他同事开发中的分支带上线。就算是出现了线上bug,开发人员也可以轻松地通过上线平台记录的git commitId进行精确快速的回滚,有效保障了页面的可用性。
自动化测试
我们注意到,每次上线迭代,在经过编译工具的压缩、组合后,都有可能会对代码中其他部分的代码造成影响。如果在测试时只验证了当前迭代的功能点,疏漏了原先其他功能点的验证,就有可能引起一些意想不到的BUG。传统的DOM元素监控并无法满足我们的需求,因为有的bug出现的时机是在一连串特定的操作后。所以我们认为,我们造轮子的时候又到了。我们需要在Athena监控体系中增加一套针对页面中各个功能点的自动进行验证测试的系统。
这个系统基于selenium webdriver搭建。在后台中,开发者针对CSS选择器配置一系列的动作链Actionchain,包括点击、hover、输入文字、拖拽等操作,再通过对指定CSS选择器的HTML属性、样式、值等因素指定一个预期结果。后台服务会定时打开页面,执行预设的操作,如果与预期结果有出入,就会触发告警。
这种单服务器运行的e2e测试,容易碰到一些偶然网络波动的影响而导致乱告警。事实上,我们刚开始跑这套服务的时候,我们经常收到告警,但事实上页面展示并没有任何问题。针对这种偶发情况,我们在验证的过程中加入了失败重试的机制。只有在连续3次测试状态都为fail的情况,才会触发告警。经过优化后,监控的准确性有了质的提升。
素材监控
在生产环境中,除了程序BUG,数据运营的一些不规范操作也有可能影响到用户的体验。举例来说,如果页面中的图片体积过于庞大,会导致页面的加载时间变长,用户等待的时间会更久;如果页面中图片尺寸不合规,会导致 图片展示不正常,出现拉伸/压缩等现象,页面就会给人很山寨的感觉了。
针对这些素材异常情况,我们部署了针对性的监控服务。开发者针对特定的CSS选择器配置图片的标准体积以及尺寸。监控服务定时开启headless浏览器抓取页面中的图片并判断是否符合规则,不符合就触发告警。通过这样的手段,我们可以第一时间知道页面上出现的超限素材,通知运营的同学修改。
实时告警
作为Athena前端体系的一环,我们接入了Athena系统用于收集首页各种性能以及用户环境相关的数据。在这套系统中,我们可以获取到用户的屏幕分辨率占比,浏览器占比,同时还有页面加载时间、接口成功率等性能数据。
基于这些数据,我们在发现问题时可以进行有针对性的优化,比如调整特定接口的等待时间,或者是调整特定请求的重试策略。但是,并没有谁会一整天对这些数据的仪表盘盯着看;等我们发现问题,可能已经是上线后第二天,在工位上吃着早餐喝着牛奶的时候了。
解决信息滞后的问题,加强消息触达是关键。我们强化了Athena监控平台的功能:除了平台上仪表盘直观的数据展示,还支持配置告警规则。在绑定告警接收人的微信号后,平台就可以通过部门公众号实时推送告警信息,真正做到24小时监控,360度无盲点触达。
更长远的探索
“如何做得更好?”这是一个永不过时的问题。以前我们觉得在页面使用CMD的模块加载体系非常酷,所以后来会在项目中使用SeaJS;去年我们渴求一次架构升级,所以我们今年用上了Nerv。今年我们又会渴望什么呢?
前后端同构
作为提升性能的一个捷径,代码同构、服务器端渲染是目前看来的终极解决方案。我们计划搭建中间层,同样使用Nerv来渲染,从而减少首屏的代码逻辑,这将对页面的加载速度有大幅度的优化。
引入强类型校验
除了更快,我们也希望能够更稳。作为弱类型语言,JavaScript有着强大的灵活性,数据类型的相互转换十分便利;但也由于各种不严谨的类型转换,代码中存在着大量不可预测的分支走向,容易出现undefined的报错,调用不存在的API等等。
强类型语言TypeScript从13年诞生到现在,已经十分成熟了。在类型推断、静态验证上,TypeScript明显会更胜一筹;而在减少了多余的类型转换之后,TypeScript的性能表现也比常规JavaScript更强。我们希望未来可以将TypeScript在项目中用起来,这对于提升页面可靠性和性能,是很有意义的。
总结
这篇文章从整体开发架构与模式,性能、体验优化的探索,页面可用性的保障等方面对京东首页的开发过程做了简单的介绍。之所以说简单,是因为短短的篇幅完全无法说完我们在开发期间的故事和感悟:许多问题的解决并不像上面讲的那样水到渠成;除此之外更是有一大堆深夜加班撸串的故事没有地方讲。
最后献上个照片,这是项目上线成功之后在公司拍的通宵证明。虽然现在会觉得这拍得真……丑,但是项目成功上线的喜悦之情,我相信屏幕前的你也一样可以感受到。
jquery抓取网页内容?去除js,javascript等的混淆和去除化javascript
网站优化 • 优采云 发表了文章 • 0 个评论 • 68 次浏览 • 2022-06-20 06:04
jquery抓取网页内容???去除js,javascript等的混淆和去除语义化javascript标签seo优化网页优化响应式seo分享javascriptsimilarity。jsesjsesfjavascript下载javascriptextension。5。6。1更新:???seo导航-v1。
3。4。9更新:???javascript预处理器v4。6。3javascript预处理器ejsv4。25。1(v4。23。
2)url
抛砖引玉吧,随便写写。1.变量和常量的区别。首先要明确两个概念,常量:number,string,long,boolean,null,undefined等字符串,基本数据类型字符串变量:数值,number,string,boolean,symbol等字符串变量...还有..数组(object)类型。变量-字符串常量不常量常量_百度百科,如下这一段:常量functionfuck(){console.log('eatthebean');}console.log(fuck())//eatthebean常量functionfuck(){console.log('eatthebean');}fuck();//eatthebean常量functionfuck(){console.log('eatthebean');}fuck();//eatthebean}常量functionfuck(){console.log('eatthebean');}fuck();//eatthebean因为变量fuck(),而fuck又一直是fuck,所以常量就是常数。
这也是比较符合人的思维方式的。之前有人问我,如何让javascript为所欲为,让他去执行我们定义的fuck,我又是怎么回答他的呢?我想了下,大概应该是这样:常量fuck();常量fuck();常量fuck();定义一个函数的时候,用fuck(),那么常量fuck()就变成了fuck函数的常量。定义一个变量,也可以用fuck(),那么常量fuck()就变成了这个变量的常量,特别是对于数组,变量fuck()的作用范围是1-99,如果让我自己定义,我会这么定义:varmy_check_check=[1,2,3,4,5,6,7,8,9];functionprice(value){returnvalue;}my_check_check(。
1)//=>1functionadd_check_check(value){returnvalue+1;}my_check_check
3)//=>5functionmy_check_check(value){returnvalue+value+1;}my_check_check
5)//=>10现在这个变量就是纯虚的常量,你想让它干什么就干什么。现在有人问我,如何让javascript写不出来虚函数和匿名函数,我就想到前段时间在慕课网上的演讲上提到的一个方法,就是把一个正则里有虚函数、没有匿名函数作为一个模板自动生成一个匿名函数,这样一来, 查看全部
jquery抓取网页内容?去除js,javascript等的混淆和去除化javascript
jquery抓取网页内容???去除js,javascript等的混淆和去除语义化javascript标签seo优化网页优化响应式seo分享javascriptsimilarity。jsesjsesfjavascript下载javascriptextension。5。6。1更新:???seo导航-v1。
3。4。9更新:???javascript预处理器v4。6。3javascript预处理器ejsv4。25。1(v4。23。
2)url
抛砖引玉吧,随便写写。1.变量和常量的区别。首先要明确两个概念,常量:number,string,long,boolean,null,undefined等字符串,基本数据类型字符串变量:数值,number,string,boolean,symbol等字符串变量...还有..数组(object)类型。变量-字符串常量不常量常量_百度百科,如下这一段:常量functionfuck(){console.log('eatthebean');}console.log(fuck())//eatthebean常量functionfuck(){console.log('eatthebean');}fuck();//eatthebean常量functionfuck(){console.log('eatthebean');}fuck();//eatthebean}常量functionfuck(){console.log('eatthebean');}fuck();//eatthebean因为变量fuck(),而fuck又一直是fuck,所以常量就是常数。
这也是比较符合人的思维方式的。之前有人问我,如何让javascript为所欲为,让他去执行我们定义的fuck,我又是怎么回答他的呢?我想了下,大概应该是这样:常量fuck();常量fuck();常量fuck();定义一个函数的时候,用fuck(),那么常量fuck()就变成了fuck函数的常量。定义一个变量,也可以用fuck(),那么常量fuck()就变成了这个变量的常量,特别是对于数组,变量fuck()的作用范围是1-99,如果让我自己定义,我会这么定义:varmy_check_check=[1,2,3,4,5,6,7,8,9];functionprice(value){returnvalue;}my_check_check(。
1)//=>1functionadd_check_check(value){returnvalue+1;}my_check_check
3)//=>5functionmy_check_check(value){returnvalue+value+1;}my_check_check
5)//=>10现在这个变量就是纯虚的常量,你想让它干什么就干什么。现在有人问我,如何让javascript写不出来虚函数和匿名函数,我就想到前段时间在慕课网上的演讲上提到的一个方法,就是把一个正则里有虚函数、没有匿名函数作为一个模板自动生成一个匿名函数,这样一来,
《简单爬虫》——抓取一部小说
网站优化 • 优采云 发表了文章 • 0 个评论 • 85 次浏览 • 2022-06-05 02:52
例子是基于nodejs的,也不是很复杂,有兴趣的可以自己也来敲敲代码。
情不知从何起
很多人喜欢手机阅读,当然我也是,不过我比较喜欢看一些盗墓偏玄幻类的小说,比如《鬼吹灯》、《盗墓笔记》这样的。现在用手机来阅读也是很方便的事情,下个app就好了,什么起点、追书神器、QQ阅读之类的,但是这些多数都是要收费的,特别是那种还未完结的。收费还贵,1章少一点的5毛,多一点的那种要收1块,这类小说不写个1000多章都不好意思出来混。
对于我这样的免(dao)费(ban)程序受益者,怎么可能花那么大的代(jin)价(qian)来支持他。所以,第一原则:百度找免费的。百度一下果然有免费的,而且还是txt的,下载完成。开始看书。
当然,不可能所有事情都是那么顺利就解决的。比如下载的txt有时候会少章节,有时候少内容,看的不够尽兴。然后我又开始找网站了,一搜一大把,然后问题又来了,一章一个页面,每次看好一章就要去刷一下页面,每次还会加载很多小广告,这里的小广告就厉害了,不点掉会挡住部分文字,点一下就不知道跳转到哪里去了,有时候点击下一章也会是这样的。这流量刷刷刷的就没有了,对于我这样的程(qiong)序(diao)员(si),一个月300M流量不够用啊。
怎么办呢?毕竟咱们是个程序员啊,想点办法吧。那么我们用爬虫把页面里面的文字爬下来就好了嘛,于是就又了今天的内容了《简单爬虫》——抓取一部小说。
而一往情深
选角
爬虫当然有很多可以选择的,各种后端语言(java、.net)、脚本语言(php、nodejs、python),很多都可以的,但是作为一名前端工程师,在现在这个大环境下,多少需要知道一些nodejs,就算不会,但是至少还是需要知道的,不然怎么能成为一名合格的前端工程师呢?所以这次试着用用nodejs。
开搞
首先,我们要找到一个我们需要抓取的文章页面。这里抓一下天蚕土豆的《大主宰》。天蚕土豆应该都知道吧,当年的《斗破苍穹》就是这家伙写的,火的不行。
思路
我们需要抓取每一章小说内容,然后将每一章按照顺序排列好之后,写入文件——大主宰.txt中。好了思路大致就这样,接下来开撸代码。
var superagent = require("superagent")//用superagent去抓取
var cheerio = require("cheerio");//分析网页结构
//大主宰第一章页面
var first_url = "";
superagent.get(first_url).end(function(err,sres){
var $ = cheerio.load(sres.text);
var con = $("#content").text();
consoe.log(con);
})
先说一下代码superagent是一个http的库,这边我们用来发起http请求。然后cheerio这东西就更好了,说白了就是一个node版的jquery,这里我通过页面的代码结构知道文章都在一个ID为content的div里面。
运行一下居然是一堆乱码。。。一看就知道是编码问题了,看一下啊html编码是gbk的,一般我们的网站都是utf-8的,但是很多小说的网站都是gbk的编码,这是为什么呢?查看一下GBK编码:是指中国的中文字符,它包含了简体中文与繁体中文字符,另外还有一种字符“gb2312”,这种字符仅能存储简体中文字符。GBK编码格式,它的功能少,仅限于中文字符,当然它所占用的空间大小会随着它的功能而减少,打开网页的速度比较快。
知道为什么用gbk了,现在就开始转码吧
var superagent = require("superagent")//用superagent去抓取
var cheerio = require("cheerio");//分析网页结构
require('superagent-charset')(superagent);//引入gbk转换插件
//大主宰第一章页面
var first_url = "";
superagent.get(first_url).charset('gbk').end(function(err,sres){
var $ = cheerio.load(sres.text);
var con = $("#content").text();
console.log(con);
})
哈哈,搞定了,单个页面抓取成功了,那么就剩多页面抓取啦。
思路:列表页抓取单个章节的文章链接,然后再去抓取单页的文章,最后拼接写入文件。恩,思路可行,那么我们来看一下列表页面...
抓取list下的里面的a标签的href即可
//大主宰列表页面
var list_url = "";
superagent.get(list_url).end(function(err,sres){
var $ = cheerio.load(sres.text);
var listurl = new Array();//存放单章的url
$("#list dl dd a").each(function(idex,element){
var $ele = $(element);
listurl.push(list_url+$ele.attr("href").replace("/0_757/",""));
});
console.log(listurl);
})
我们这里打印一下列表页面的链接地址看一下。
恩 打印的链接没问题
superagent.get(list_url).end(function(err,sres){
var $ = cheerio.load(sres.text);
var listurl = new Array();//存放单章的url
$("#list dl dd a").each(function(idex,element){
var $ele = $(element);
listurl.push(list_url+$ele.attr("href").replace("/0_757/",""));
});
var content = "";//最终要的文章内容
var tmplisturl = listurl.slice(0,10);
tmplisturl.forEach(function(item){
superagent.get(item).charset('gbk').end(function(err,sres){
var $ = cheerio.load(sres.text);
var con = $("#content").text();
content+=con;
})
});
console.log(content)
});
那么来看一下代码多了一个循环,但是我打印出来的content居然是空...why?哦原来这个superagent抓取的时候是一个异步操作。所以我需要一个监听事件来告诉我,异步完成了才行。
我们这里用eventproxy,用来处理异步协作是很有用的,有兴趣的可以去#%E9%87%8D%E5%A4%8D%E5%BC%82%E6%AD%A5%E5%8D%8F%E4%BD%9C 学习一下。
superagent.get(list_url).end(function(err,sres){
var $ = cheerio.load(sres.text);
var listurl = new Array();//存放单章的url
$("#list dl dd a").each(function(idex,element){
var $ele = $(element);
listurl.push(list_url+$ele.attr("href").replace("/0_757/",""));
});
var content = "";//最终要的文章内容
var tmplisturl = listurl.slice(0,10);
tmplisturl.forEach(function(item){
superagent.get(item).charset('gbk').end(function(err,sres){
var $ = cheerio.load(sres.text);
var name = $(".bookname h1").text();
var con = $("#content").text().replace(/\n/g,'');
content+=name+"\n"+con+"\n";
ep.emit("finish");
})
});
ep.after("finish",tmplisturl.length,function(){
console.log(content);
});
});
ok啦!文章都能打印出来了,这里试了一下搞了前面10篇,加了点去除换行之类的小东西。但是还是有点问题,看下面的打印截图:
因为是异步的原因,所以没有按照我们原来设想的那样按照章节的顺序输出。想想是不是得先用对象保存起来然后再来排序输出呢?
tmplisturl.forEach(function(item){
superagent.get(item.url).charset('gbk').end(function(err,sres){
var $ = cheerio.load(sres.text);
var name = $(".bookname h1").text();
var con = $("#content").text().replace(/\n/g,'');
item.con = name+"\n"+con+"\n";
ep.emit("finish");
})
});
ep.after("finish",tmplisturl.length,function(){ //模拟10次
var content = "";
for(var i=0;i
content+=tmplisturl[i].con;
}
console.log(content);
});
试了一下果然ok啦。打完收工,领盒饭~
哦,还有最后需要写入文件
//'a+' - 以读取和追加模式打开文件。如果文件不存在,则会被创建
fs.open("大主宰.txt",'a+',function(){
fs.appendFile("大主宰.txt",content,function(){
console.log("写完了");
});
})
这里还有一点要提一下,别一次写入太多东西到文件中,内存不足会崩溃的。我这边做了一下分割循环。大概是30章一分割,也就是每次请求30章内容然后写入文件,待文件写入成功后继续请求30章,直到完成为止。但是我这边却只是写了600多章就没有了,这让我很惆怅...检查一下代码好像没什么问题,然后再次跑了一次,依然是600章不到的地方不动了..
看看打印的log信息:
想了想是不是我的内存不够用啊?看了一下,还真是这么一回事!
然后开始清理一下自己开的东西。微信,chrome,qq,outlook,sourcetree,phpstorm关了,然后瞬间清爽了。
剩余了10个G,继续跑一遍试试看,靠...还是不行,这次比之前的都少了,才200多章就停了...还不是内存占用的问题,那就是异步写入导致的I/O阻塞?
fs.appendFile("大主宰.txt",content,function(){
console.log("写入成功"+go)
if(go){
setTimeout(function(){
write();
},1000);
}else{
console.log('完成');
}
});
改成这样试试看,为了测试猜想,我只是写入了文章的标题。看一下运行结果:
还真的可以啊,当然这里的用的setTimeout,回头改成文件的关闭事件就好了。
完整代码
var superagent = require("superagent")//用superagent去抓取
var cheerio = require("cheerio");//分析网页结构
var eventproxy = require("eventproxy");//控制并发
require('superagent-charset')(superagent);//引入gbk转换插件
var list_url = "";//大主宰列表页面
var ep = new eventproxy();//得到一个 eventproxy 的实例
var fs = require("fs");//文件模块
console.time("start")
superagent.get(list_url).end(function(err,sres){
var $ = cheerio.load(sres.text);
var listurl = new Array();//存放单章的url
$("#list dl dd a").each(function(idex,element){
var $ele = $(element);
var tmpurl = $ele.attr("href");
var tmpid = tmpurl.replace("/0_757/","").replace(".html","")*1;
var tmp = {
url:list_url+tmpurl.replace("/0_757/",""),
id:tmpid,
con:""
}
listurl.push(tmp);
});
write();
function write(){
var tmplisturl = listurl.splice(0,30);
var go = true;
if(tmplisturl.length 查看全部
《简单爬虫》——抓取一部小说
例子是基于nodejs的,也不是很复杂,有兴趣的可以自己也来敲敲代码。
情不知从何起
很多人喜欢手机阅读,当然我也是,不过我比较喜欢看一些盗墓偏玄幻类的小说,比如《鬼吹灯》、《盗墓笔记》这样的。现在用手机来阅读也是很方便的事情,下个app就好了,什么起点、追书神器、QQ阅读之类的,但是这些多数都是要收费的,特别是那种还未完结的。收费还贵,1章少一点的5毛,多一点的那种要收1块,这类小说不写个1000多章都不好意思出来混。
对于我这样的免(dao)费(ban)程序受益者,怎么可能花那么大的代(jin)价(qian)来支持他。所以,第一原则:百度找免费的。百度一下果然有免费的,而且还是txt的,下载完成。开始看书。
当然,不可能所有事情都是那么顺利就解决的。比如下载的txt有时候会少章节,有时候少内容,看的不够尽兴。然后我又开始找网站了,一搜一大把,然后问题又来了,一章一个页面,每次看好一章就要去刷一下页面,每次还会加载很多小广告,这里的小广告就厉害了,不点掉会挡住部分文字,点一下就不知道跳转到哪里去了,有时候点击下一章也会是这样的。这流量刷刷刷的就没有了,对于我这样的程(qiong)序(diao)员(si),一个月300M流量不够用啊。
怎么办呢?毕竟咱们是个程序员啊,想点办法吧。那么我们用爬虫把页面里面的文字爬下来就好了嘛,于是就又了今天的内容了《简单爬虫》——抓取一部小说。
而一往情深
选角
爬虫当然有很多可以选择的,各种后端语言(java、.net)、脚本语言(php、nodejs、python),很多都可以的,但是作为一名前端工程师,在现在这个大环境下,多少需要知道一些nodejs,就算不会,但是至少还是需要知道的,不然怎么能成为一名合格的前端工程师呢?所以这次试着用用nodejs。
开搞
首先,我们要找到一个我们需要抓取的文章页面。这里抓一下天蚕土豆的《大主宰》。天蚕土豆应该都知道吧,当年的《斗破苍穹》就是这家伙写的,火的不行。
思路
我们需要抓取每一章小说内容,然后将每一章按照顺序排列好之后,写入文件——大主宰.txt中。好了思路大致就这样,接下来开撸代码。
var superagent = require("superagent")//用superagent去抓取
var cheerio = require("cheerio");//分析网页结构
//大主宰第一章页面
var first_url = "";
superagent.get(first_url).end(function(err,sres){
var $ = cheerio.load(sres.text);
var con = $("#content").text();
consoe.log(con);
})
先说一下代码superagent是一个http的库,这边我们用来发起http请求。然后cheerio这东西就更好了,说白了就是一个node版的jquery,这里我通过页面的代码结构知道文章都在一个ID为content的div里面。
运行一下居然是一堆乱码。。。一看就知道是编码问题了,看一下啊html编码是gbk的,一般我们的网站都是utf-8的,但是很多小说的网站都是gbk的编码,这是为什么呢?查看一下GBK编码:是指中国的中文字符,它包含了简体中文与繁体中文字符,另外还有一种字符“gb2312”,这种字符仅能存储简体中文字符。GBK编码格式,它的功能少,仅限于中文字符,当然它所占用的空间大小会随着它的功能而减少,打开网页的速度比较快。
知道为什么用gbk了,现在就开始转码吧
var superagent = require("superagent")//用superagent去抓取
var cheerio = require("cheerio");//分析网页结构
require('superagent-charset')(superagent);//引入gbk转换插件
//大主宰第一章页面
var first_url = "";
superagent.get(first_url).charset('gbk').end(function(err,sres){
var $ = cheerio.load(sres.text);
var con = $("#content").text();
console.log(con);
})
哈哈,搞定了,单个页面抓取成功了,那么就剩多页面抓取啦。
思路:列表页抓取单个章节的文章链接,然后再去抓取单页的文章,最后拼接写入文件。恩,思路可行,那么我们来看一下列表页面...
抓取list下的里面的a标签的href即可
//大主宰列表页面
var list_url = "";
superagent.get(list_url).end(function(err,sres){
var $ = cheerio.load(sres.text);
var listurl = new Array();//存放单章的url
$("#list dl dd a").each(function(idex,element){
var $ele = $(element);
listurl.push(list_url+$ele.attr("href").replace("/0_757/",""));
});
console.log(listurl);
})
我们这里打印一下列表页面的链接地址看一下。
恩 打印的链接没问题
superagent.get(list_url).end(function(err,sres){
var $ = cheerio.load(sres.text);
var listurl = new Array();//存放单章的url
$("#list dl dd a").each(function(idex,element){
var $ele = $(element);
listurl.push(list_url+$ele.attr("href").replace("/0_757/",""));
});
var content = "";//最终要的文章内容
var tmplisturl = listurl.slice(0,10);
tmplisturl.forEach(function(item){
superagent.get(item).charset('gbk').end(function(err,sres){
var $ = cheerio.load(sres.text);
var con = $("#content").text();
content+=con;
})
});
console.log(content)
});
那么来看一下代码多了一个循环,但是我打印出来的content居然是空...why?哦原来这个superagent抓取的时候是一个异步操作。所以我需要一个监听事件来告诉我,异步完成了才行。
我们这里用eventproxy,用来处理异步协作是很有用的,有兴趣的可以去#%E9%87%8D%E5%A4%8D%E5%BC%82%E6%AD%A5%E5%8D%8F%E4%BD%9C 学习一下。
superagent.get(list_url).end(function(err,sres){
var $ = cheerio.load(sres.text);
var listurl = new Array();//存放单章的url
$("#list dl dd a").each(function(idex,element){
var $ele = $(element);
listurl.push(list_url+$ele.attr("href").replace("/0_757/",""));
});
var content = "";//最终要的文章内容
var tmplisturl = listurl.slice(0,10);
tmplisturl.forEach(function(item){
superagent.get(item).charset('gbk').end(function(err,sres){
var $ = cheerio.load(sres.text);
var name = $(".bookname h1").text();
var con = $("#content").text().replace(/\n/g,'');
content+=name+"\n"+con+"\n";
ep.emit("finish");
})
});
ep.after("finish",tmplisturl.length,function(){
console.log(content);
});
});
ok啦!文章都能打印出来了,这里试了一下搞了前面10篇,加了点去除换行之类的小东西。但是还是有点问题,看下面的打印截图:
因为是异步的原因,所以没有按照我们原来设想的那样按照章节的顺序输出。想想是不是得先用对象保存起来然后再来排序输出呢?
tmplisturl.forEach(function(item){
superagent.get(item.url).charset('gbk').end(function(err,sres){
var $ = cheerio.load(sres.text);
var name = $(".bookname h1").text();
var con = $("#content").text().replace(/\n/g,'');
item.con = name+"\n"+con+"\n";
ep.emit("finish");
})
});
ep.after("finish",tmplisturl.length,function(){ //模拟10次
var content = "";
for(var i=0;i
content+=tmplisturl[i].con;
}
console.log(content);
});
试了一下果然ok啦。打完收工,领盒饭~
哦,还有最后需要写入文件
//'a+' - 以读取和追加模式打开文件。如果文件不存在,则会被创建
fs.open("大主宰.txt",'a+',function(){
fs.appendFile("大主宰.txt",content,function(){
console.log("写完了");
});
})
这里还有一点要提一下,别一次写入太多东西到文件中,内存不足会崩溃的。我这边做了一下分割循环。大概是30章一分割,也就是每次请求30章内容然后写入文件,待文件写入成功后继续请求30章,直到完成为止。但是我这边却只是写了600多章就没有了,这让我很惆怅...检查一下代码好像没什么问题,然后再次跑了一次,依然是600章不到的地方不动了..
看看打印的log信息:
想了想是不是我的内存不够用啊?看了一下,还真是这么一回事!
然后开始清理一下自己开的东西。微信,chrome,qq,outlook,sourcetree,phpstorm关了,然后瞬间清爽了。
剩余了10个G,继续跑一遍试试看,靠...还是不行,这次比之前的都少了,才200多章就停了...还不是内存占用的问题,那就是异步写入导致的I/O阻塞?
fs.appendFile("大主宰.txt",content,function(){
console.log("写入成功"+go)
if(go){
setTimeout(function(){
write();
},1000);
}else{
console.log('完成');
}
});
改成这样试试看,为了测试猜想,我只是写入了文章的标题。看一下运行结果:
还真的可以啊,当然这里的用的setTimeout,回头改成文件的关闭事件就好了。
完整代码
var superagent = require("superagent")//用superagent去抓取
var cheerio = require("cheerio");//分析网页结构
var eventproxy = require("eventproxy");//控制并发
require('superagent-charset')(superagent);//引入gbk转换插件
var list_url = "";//大主宰列表页面
var ep = new eventproxy();//得到一个 eventproxy 的实例
var fs = require("fs");//文件模块
console.time("start")
superagent.get(list_url).end(function(err,sres){
var $ = cheerio.load(sres.text);
var listurl = new Array();//存放单章的url
$("#list dl dd a").each(function(idex,element){
var $ele = $(element);
var tmpurl = $ele.attr("href");
var tmpid = tmpurl.replace("/0_757/","").replace(".html","")*1;
var tmp = {
url:list_url+tmpurl.replace("/0_757/",""),
id:tmpid,
con:""
}
listurl.push(tmp);
});
write();
function write(){
var tmplisturl = listurl.splice(0,30);
var go = true;
if(tmplisturl.length
jquery抓取网页内容,完整教程可以参考我翻译的文章
网站优化 • 优采云 发表了文章 • 0 个评论 • 78 次浏览 • 2022-05-30 10:07
jquery抓取网页内容,完整教程可以参考我翻译的文章;-mocking/里面详细说明了如何抓取html页面。对于jquery并不熟悉,只是学过jquerydom,会用几个函数,并不知道该如何抓取html内容,所以对抓取的流程都不熟悉。初学者,建议看我的这篇文章jquery抓取内容,对抓取有一个大概的了解jquery抓取网页内容_技术博客_zim_新浪博客。
对于没有任何编程基础的同学,可以尝试先从这篇文章了解一下抓取html的步骤。了解完以后有可能对python抓取js源码这件事就会有一个感性的认识了。
可以通过如下方式:1.爬取百度搜索信息:搜索关键词-->点击列表列表信息2.爬取百度文库:百度文库页面内容
一、爬虫简介
1、爬虫是什么?顾名思义,爬虫就是“爬行”网页的机器,
2、爬虫有哪些分类?爬虫分为以下几类:搜索引擎爬虫、抓取网页的机器人等;
3、哪些网站需要爬虫?
1)自然搜索引擎,要学会谷歌搜索技术、关键词、竞价广告等基础抓取,
2)用户产生的内容,比如知乎的使用体验内容等,
3)小说、新闻等互联网专业产生的内容也可以靠爬虫解决。
二、爬虫入门
1、概念爬虫程序,
2、代码爬虫的入门,
1、如何通过浏览器输入网址链接来抓取网页内容
2、异步获取网页内容?在浏览器输入网址的时候,浏览器会按照特定的结构去结构化搜索内容(即将关键字进行编码储存,储存的结构为plainjs、postjs、basejs等格式,如:cookie),所以当你爬取的网页经过登录等安全防护的时候,就可以直接抓取你想要的网页,从而形成“爬虫”。
爬虫如何实现异步获取呢?看下下面的图解:异步获取网页内容
1、通过selenium模拟自己的电脑去执行命令;
2、通过asyncio库自己的异步机制
3、看一下如何异步获取网页内容:/,例如:requests(request)是异步下的模拟请求方法,通过selenium模拟其他人工操作浏览器,从而提取网页上的内容的方法。
三、爬虫延伸
1、高阶爬虫?本身爬虫主要目的是爬取网页数据,但是爬虫中也会涉及到一些高阶的操作,如自动发帖、自动上传图片等。
2、爬虫扩展
1)可爬取博客列表
1)博客列表分为新闻站,说明各个官方博客的站点有编辑把内容抓取过来,再存入对应的get方法中,进而把数据保存到目标post、head等方法中。
2)博客站点有几百个,虽然定位博客列表爬虫容易, 查看全部
jquery抓取网页内容,完整教程可以参考我翻译的文章
jquery抓取网页内容,完整教程可以参考我翻译的文章;-mocking/里面详细说明了如何抓取html页面。对于jquery并不熟悉,只是学过jquerydom,会用几个函数,并不知道该如何抓取html内容,所以对抓取的流程都不熟悉。初学者,建议看我的这篇文章jquery抓取内容,对抓取有一个大概的了解jquery抓取网页内容_技术博客_zim_新浪博客。
对于没有任何编程基础的同学,可以尝试先从这篇文章了解一下抓取html的步骤。了解完以后有可能对python抓取js源码这件事就会有一个感性的认识了。
可以通过如下方式:1.爬取百度搜索信息:搜索关键词-->点击列表列表信息2.爬取百度文库:百度文库页面内容
一、爬虫简介
1、爬虫是什么?顾名思义,爬虫就是“爬行”网页的机器,
2、爬虫有哪些分类?爬虫分为以下几类:搜索引擎爬虫、抓取网页的机器人等;
3、哪些网站需要爬虫?
1)自然搜索引擎,要学会谷歌搜索技术、关键词、竞价广告等基础抓取,
2)用户产生的内容,比如知乎的使用体验内容等,
3)小说、新闻等互联网专业产生的内容也可以靠爬虫解决。
二、爬虫入门
1、概念爬虫程序,
2、代码爬虫的入门,
1、如何通过浏览器输入网址链接来抓取网页内容
2、异步获取网页内容?在浏览器输入网址的时候,浏览器会按照特定的结构去结构化搜索内容(即将关键字进行编码储存,储存的结构为plainjs、postjs、basejs等格式,如:cookie),所以当你爬取的网页经过登录等安全防护的时候,就可以直接抓取你想要的网页,从而形成“爬虫”。
爬虫如何实现异步获取呢?看下下面的图解:异步获取网页内容
1、通过selenium模拟自己的电脑去执行命令;
2、通过asyncio库自己的异步机制
3、看一下如何异步获取网页内容:/,例如:requests(request)是异步下的模拟请求方法,通过selenium模拟其他人工操作浏览器,从而提取网页上的内容的方法。
三、爬虫延伸
1、高阶爬虫?本身爬虫主要目的是爬取网页数据,但是爬虫中也会涉及到一些高阶的操作,如自动发帖、自动上传图片等。
2、爬虫扩展
1)可爬取博客列表
1)博客列表分为新闻站,说明各个官方博客的站点有编辑把内容抓取过来,再存入对应的get方法中,进而把数据保存到目标post、head等方法中。
2)博客站点有几百个,虽然定位博客列表爬虫容易,
jquery抓取网页内容到json格式是javascript与dom结合的技术
网站优化 • 优采云 发表了文章 • 0 个评论 • 80 次浏览 • 2022-05-27 03:01
jquery抓取网页内容到json格式是javascript与dom结合的技术,如果您还没有了解过dom,请参考:[原创]firefox+jquery+nodejs做网页抓取实战[原创]jquery抓取网页内容用javascript解析我自己用less格式的html文件[原创]如何将请求url转化为nodejs可以解析的array格式{{"name":"crawler","version":1,"selector":"","callback":function(res,req){letname=req。
tostring(req。uri)。split("")[2]。replace(/\。(||)$/,"")。split("")。replace(/\。(||)$/,"")//。returnname}}分页文件可以这样格式:{{"name":"crawler","version":1,"selector":"","callback":function(res,req){letname=req。
tostring(req。uri)。split("")[2]。replace(/\。(||)$/,"")//。returnname}}这样做到目前为止,效果是是这样的:页面分页支持(假设page[1,numof]===0):{{"name":"crawler","version":1,"selector":"/","callback":function(res,req){letname=req。
tostring(req。uri)。split("")[2]。replace(/\。(||)$/,"")//。returnname}}每次发生请求或者http连接超时等情况导致page[1,numof]===0:{{"name":"crawler","version":1,"selector":"/","callback":function(res,req){letname=req。
tostring(req。uri)。split("")[2]。replace(/\。(||)$/,"")//。returnname}}。 查看全部
jquery抓取网页内容到json格式是javascript与dom结合的技术
jquery抓取网页内容到json格式是javascript与dom结合的技术,如果您还没有了解过dom,请参考:[原创]firefox+jquery+nodejs做网页抓取实战[原创]jquery抓取网页内容用javascript解析我自己用less格式的html文件[原创]如何将请求url转化为nodejs可以解析的array格式{{"name":"crawler","version":1,"selector":"","callback":function(res,req){letname=req。
tostring(req。uri)。split("")[2]。replace(/\。(||)$/,"")。split("")。replace(/\。(||)$/,"")//。returnname}}分页文件可以这样格式:{{"name":"crawler","version":1,"selector":"","callback":function(res,req){letname=req。
tostring(req。uri)。split("")[2]。replace(/\。(||)$/,"")//。returnname}}这样做到目前为止,效果是是这样的:页面分页支持(假设page[1,numof]===0):{{"name":"crawler","version":1,"selector":"/","callback":function(res,req){letname=req。
tostring(req。uri)。split("")[2]。replace(/\。(||)$/,"")//。returnname}}每次发生请求或者http连接超时等情况导致page[1,numof]===0:{{"name":"crawler","version":1,"selector":"/","callback":function(res,req){letname=req。
tostring(req。uri)。split("")[2]。replace(/\。(||)$/,"")//。returnname}}。
跟jQuery那样简单方便操作Html文档的Java工具类,今天我必须要告知你!
网站优化 • 优采云 发表了文章 • 0 个评论 • 78 次浏览 • 2022-05-21 18:18
神器介绍
今天我要介绍一款操作Html文档非常好用的Java插件,强烈安利!因为实在太好用了!
“
Jsoup 是一款纯Java实现,可以非常方便读取和操作Html文档的一款插件。她的API跟jQuery非常相似。我都甚至怀疑创造者是否是jQuery的忠实粉丝。
”读完本文,你能做哪些“坏事”?我选择她的原因
其解析器能够尽最大可能从你提供的HTML文档来创建一个干净完整的解析结果,不管你提供的HTML的格式是否完整。这样就可以完美解决一些不规范的html文档读取的时候,容易报错,太影响开发进度了,并且,影响了程序的健壮性。动不动就报Html格式不规范的错误,想想都怕了。
例如:
代码实操加载html文档
大家可以随便拿个网页来操作一下,我就随手了我自己微信公众号的一篇原创文章作为我的例子去讲解了。为了简化讲解流程,本文就不讲爬虫技术这部分内容了,跳过使用爬虫技术获取网页。
本文简单一点,直接人手保存一个网页Html代替爬虫自动抓取网页动作了。
把html文件加载到Jsoup中
FileReader fileReader = new FileReader("D:\\temp\\demo\\wxPage.html");<br /> String result = fileReader.readString();<br /> Document doc = Jsoup.parse(result);//其实,就一行代码<br />
获取header里面的 meta 标签 <p>Elements metaList = doc.getElementsByTag("meta");<br /> <br /> int metaListSize = metaList.size();<br /> System.out.println("metaList size:"+metaListSize);<br /> for(int i =0 ;i 查看全部
跟jQuery那样简单方便操作Html文档的Java工具类,今天我必须要告知你!
神器介绍
今天我要介绍一款操作Html文档非常好用的Java插件,强烈安利!因为实在太好用了!
“
Jsoup 是一款纯Java实现,可以非常方便读取和操作Html文档的一款插件。她的API跟jQuery非常相似。我都甚至怀疑创造者是否是jQuery的忠实粉丝。
”读完本文,你能做哪些“坏事”?我选择她的原因
其解析器能够尽最大可能从你提供的HTML文档来创建一个干净完整的解析结果,不管你提供的HTML的格式是否完整。这样就可以完美解决一些不规范的html文档读取的时候,容易报错,太影响开发进度了,并且,影响了程序的健壮性。动不动就报Html格式不规范的错误,想想都怕了。
例如:
代码实操加载html文档
大家可以随便拿个网页来操作一下,我就随手了我自己微信公众号的一篇原创文章作为我的例子去讲解了。为了简化讲解流程,本文就不讲爬虫技术这部分内容了,跳过使用爬虫技术获取网页。
本文简单一点,直接人手保存一个网页Html代替爬虫自动抓取网页动作了。
把html文件加载到Jsoup中
FileReader fileReader = new FileReader("D:\\temp\\demo\\wxPage.html");<br /> String result = fileReader.readString();<br /> Document doc = Jsoup.parse(result);//其实,就一行代码<br />
获取header里面的 meta 标签 <p>Elements metaList = doc.getElementsByTag("meta");<br /> <br /> int metaListSize = metaList.size();<br /> System.out.println("metaList size:"+metaListSize);<br /> for(int i =0 ;i
【第1259期】Nerv实战 - 京东首页改版小结
网站优化 • 优采云 发表了文章 • 0 个评论 • 107 次浏览 • 2022-05-15 14:50
前言
项目代码还是要时不时的拿出来重构下,不然面对不定的需求最后那代码可想而知。今日早读文章来自凹凸实验室@Littly授权分享。
正文从这开始~
回想17年的京东首页改版,从上线到现在竟然已经过去了四个多月。这四个多月,除了不曾中断的日常维护需求,对首页孜孜不倦的优化工作,更多的是那些与拖延症抗争的日夜:是今天写,还是等好好休憩回味后再动手?很明显,在这几十上百个日夜里,我基本都选择了第三个选项:不折腾了,先休息吧。现在想起来,关于那一个月白加黑五加二加班生活的印象已经渐渐模糊。到现在依然能清晰记着的,大概是最为深刻的记忆了。
16版的京东首页,在性能、体验、灾备策略等各方面都做到了极致。站在如此高大的巨人肩上,除了满满的自信,我们心里更怕扑街。毫无疑问,我们在接到改版需求的那一刻,立马就敲定了新首页的技术选型:妥妥的jQuery+SeaJS!
但很快,我们就发现这样一点都不酷。jQuery是2006年的框架了,SeaJS停止维护也已经四年。这些项目的诞生都是为了解决当时业界的一些痛点:比如jQuery最开始是为了方便程序员在页面中操作DOM,绑定事件等;SeaJS则是为了在浏览器中实现CMD规范的模块开发和加载。但在各种VirtualDOM框架横飞的现在,程序员已经很少会直接操作DOM元素,而模块的开发和加载也有早已有了别的方案。
就在这时,Nerv项目的作者提出了建议:“不然用Nerv来一发?”我记得,当时他脸上洋溢着淳朴的笑,Nerv也仅仅是部门内部的一个小项目。我们回想了首页这个业务,技术栈已经好几年未曾更新过,开发流程也不够理想。如果再不做出改变,明年的这个时候我们依然会面对一堆陈年老代码头疼不已。抱着试一试的心态,我们接受了他的提议。没想到,这个决定让首页从此摆脱了落后的技术架构,而Nerv现在也已经成长为GitHub上3k+ Star的热门项目。
Q: 为什么不使用React/Preact/Vue?
A: 这三者都是前端圈子中相当流行的项目。React有完善的体系和浓厚的社区氛围,Preact有着羞涩的体积,Vue则使用了先进的html模板和数据绑定机制。但是,上边这三者都 无法兼容IE8。我们在经过相关数据的论证后,发现IE8的用户还是有一定的价值,这才最终激发了我们团队内部自己造轮子的想法。当然,在造轮子的过程中,我们也不忘向上面这些优秀框架的看齐。最终,Nerv在完美兼容React语法的同时,具有着出众的性能表现,在Gzip后也只占用9Kb的体积。
整体架构
在这次的项目中,我们基于上一年久经考验的前端体系,进行了升级:
整体架构图开发模式Athena2.0
1.0版本的Athena,基于vinyl-fs的流操作,或者说是类似于gulp的压缩、编译等等操作的任务流。而到了2017年,webpack早已在前端圈中流行。同行们也早已经习惯在项目中直接基于最新的语言特性去开发,在webpack.config.js加上一个babel-loader就可以完美支持新语法并完成打包。Athena 1.0背着太沉重的历史包袱,已经很难快速实现对babel转译的支持。所以在首页的开发前,我们将Athena升级到了全新的2.0版本。
一如既往,Athena会为项目提供init(初始化),serve(实时预览),build(编译),publish(发布)等功能。除此之外,由于2.0版本的Athena是基于webpack的,所以项目中可以 统一用npm来管理依赖,也可以直接使用最新的ES语言特性来进行开发。
使用Athena2.0开发时,建议的文件架构如下:
文件架构前后端协作
我们依然是采用了前后端分离的协作模式,由后端给出json格式的数据,前端拉取json数据进行渲染。对于大部分的组件来说,都会在constructor中做好组件的初始化工作,在componentDidMount的生命周期中拉取数据写入组件的state,再通过render函数进行渲染。
/* myComponent.js */<br style="box-sizing: border-box;" />import Nerv from 'nervjs'<br style="box-sizing: border-box;" />class MyComponent extends Nerv.Component {<br style="box-sizing: border-box;" /> constructor() {<br style="box-sizing: border-box;" /> super(...arguments)<br style="box-sizing: border-box;" /> this.state = {<br style="box-sizing: border-box;" /> xxx: 'xxx'<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> async requestData() {<br style="box-sizing: border-box;" /> // return await fetch('xxx').then(res => res.json())<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> componentDidMount() {<br style="box-sizing: border-box;" /> this.requestData()<br style="box-sizing: border-box;" /> .then(data => {<br style="box-sizing: border-box;" /> this.setState({<br style="box-sizing: border-box;" /> xxx: 'yyy'<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" /> }).catch(() => {<br style="box-sizing: border-box;" /> this.setState({<br style="box-sizing: border-box;" /> xxx: 'zzz'<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> render() {<br style="box-sizing: border-box;" /> return (<br style="box-sizing: border-box;" /> {this.state.xxx}<br style="box-sizing: border-box;" /> )<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" />}<br style="box-sizing: border-box;" />export default MyComponent
代码规范约束
有一千个读者,就会有一千个哈姆雷特。
上面这句名言,深刻地体现在了16版首页的代码仓库中。同一个组件,如果是基于jQuery+SeaJS的模式,一千个程序猿就会有一千种写法。结果在同一个项目中,代码风格不尽相同,代码质量良莠不齐,多人协作也会无从下手。
何以解忧?唯有统一代码风格了。通过ESLint+Husky,我们对每次代码提交都做了代码风格检查,对是否使用prefer const的变量声明、代码缩进该使用Tab还是空格等等的规则都做了约束。一开始定下规范的时候,团队成员或多或少都会有些不习惯。但通过偷偷在代码里下毒Athena的生成的项目模板中添加对应的规则,潜移默化地,团队成员们也都开始接受、习惯这些约束。
Git提交流程
禁用变量重声明等规则,在一定程度上保证了代码质量;而统一的代码样式风格,则使得项目的多人协作更加便利。
Q: 保证代码质量,促进多人协作的终极好处是什么?
A: 由于项目代码风格统一,通俗易懂容易上手,我们首页的开发团队终于开始妹纸加入了!一群雄性程序猿敲代码能敲出什么火花啊…
对性能优化的探索首屏直出
直出可能是加快首屏加载最行之有效的办法了。它在减少页面加载时间、首屏请求数等方面的好处自然不必再提,结合jQuery,也可以很方便地在直出的DOM上进行更多的操作。
Nerv框架对于它内部的组件、DOM有着良好的操作性,但是对于体系外的DOM节点,却是天生的操作无力。举个例子,比如在页面文件中我们直出一个轮播图:
<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> </a><br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> </a><br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> </a><br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> </a><br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" />
使用Nerv为这段HTML添加轮播逻辑,成为了非常艰难的操作。终极的解决方案,应该是使用 SSR(Server Side Render)的方案,搭建Nerv-server中间层来将组件直出。但现在革命尚未成功,首屏直出尚且依赖后端的研发同学,首页上线又迫在眉睫。被逼急的我们最终选择了比较trick的方式来过渡这个问题:在组件初始化的时候先通过DOM操作获取渲染所需的数据,再将DOM替换成Nerv渲染后的内容。
import Slider from 'path/to/slider'<br style="box-sizing: border-box;" />class Example extends Nerv.Component {<br style="box-sizing: border-box;" /> constructor() {<br style="box-sizing: border-box;" /> const ctn = this.props.ctn<br style="box-sizing: border-box;" /> this.state = {<br style="box-sizing: border-box;" /> data: this.gatherInfos()<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> ctn.innerHTML = ''<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> gatherInfos() {<br style="box-sizing: border-box;" /> // 返回已有DOM中的链接,图片url等信息<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> render() {<br style="box-sizing: border-box;" /> return (<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> {this.data.map(v => )}<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> )<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" />}<br style="box-sizing: border-box;" />const el = document.querySelector('#example')<br style="box-sizing: border-box;" />Nerv.render(, el)
代码分割
在生产环境中,随着代码体积增大,浏览器解压Gzip、执行等操作也会需要更多的开销。在SeaJS的时代,我们尚且会通过SeaJS.use或者require.async异步加载模块代码,避免一次性加载过多内容。但webpack的默认行为却会将整个页面的代码打包为一个单独的文件,这明显不是最佳的实践。对此,webpack给出的解决方案是动态引入(Dynamic Imports)。我们可以通过如下的代码来使用这个便利的特性:
import(<br style="box-sizing: border-box;" /> /* webpackChunkName: ${chunkName} */<br style="box-sizing: border-box;" /> '${chunkName}'<br style="box-sizing: border-box;" />).then((loaded) => {<br style="box-sizing: border-box;" /> /* Do anything to the loaded module */<br style="box-sizing: border-box;" />})
与此同时,webpack会将使用了动态引入的组件从主bundle文件中抽离出来,这就减小了主bundle文件的体积。
构建后的代码
对于我们的具体需求而言,需要做动态引入的一般是Nerv的组件。对于组件的动态引入,业界已经有非常好的实现方案react-loadable。举个栗子,通过下面的代码,我们可以在页面中使用来实现对组件MyComponent的动态引入,并且具有加载超时、错误、加载中等不同状态的展示:
/* loadableMyComponent.js */<br style="box-sizing: border-box;" />import Loadable from 'react-loadable';<br style="box-sizing: border-box;" />const LoadableMyComponent = Loadable({<br style="box-sizing: border-box;" /> loader: () => import('MyComponent'),<br style="box-sizing: border-box;" /> delay: 300,<br style="box-sizing: border-box;" /> loading: (props) => {<br style="box-sizing: border-box;" /> if (props.error) {<br style="box-sizing: border-box;" /> // 加载错误<br style="box-sizing: border-box;" /> return Error!;<br style="box-sizing: border-box;" /> } else if (props.timedOut) {<br style="box-sizing: border-box;" /> // 加载超时<br style="box-sizing: border-box;" /> return TimedOut!; <br style="box-sizing: border-box;" /> } else if (props.pastDelay) {<br style="box-sizing: border-box;" /> // 加载占位符<br style="box-sizing: border-box;" /> return Loading...;<br style="box-sizing: border-box;" /> } else {<br style="box-sizing: border-box;" /> return null;<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> },<br style="box-sizing: border-box;" /> render: (loaded, props) => {<br style="box-sizing: border-box;" /> return ;<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" />});<br style="box-sizing: border-box;" />export default LoadableMyComponent
再进一步,我们希望对于屏幕外的组件,仅仅是在它进入用户视野后再开始加载,这也就是我们常说的滚动懒加载。这可以结合业界已有的懒加载组件react-lazyload来实现。针对上面的,在下面的例子中,只有进入用户屏幕后,MyComponent才会开始加载:
/* myApp.js */<br style="box-sizing: border-box;" />import Nerv from 'nervjs'<br style="box-sizing: border-box;" />import LazyLoad from 'react-lazyload';<br style="box-sizing: border-box;" />import LoadableMyComponent from './loadableMyComponent';<br style="box-sizing: border-box;" />class MyApp extends Nerv.Component {<br style="box-sizing: border-box;" /> render () {<br style="box-sizing: border-box;" /> return (<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> )<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" />}<br style="box-sizing: border-box;" />export default MyApp
上面的例子为lazyload的组件设置了200px的占位高度。并且设定了占位元素的类名,方便设定样式。
代码延后加载
在给首页全面升级技术栈的时候,我们忽略了一个问题:页面上还引用着少量来自兄弟团队的SeaJS模块,我们升级了技术栈是可以,但是强迫兄弟团队也一起去掉SeaJS重构一遍代码,这就有点不合理了。我们也不能仅仅为了这部分模块,就把SeaJS给打包进代码里面,这也是不科学的。
上面讲到的 动态引入功能,帮我们很好地解决了这个问题。我们在代码中单独抽离了一个legacy模块,其中包含了SeaJS、SeaJS-combo等老模块并做了导出。这部分代码在首屏中并不直接引入,而是在需要执行的时候,通过上面的动态引入功能,单独请求下来使用:
import('../legacy')<br style="box-sizing: border-box;" /> .then(({ SeaJS }) => {<br style="box-sizing: border-box;" /> SeaJS.use('xxx', function (XXX) {<br style="box-sizing: border-box;" /> // XXX.init();<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" /> })
打包性能优化
webpack默认会对整个项目引用到的文件进行编译、打包,其中还包括了Nervjs、es5-polyfill等基础的依赖库。这些文件从加入项目开始,基本都不会再有任何更改;然而在每次构建新版本时,webpack打包的这些基础库都会与上一版本有一些细微的区别,这会导致用户浏览器中对应的代码缓存失效。为此,我们考虑将这些基础库分开打包。
针对这种需求,webpack官方建议使用DLL插件来优化。DLL是Dynamic Link Library的简称,是windows系统中对于应用程序依赖的函数库的称呼。对于webpack,我们需要使用一个单独的webpack配置去生成DLL:
/* webpack.dll.config.js */<br style="box-sizing: border-box;" />plugins: [<br style="box-sizing: border-box;" /> new webpack.DllPlugin({<br style="box-sizing: border-box;" /> path: path.join(__dirname, 'dist', 'lib-manifest.json'),<br style="box-sizing: border-box;" /> name: 'lib.dll.js'<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" />]
接下来,在我们的项目的webpack配置中引用DllReferencdPlugin,传入上面生成的json文件:
/* webpack.dev.config.js */<br style="box-sizing: border-box;" />plugins: [<br style="box-sizing: border-box;" /> new webpack.DllReferencePlugin({<br style="box-sizing: border-box;" /> context: __dirname,<br style="box-sizing: border-box;" /> manifest: require('./dist/lib-manifest.json')<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" />]
这样就完成了动态链接库的生成和引用。除了最开始的一次编译,后续开发中如果基础库没有变动,DLL就再也不需要重新编译,这也就解决了上面的代码变动的问题。
体验优化探索兼容IE8
兼容旧版本IE浏览器一直是前端开发人员心中永远的痛。过去,我们使用jQuery去统一不同浏览器的DOM操作和绑定事件,通过jQuery元素实例的map、each等类数组函数批量做JavaScript动画,等等。
但是在使用Nerv之后,从体系外直接操作DOM就显得很不优雅;更推荐的写法,是通过组件的ref属性来访问原生DOM。而map、each等函数,IE9+的浏览器也已经在Array.prototype下有了相应的实现。如果我们在代码中直接引入jQuery,这肯定是不科学的,这将使页面的脚本体积提高许多,同时还引入了很多我们根本用不上的多余功能。
面对这种情况,我们做了一个仅针对ie8的轻量级的兼容库es5-polyfill。它包括这些实现:Object的扩展函数、ES5对Array.prototype的扩充、标准的addEventListener和removeEventListener等。在入口文件顶部使用require('es5-polyfill');引入es5-polyfill后,只需3分钟,你就会甘我一样,爱上这款框架可以在代码中愉快地使用上面说到的那些IE8不支持的API了。
但是,通过上面的CMD方式引入不就意味着对于IE9+的用户都引入了这些代码吗?这并不符合我们“随用随取,避免浪费”的原则。我们更推荐的做法,是在webpack中为配置多个entry,再使用HTMLWebpackPlugin在HTML模板中为es5-polyfill输出一段针对IE8的条件注释。具体实现可以参考nerv-webpack-boilerplate。
SVG Sprite
在页面中使用SVG,可以有效提升小图标在高清屏中的体验。类似于图片Sprite,SVG也可以通过Sprite来减少页面的请求
举个栗子,我们在Nerv中声明svgSprite组件,用以存放页面中用到的svg小图标:
/* svgSprite.js */<br style="box-sizing: border-box;" />const svgSprite = () => {<br style="box-sizing: border-box;" /> return (<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> {/* 更多的 */}<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> )<br style="box-sizing: border-box;" />}<br style="box-sizing: border-box;" />export default svgSprite
接下来,我们可以在页面中动态引入上面的svgSprite组件就可以了:
/* app.js */<br style="box-sizing: border-box;" />import Loadable from 'loadable'<br style="box-sizing: border-box;" />const LoadableSvgSprite = Loadable({<br style="box-sizing: border-box;" /> loader: () => import('./svgSprite'),<br style="box-sizing: border-box;" /> delay: 300,<br style="box-sizing: border-box;" /> render: (loaded, props) => {<br style="box-sizing: border-box;" /> return ;<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" />});
在页面中挂载后,我们即可使用形如的代码去引用相应的图标了。
数据大屏
除了用户,我们同样也关注运营人员的体验。如果可以将运营数据都以直观的图表展示,这对于运营同学、产品同学都是十分幸福的事。这次的首页改版,我们与数据方合作,为首页配套开发了数据大屏项目SEE,用于运营数据的实时滚动展示。
SEE基于Nerv+Redux开发,使用ECharts进行数据的可视化展示。除了线上数据,SEE还有专门针对开发人员需求的性能版大屏,实时展示开发人员关心的页面onload时间,接口成功率,js报错数等指标。我们也希望未来SEE可以在更多的业务中用起来。
页面可用性保障和监控
我们做了许多优化工作来提升页面在性能、体验上的优良表现。但如果页面出现了JS逻辑错误,或者展示有问题,前面的优化工作就都前功尽弃了。所以在保证项目进度的基础上,我们又做了一系列的工作来保证首页的安全与稳定。
统一上线
同一份代码,经过不同版本的开发工具进行编译、压缩,生成的文件可能会天差地别,这种情况在多人协作中是相当致命的。比如:开发人员A的代码使用了新版本开发工具的API,而无辜的开发人员B对此毫不知情,使用了老版本的开发工具进行编译和发布…说多了都是泪,又是一场人间悲剧。
为了消除差异,我们希望不同开发人员的开发环境保持严格统一,但这其实是难以保证的:除了开发工具版本不同,有时候windows下,macOS下甚至是Linux下的表现也是不一样的。
为了解决这个问题,我们将编译的工作挪到了服务器端。开发人员在本地进行开发、自测,联调通过后提交到代码仓库中,确认上线后,上线平台拉取项目的代码,使用服务器端的工具链进行编译、压缩、发布等工作。
此外,上线平台还提供上线代码diff功能,可以将待上线的文件与线上的版本进行diff,待开发人员确认完才能继续上线操作。
接入上线平台后,开发人员再也不必担心开发环境的差异影响了编译结果,也不会误操作将其他同事开发中的分支带上线。就算是出现了线上bug,开发人员也可以轻松地通过上线平台记录的git commitId进行 精确快速的回滚,有效保障了页面的可用性。
自动化测试
我们注意到,每次上线迭代,在经过编译工具的压缩、组合后,都有可能会对代码中其他部分的代码造成影响。如果在测试时只验证了当前迭代的功能点,疏漏了原先其他功能点的验证,就有可能引起一些意想不到的BUG。传统的DOM元素监控并无法满足我们的需求,因为有的bug出现的时机是在一连串特定的操作后。所以我们认为,我们造轮子的时候又到了。我们需要在Athena监控体系中增加一套针对页面中各个功能点的自动进行验证测试的系统。
这个系统基于selenium webdriver搭建。在后台中,开发者针对CSS选择器配置一系列的动作链Actionchain,包括点击、hover、输入文字、拖拽等操作,再通过对指定CSS选择器的HTML属性、样式、值等因素指定一个预期结果。后台服务会定时打开页面,执行预设的操作,如果与预期结果有出入,就会触发告警。
这种单服务器运行的e2e测试,容易碰到一些偶然网络波动的影响而导致乱告警。事实上,我们刚开始跑这套服务的时候,我们经常收到告警,但事实上页面展示并没有任何问题。针对这种偶发情况,我们在验证的过程中加入了失败重试的机制。只有在连续3次测试状态都为fail的情况,才会触发告警。经过优化后,监控的准确性有了质的提升。
素材监控
在生产环境中,除了程序BUG,数据运营的一些不规范操作也有可能影响到用户的体验。举例来说,如果页面中的图片体积过于庞大,会导致页面的加载时间变长,用户等待的时间会更久;如果页面中图片尺寸不合规,会导致 图片展示不正常,出现拉伸/压缩等现象,页面就会给人很山寨的感觉了。
针对这些素材异常情况,我们部署了针对性的监控服务。开发者针对特定的CSS选择器配置图片的标准体积以及尺寸。监控服务定时开启headless浏览器抓取页面中的图片并判断是否符合规则,不符合就触发告警。通过这样的手段,我们可以第一时间知道页面上出现的超限素材,通知运营的同学修改。
实时告警
作为Athena前端体系的一环,我们接入了Athena系统用于收集首页各种性能以及用户环境相关的数据。在这套系统中,我们可以获取到用户的屏幕分辨率占比,浏览器占比,同时还有 页面加载时间、接口成功率等性能数据。
基于这些数据,我们在发现问题时可以进行有针对性的优化,比如调整特定接口的等待时间,或者是调整特定请求的重试策略。但是,并没有谁会一整天对这些数据的仪表盘盯着看;等我们发现问题,可能已经是上线后第二天,在工位上吃着早餐喝着牛奶的时候了。
解决信息滞后的问题,加强消息触达是关键。我们强化了Athena监控平台的功能:除了平台上仪表盘直观的数据展示,还支持配置告警规则。在绑定告警接收人的微信号后,平台就可以通过部门公众号实时推送告警信息,真正做到24小时监控,360度无盲点触达。
更长远的探索
“如何做得更好?”这是一个永不过时的问题。以前我们觉得在页面使用CMD的模块加载体系非常酷,所以后来会在项目中使用SeaJS;去年我们渴求一次架构升级,所以我们今年用上了Nerv。今年我们又会渴望什么呢?
前后端同构
作为提升性能的一个捷径,代码同构、服务器端渲染是目前看来的终极解决方案。我们计划搭建中间层,同样使用Nerv来渲染,从而减少首屏的代码逻辑,这将对页面的加载速度有大幅度的优化。
引入强类型校验
除了更快,我们也希望能够更稳。作为弱类型语言,JavaScript有着强大的灵活性,数据类型的相互转换十分便利;但也由于各种不严谨的类型转换,代码中存在着大量不可预测的分支走向,容易出现undefined的报错,调用不存在的API等等。
强类型语言TypeScript从13年诞生到现在,已经十分成熟了。在类型推断、静态验证上,TypeScript明显会更胜一筹;而在减少了多余的类型转换之后,TypeScript的性能表现也比常规JavaScript更强。我们希望未来可以将TypeScript在项目中用起来,这对于提升页面可靠性和性能,是很有意义的。
总结
这篇文章从整体开发架构与模式,性能、体验优化的探索,页面可用性的保障等方面对京东首页的开发过程做了简单的介绍。之所以说简单,是因为短短的篇幅完全无法说完我们在开发期间的故事和感悟:许多问题的解决并不像上面讲的那样水到渠成;除此之外更是有一大堆深夜加班撸串的故事没有地方讲。
最后,为你推荐
关于本文 查看全部
【第1259期】Nerv实战 - 京东首页改版小结
前言
项目代码还是要时不时的拿出来重构下,不然面对不定的需求最后那代码可想而知。今日早读文章来自凹凸实验室@Littly授权分享。
正文从这开始~
回想17年的京东首页改版,从上线到现在竟然已经过去了四个多月。这四个多月,除了不曾中断的日常维护需求,对首页孜孜不倦的优化工作,更多的是那些与拖延症抗争的日夜:是今天写,还是等好好休憩回味后再动手?很明显,在这几十上百个日夜里,我基本都选择了第三个选项:不折腾了,先休息吧。现在想起来,关于那一个月白加黑五加二加班生活的印象已经渐渐模糊。到现在依然能清晰记着的,大概是最为深刻的记忆了。
16版的京东首页,在性能、体验、灾备策略等各方面都做到了极致。站在如此高大的巨人肩上,除了满满的自信,我们心里更怕扑街。毫无疑问,我们在接到改版需求的那一刻,立马就敲定了新首页的技术选型:妥妥的jQuery+SeaJS!
但很快,我们就发现这样一点都不酷。jQuery是2006年的框架了,SeaJS停止维护也已经四年。这些项目的诞生都是为了解决当时业界的一些痛点:比如jQuery最开始是为了方便程序员在页面中操作DOM,绑定事件等;SeaJS则是为了在浏览器中实现CMD规范的模块开发和加载。但在各种VirtualDOM框架横飞的现在,程序员已经很少会直接操作DOM元素,而模块的开发和加载也有早已有了别的方案。
就在这时,Nerv项目的作者提出了建议:“不然用Nerv来一发?”我记得,当时他脸上洋溢着淳朴的笑,Nerv也仅仅是部门内部的一个小项目。我们回想了首页这个业务,技术栈已经好几年未曾更新过,开发流程也不够理想。如果再不做出改变,明年的这个时候我们依然会面对一堆陈年老代码头疼不已。抱着试一试的心态,我们接受了他的提议。没想到,这个决定让首页从此摆脱了落后的技术架构,而Nerv现在也已经成长为GitHub上3k+ Star的热门项目。
Q: 为什么不使用React/Preact/Vue?
A: 这三者都是前端圈子中相当流行的项目。React有完善的体系和浓厚的社区氛围,Preact有着羞涩的体积,Vue则使用了先进的html模板和数据绑定机制。但是,上边这三者都 无法兼容IE8。我们在经过相关数据的论证后,发现IE8的用户还是有一定的价值,这才最终激发了我们团队内部自己造轮子的想法。当然,在造轮子的过程中,我们也不忘向上面这些优秀框架的看齐。最终,Nerv在完美兼容React语法的同时,具有着出众的性能表现,在Gzip后也只占用9Kb的体积。
整体架构
在这次的项目中,我们基于上一年久经考验的前端体系,进行了升级:
整体架构图开发模式Athena2.0
1.0版本的Athena,基于vinyl-fs的流操作,或者说是类似于gulp的压缩、编译等等操作的任务流。而到了2017年,webpack早已在前端圈中流行。同行们也早已经习惯在项目中直接基于最新的语言特性去开发,在webpack.config.js加上一个babel-loader就可以完美支持新语法并完成打包。Athena 1.0背着太沉重的历史包袱,已经很难快速实现对babel转译的支持。所以在首页的开发前,我们将Athena升级到了全新的2.0版本。
一如既往,Athena会为项目提供init(初始化),serve(实时预览),build(编译),publish(发布)等功能。除此之外,由于2.0版本的Athena是基于webpack的,所以项目中可以 统一用npm来管理依赖,也可以直接使用最新的ES语言特性来进行开发。
使用Athena2.0开发时,建议的文件架构如下:
文件架构前后端协作
我们依然是采用了前后端分离的协作模式,由后端给出json格式的数据,前端拉取json数据进行渲染。对于大部分的组件来说,都会在constructor中做好组件的初始化工作,在componentDidMount的生命周期中拉取数据写入组件的state,再通过render函数进行渲染。
/* myComponent.js */<br style="box-sizing: border-box;" />import Nerv from 'nervjs'<br style="box-sizing: border-box;" />class MyComponent extends Nerv.Component {<br style="box-sizing: border-box;" /> constructor() {<br style="box-sizing: border-box;" /> super(...arguments)<br style="box-sizing: border-box;" /> this.state = {<br style="box-sizing: border-box;" /> xxx: 'xxx'<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> async requestData() {<br style="box-sizing: border-box;" /> // return await fetch('xxx').then(res => res.json())<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> componentDidMount() {<br style="box-sizing: border-box;" /> this.requestData()<br style="box-sizing: border-box;" /> .then(data => {<br style="box-sizing: border-box;" /> this.setState({<br style="box-sizing: border-box;" /> xxx: 'yyy'<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" /> }).catch(() => {<br style="box-sizing: border-box;" /> this.setState({<br style="box-sizing: border-box;" /> xxx: 'zzz'<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> render() {<br style="box-sizing: border-box;" /> return (<br style="box-sizing: border-box;" /> {this.state.xxx}<br style="box-sizing: border-box;" /> )<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" />}<br style="box-sizing: border-box;" />export default MyComponent
代码规范约束
有一千个读者,就会有一千个哈姆雷特。
上面这句名言,深刻地体现在了16版首页的代码仓库中。同一个组件,如果是基于jQuery+SeaJS的模式,一千个程序猿就会有一千种写法。结果在同一个项目中,代码风格不尽相同,代码质量良莠不齐,多人协作也会无从下手。
何以解忧?唯有统一代码风格了。通过ESLint+Husky,我们对每次代码提交都做了代码风格检查,对是否使用prefer const的变量声明、代码缩进该使用Tab还是空格等等的规则都做了约束。一开始定下规范的时候,团队成员或多或少都会有些不习惯。但通过偷偷在代码里下毒Athena的生成的项目模板中添加对应的规则,潜移默化地,团队成员们也都开始接受、习惯这些约束。
Git提交流程
禁用变量重声明等规则,在一定程度上保证了代码质量;而统一的代码样式风格,则使得项目的多人协作更加便利。
Q: 保证代码质量,促进多人协作的终极好处是什么?
A: 由于项目代码风格统一,通俗易懂容易上手,我们首页的开发团队终于开始妹纸加入了!一群雄性程序猿敲代码能敲出什么火花啊…
对性能优化的探索首屏直出
直出可能是加快首屏加载最行之有效的办法了。它在减少页面加载时间、首屏请求数等方面的好处自然不必再提,结合jQuery,也可以很方便地在直出的DOM上进行更多的操作。
Nerv框架对于它内部的组件、DOM有着良好的操作性,但是对于体系外的DOM节点,却是天生的操作无力。举个例子,比如在页面文件中我们直出一个轮播图:
<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> </a><br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> </a><br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> </a><br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> </a><br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" />
使用Nerv为这段HTML添加轮播逻辑,成为了非常艰难的操作。终极的解决方案,应该是使用 SSR(Server Side Render)的方案,搭建Nerv-server中间层来将组件直出。但现在革命尚未成功,首屏直出尚且依赖后端的研发同学,首页上线又迫在眉睫。被逼急的我们最终选择了比较trick的方式来过渡这个问题:在组件初始化的时候先通过DOM操作获取渲染所需的数据,再将DOM替换成Nerv渲染后的内容。
import Slider from 'path/to/slider'<br style="box-sizing: border-box;" />class Example extends Nerv.Component {<br style="box-sizing: border-box;" /> constructor() {<br style="box-sizing: border-box;" /> const ctn = this.props.ctn<br style="box-sizing: border-box;" /> this.state = {<br style="box-sizing: border-box;" /> data: this.gatherInfos()<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> ctn.innerHTML = ''<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> gatherInfos() {<br style="box-sizing: border-box;" /> // 返回已有DOM中的链接,图片url等信息<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> render() {<br style="box-sizing: border-box;" /> return (<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> {this.data.map(v => )}<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> )<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" />}<br style="box-sizing: border-box;" />const el = document.querySelector('#example')<br style="box-sizing: border-box;" />Nerv.render(, el)
代码分割
在生产环境中,随着代码体积增大,浏览器解压Gzip、执行等操作也会需要更多的开销。在SeaJS的时代,我们尚且会通过SeaJS.use或者require.async异步加载模块代码,避免一次性加载过多内容。但webpack的默认行为却会将整个页面的代码打包为一个单独的文件,这明显不是最佳的实践。对此,webpack给出的解决方案是动态引入(Dynamic Imports)。我们可以通过如下的代码来使用这个便利的特性:
import(<br style="box-sizing: border-box;" /> /* webpackChunkName: ${chunkName} */<br style="box-sizing: border-box;" /> '${chunkName}'<br style="box-sizing: border-box;" />).then((loaded) => {<br style="box-sizing: border-box;" /> /* Do anything to the loaded module */<br style="box-sizing: border-box;" />})
与此同时,webpack会将使用了动态引入的组件从主bundle文件中抽离出来,这就减小了主bundle文件的体积。
构建后的代码
对于我们的具体需求而言,需要做动态引入的一般是Nerv的组件。对于组件的动态引入,业界已经有非常好的实现方案react-loadable。举个栗子,通过下面的代码,我们可以在页面中使用来实现对组件MyComponent的动态引入,并且具有加载超时、错误、加载中等不同状态的展示:
/* loadableMyComponent.js */<br style="box-sizing: border-box;" />import Loadable from 'react-loadable';<br style="box-sizing: border-box;" />const LoadableMyComponent = Loadable({<br style="box-sizing: border-box;" /> loader: () => import('MyComponent'),<br style="box-sizing: border-box;" /> delay: 300,<br style="box-sizing: border-box;" /> loading: (props) => {<br style="box-sizing: border-box;" /> if (props.error) {<br style="box-sizing: border-box;" /> // 加载错误<br style="box-sizing: border-box;" /> return Error!;<br style="box-sizing: border-box;" /> } else if (props.timedOut) {<br style="box-sizing: border-box;" /> // 加载超时<br style="box-sizing: border-box;" /> return TimedOut!; <br style="box-sizing: border-box;" /> } else if (props.pastDelay) {<br style="box-sizing: border-box;" /> // 加载占位符<br style="box-sizing: border-box;" /> return Loading...;<br style="box-sizing: border-box;" /> } else {<br style="box-sizing: border-box;" /> return null;<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" /> },<br style="box-sizing: border-box;" /> render: (loaded, props) => {<br style="box-sizing: border-box;" /> return ;<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" />});<br style="box-sizing: border-box;" />export default LoadableMyComponent
再进一步,我们希望对于屏幕外的组件,仅仅是在它进入用户视野后再开始加载,这也就是我们常说的滚动懒加载。这可以结合业界已有的懒加载组件react-lazyload来实现。针对上面的,在下面的例子中,只有进入用户屏幕后,MyComponent才会开始加载:
/* myApp.js */<br style="box-sizing: border-box;" />import Nerv from 'nervjs'<br style="box-sizing: border-box;" />import LazyLoad from 'react-lazyload';<br style="box-sizing: border-box;" />import LoadableMyComponent from './loadableMyComponent';<br style="box-sizing: border-box;" />class MyApp extends Nerv.Component {<br style="box-sizing: border-box;" /> render () {<br style="box-sizing: border-box;" /> return (<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> )<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" />}<br style="box-sizing: border-box;" />export default MyApp
上面的例子为lazyload的组件设置了200px的占位高度。并且设定了占位元素的类名,方便设定样式。
代码延后加载
在给首页全面升级技术栈的时候,我们忽略了一个问题:页面上还引用着少量来自兄弟团队的SeaJS模块,我们升级了技术栈是可以,但是强迫兄弟团队也一起去掉SeaJS重构一遍代码,这就有点不合理了。我们也不能仅仅为了这部分模块,就把SeaJS给打包进代码里面,这也是不科学的。
上面讲到的 动态引入功能,帮我们很好地解决了这个问题。我们在代码中单独抽离了一个legacy模块,其中包含了SeaJS、SeaJS-combo等老模块并做了导出。这部分代码在首屏中并不直接引入,而是在需要执行的时候,通过上面的动态引入功能,单独请求下来使用:
import('../legacy')<br style="box-sizing: border-box;" /> .then(({ SeaJS }) => {<br style="box-sizing: border-box;" /> SeaJS.use('xxx', function (XXX) {<br style="box-sizing: border-box;" /> // XXX.init();<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" /> })
打包性能优化
webpack默认会对整个项目引用到的文件进行编译、打包,其中还包括了Nervjs、es5-polyfill等基础的依赖库。这些文件从加入项目开始,基本都不会再有任何更改;然而在每次构建新版本时,webpack打包的这些基础库都会与上一版本有一些细微的区别,这会导致用户浏览器中对应的代码缓存失效。为此,我们考虑将这些基础库分开打包。
针对这种需求,webpack官方建议使用DLL插件来优化。DLL是Dynamic Link Library的简称,是windows系统中对于应用程序依赖的函数库的称呼。对于webpack,我们需要使用一个单独的webpack配置去生成DLL:
/* webpack.dll.config.js */<br style="box-sizing: border-box;" />plugins: [<br style="box-sizing: border-box;" /> new webpack.DllPlugin({<br style="box-sizing: border-box;" /> path: path.join(__dirname, 'dist', 'lib-manifest.json'),<br style="box-sizing: border-box;" /> name: 'lib.dll.js'<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" />]
接下来,在我们的项目的webpack配置中引用DllReferencdPlugin,传入上面生成的json文件:
/* webpack.dev.config.js */<br style="box-sizing: border-box;" />plugins: [<br style="box-sizing: border-box;" /> new webpack.DllReferencePlugin({<br style="box-sizing: border-box;" /> context: __dirname,<br style="box-sizing: border-box;" /> manifest: require('./dist/lib-manifest.json')<br style="box-sizing: border-box;" /> })<br style="box-sizing: border-box;" />]
这样就完成了动态链接库的生成和引用。除了最开始的一次编译,后续开发中如果基础库没有变动,DLL就再也不需要重新编译,这也就解决了上面的代码变动的问题。
体验优化探索兼容IE8
兼容旧版本IE浏览器一直是前端开发人员心中永远的痛。过去,我们使用jQuery去统一不同浏览器的DOM操作和绑定事件,通过jQuery元素实例的map、each等类数组函数批量做JavaScript动画,等等。
但是在使用Nerv之后,从体系外直接操作DOM就显得很不优雅;更推荐的写法,是通过组件的ref属性来访问原生DOM。而map、each等函数,IE9+的浏览器也已经在Array.prototype下有了相应的实现。如果我们在代码中直接引入jQuery,这肯定是不科学的,这将使页面的脚本体积提高许多,同时还引入了很多我们根本用不上的多余功能。
面对这种情况,我们做了一个仅针对ie8的轻量级的兼容库es5-polyfill。它包括这些实现:Object的扩展函数、ES5对Array.prototype的扩充、标准的addEventListener和removeEventListener等。在入口文件顶部使用require('es5-polyfill');引入es5-polyfill后,只需3分钟,你就会甘我一样,爱上这款框架可以在代码中愉快地使用上面说到的那些IE8不支持的API了。
但是,通过上面的CMD方式引入不就意味着对于IE9+的用户都引入了这些代码吗?这并不符合我们“随用随取,避免浪费”的原则。我们更推荐的做法,是在webpack中为配置多个entry,再使用HTMLWebpackPlugin在HTML模板中为es5-polyfill输出一段针对IE8的条件注释。具体实现可以参考nerv-webpack-boilerplate。
SVG Sprite
在页面中使用SVG,可以有效提升小图标在高清屏中的体验。类似于图片Sprite,SVG也可以通过Sprite来减少页面的请求
举个栗子,我们在Nerv中声明svgSprite组件,用以存放页面中用到的svg小图标:
/* svgSprite.js */<br style="box-sizing: border-box;" />const svgSprite = () => {<br style="box-sizing: border-box;" /> return (<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> {/* 更多的 */}<br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> <br style="box-sizing: border-box;" /> )<br style="box-sizing: border-box;" />}<br style="box-sizing: border-box;" />export default svgSprite
接下来,我们可以在页面中动态引入上面的svgSprite组件就可以了:
/* app.js */<br style="box-sizing: border-box;" />import Loadable from 'loadable'<br style="box-sizing: border-box;" />const LoadableSvgSprite = Loadable({<br style="box-sizing: border-box;" /> loader: () => import('./svgSprite'),<br style="box-sizing: border-box;" /> delay: 300,<br style="box-sizing: border-box;" /> render: (loaded, props) => {<br style="box-sizing: border-box;" /> return ;<br style="box-sizing: border-box;" /> }<br style="box-sizing: border-box;" />});
在页面中挂载后,我们即可使用形如的代码去引用相应的图标了。
数据大屏
除了用户,我们同样也关注运营人员的体验。如果可以将运营数据都以直观的图表展示,这对于运营同学、产品同学都是十分幸福的事。这次的首页改版,我们与数据方合作,为首页配套开发了数据大屏项目SEE,用于运营数据的实时滚动展示。
SEE基于Nerv+Redux开发,使用ECharts进行数据的可视化展示。除了线上数据,SEE还有专门针对开发人员需求的性能版大屏,实时展示开发人员关心的页面onload时间,接口成功率,js报错数等指标。我们也希望未来SEE可以在更多的业务中用起来。
页面可用性保障和监控
我们做了许多优化工作来提升页面在性能、体验上的优良表现。但如果页面出现了JS逻辑错误,或者展示有问题,前面的优化工作就都前功尽弃了。所以在保证项目进度的基础上,我们又做了一系列的工作来保证首页的安全与稳定。
统一上线
同一份代码,经过不同版本的开发工具进行编译、压缩,生成的文件可能会天差地别,这种情况在多人协作中是相当致命的。比如:开发人员A的代码使用了新版本开发工具的API,而无辜的开发人员B对此毫不知情,使用了老版本的开发工具进行编译和发布…说多了都是泪,又是一场人间悲剧。
为了消除差异,我们希望不同开发人员的开发环境保持严格统一,但这其实是难以保证的:除了开发工具版本不同,有时候windows下,macOS下甚至是Linux下的表现也是不一样的。
为了解决这个问题,我们将编译的工作挪到了服务器端。开发人员在本地进行开发、自测,联调通过后提交到代码仓库中,确认上线后,上线平台拉取项目的代码,使用服务器端的工具链进行编译、压缩、发布等工作。
此外,上线平台还提供上线代码diff功能,可以将待上线的文件与线上的版本进行diff,待开发人员确认完才能继续上线操作。
接入上线平台后,开发人员再也不必担心开发环境的差异影响了编译结果,也不会误操作将其他同事开发中的分支带上线。就算是出现了线上bug,开发人员也可以轻松地通过上线平台记录的git commitId进行 精确快速的回滚,有效保障了页面的可用性。
自动化测试
我们注意到,每次上线迭代,在经过编译工具的压缩、组合后,都有可能会对代码中其他部分的代码造成影响。如果在测试时只验证了当前迭代的功能点,疏漏了原先其他功能点的验证,就有可能引起一些意想不到的BUG。传统的DOM元素监控并无法满足我们的需求,因为有的bug出现的时机是在一连串特定的操作后。所以我们认为,我们造轮子的时候又到了。我们需要在Athena监控体系中增加一套针对页面中各个功能点的自动进行验证测试的系统。
这个系统基于selenium webdriver搭建。在后台中,开发者针对CSS选择器配置一系列的动作链Actionchain,包括点击、hover、输入文字、拖拽等操作,再通过对指定CSS选择器的HTML属性、样式、值等因素指定一个预期结果。后台服务会定时打开页面,执行预设的操作,如果与预期结果有出入,就会触发告警。
这种单服务器运行的e2e测试,容易碰到一些偶然网络波动的影响而导致乱告警。事实上,我们刚开始跑这套服务的时候,我们经常收到告警,但事实上页面展示并没有任何问题。针对这种偶发情况,我们在验证的过程中加入了失败重试的机制。只有在连续3次测试状态都为fail的情况,才会触发告警。经过优化后,监控的准确性有了质的提升。
素材监控
在生产环境中,除了程序BUG,数据运营的一些不规范操作也有可能影响到用户的体验。举例来说,如果页面中的图片体积过于庞大,会导致页面的加载时间变长,用户等待的时间会更久;如果页面中图片尺寸不合规,会导致 图片展示不正常,出现拉伸/压缩等现象,页面就会给人很山寨的感觉了。
针对这些素材异常情况,我们部署了针对性的监控服务。开发者针对特定的CSS选择器配置图片的标准体积以及尺寸。监控服务定时开启headless浏览器抓取页面中的图片并判断是否符合规则,不符合就触发告警。通过这样的手段,我们可以第一时间知道页面上出现的超限素材,通知运营的同学修改。
实时告警
作为Athena前端体系的一环,我们接入了Athena系统用于收集首页各种性能以及用户环境相关的数据。在这套系统中,我们可以获取到用户的屏幕分辨率占比,浏览器占比,同时还有 页面加载时间、接口成功率等性能数据。
基于这些数据,我们在发现问题时可以进行有针对性的优化,比如调整特定接口的等待时间,或者是调整特定请求的重试策略。但是,并没有谁会一整天对这些数据的仪表盘盯着看;等我们发现问题,可能已经是上线后第二天,在工位上吃着早餐喝着牛奶的时候了。
解决信息滞后的问题,加强消息触达是关键。我们强化了Athena监控平台的功能:除了平台上仪表盘直观的数据展示,还支持配置告警规则。在绑定告警接收人的微信号后,平台就可以通过部门公众号实时推送告警信息,真正做到24小时监控,360度无盲点触达。
更长远的探索
“如何做得更好?”这是一个永不过时的问题。以前我们觉得在页面使用CMD的模块加载体系非常酷,所以后来会在项目中使用SeaJS;去年我们渴求一次架构升级,所以我们今年用上了Nerv。今年我们又会渴望什么呢?
前后端同构
作为提升性能的一个捷径,代码同构、服务器端渲染是目前看来的终极解决方案。我们计划搭建中间层,同样使用Nerv来渲染,从而减少首屏的代码逻辑,这将对页面的加载速度有大幅度的优化。
引入强类型校验
除了更快,我们也希望能够更稳。作为弱类型语言,JavaScript有着强大的灵活性,数据类型的相互转换十分便利;但也由于各种不严谨的类型转换,代码中存在着大量不可预测的分支走向,容易出现undefined的报错,调用不存在的API等等。
强类型语言TypeScript从13年诞生到现在,已经十分成熟了。在类型推断、静态验证上,TypeScript明显会更胜一筹;而在减少了多余的类型转换之后,TypeScript的性能表现也比常规JavaScript更强。我们希望未来可以将TypeScript在项目中用起来,这对于提升页面可靠性和性能,是很有意义的。
总结
这篇文章从整体开发架构与模式,性能、体验优化的探索,页面可用性的保障等方面对京东首页的开发过程做了简单的介绍。之所以说简单,是因为短短的篇幅完全无法说完我们在开发期间的故事和感悟:许多问题的解决并不像上面讲的那样水到渠成;除此之外更是有一大堆深夜加班撸串的故事没有地方讲。
最后,为你推荐
关于本文
抓包分析、json数据解析(一)|抓包数据分析
网站优化 • 优采云 发表了文章 • 0 个评论 • 94 次浏览 • 2022-05-15 06:00
jquery抓取网页内容之json数据前言先来说说http的文档格式。浏览器保存http页面上的内容,只能保存静态内容,即post或get。而json本质是一个对象格式,其中可以包含任意数据,比如对象对象函数属性等。本文主要通过抓包分析、json数据解析进行讲解。
一、抓包分析百度json包的抓包抓包分析http::8080/json/static/static/web/public/json/blob,抓取代码如下varurl='/'varstatic_json={}static_json.push({json:{'name':'amethyst','learned':1}})static_json.send({'name':'adman','learned':6}),methods:{'get':url,'post':static_json},varflag=false是因为static_json是预处理的json数据。
flag的值可以是
0、1,如果flag=0是获取无值的json,flag=1则获取内部已经赋值了的json。(flag值为1则数据无效)举例:前两张图就可以看到,抓取的结果并不是post或get的格式,而是一个纯文本格式。
二、解析json数据其实解析json数据的方法很多,传统json是利用json+json.length等单个key值来实现。其实可以利用lookup.split来实现数据的去重,其代码如下functionlookup(value){varpresize=length;lookup(value,value);returnfalse;}其实,下面我们抓包看看再说。
三、代码的实现。a.抓包工具vs-xml-cookie这个抓包工具在网页数据抓取上还是挺好用的,下面演示一下这个抓包工具和xmlcookie进行的数据交互。其实这里用到的process.protocol对于我们来说也用不上,因为不是当前浏览器可以抓取的,这里简单演示一下。首先,打开抓包工具:接着,看页面内容,利用浏览器的network里对ajax的跳转,找到本地的method:type:1,即是:json的前缀:0x00。
之后在process.protocol对象对应的属性处加上:82464,即为抓取工具的端口号,就变成本地的ajax了:可以看到,http网站的数据就变成了一个json数据。b.json的格式c.数据的解析实现json的数据解析,通常要先解析json格式的内容,而不是解析xml格式的内容,因为xml数据分析的时候需要考虑单元格或列表的格式,这里我们就选用了解析json的方法,具体详见源码。
其中分解json数据的数据结构,数据转换方法,如object.values,function.apply等等。而xml数据解析的方法,都是简单粗暴的:复制flag=false.slice,然后简单进行替换操作即可。来自已经实现的csv数据分析练习。该代码。 查看全部
抓包分析、json数据解析(一)|抓包数据分析
jquery抓取网页内容之json数据前言先来说说http的文档格式。浏览器保存http页面上的内容,只能保存静态内容,即post或get。而json本质是一个对象格式,其中可以包含任意数据,比如对象对象函数属性等。本文主要通过抓包分析、json数据解析进行讲解。
一、抓包分析百度json包的抓包抓包分析http::8080/json/static/static/web/public/json/blob,抓取代码如下varurl='/'varstatic_json={}static_json.push({json:{'name':'amethyst','learned':1}})static_json.send({'name':'adman','learned':6}),methods:{'get':url,'post':static_json},varflag=false是因为static_json是预处理的json数据。
flag的值可以是
0、1,如果flag=0是获取无值的json,flag=1则获取内部已经赋值了的json。(flag值为1则数据无效)举例:前两张图就可以看到,抓取的结果并不是post或get的格式,而是一个纯文本格式。
二、解析json数据其实解析json数据的方法很多,传统json是利用json+json.length等单个key值来实现。其实可以利用lookup.split来实现数据的去重,其代码如下functionlookup(value){varpresize=length;lookup(value,value);returnfalse;}其实,下面我们抓包看看再说。
三、代码的实现。a.抓包工具vs-xml-cookie这个抓包工具在网页数据抓取上还是挺好用的,下面演示一下这个抓包工具和xmlcookie进行的数据交互。其实这里用到的process.protocol对于我们来说也用不上,因为不是当前浏览器可以抓取的,这里简单演示一下。首先,打开抓包工具:接着,看页面内容,利用浏览器的network里对ajax的跳转,找到本地的method:type:1,即是:json的前缀:0x00。
之后在process.protocol对象对应的属性处加上:82464,即为抓取工具的端口号,就变成本地的ajax了:可以看到,http网站的数据就变成了一个json数据。b.json的格式c.数据的解析实现json的数据解析,通常要先解析json格式的内容,而不是解析xml格式的内容,因为xml数据分析的时候需要考虑单元格或列表的格式,这里我们就选用了解析json的方法,具体详见源码。
其中分解json数据的数据结构,数据转换方法,如object.values,function.apply等等。而xml数据解析的方法,都是简单粗暴的:复制flag=false.slice,然后简单进行替换操作即可。来自已经实现的csv数据分析练习。该代码。