奇怪的知识增加了(一)

最近在搞 Mine 这个插件,涉及好多关于 DOM、CSS相关的内容,踩了不少坑(主要是因为自己才疏学浅)。可能在爬虫相关的开发,前端开发的一些场景,或者 Webpack 给自己写一些 Loader 的时候都会有一些用处吧,记录在这,好多还蛮有趣的。

CSS 解析

项目里需要把 CSS 文本转为 AST,找了好多个 CSS 解析用的库,最后发现 PostCSS 这 个宝藏。

大多数找到的文档都是讲如何用这个来作为 Webpack Loader 自定义处理 CSS 的,官方的文档写的也不是太清楚。最后忘了在哪个犄角旮旯找到一点代码,然后就写出了解析的相关代码:

1
2
3
4
5
6
// yarn add postcss
import * as postcss from 'postcss';

const styleString = 'body { color: red }';
const processor = postcss();
const ast = processor.process(styleString);

至于得到的 AST,参照 postcssd.ts 声明文件查看就可以了,结构很简单也很清晰,或者打印出来在浏览器里查看对象也可以,这里赞一句,TypeScript 真香。

JQuery使用

  • jQuery 的 find() 方法,查找的范围是子元素,所以通过 HTML 字符串构建 JQuery 对象的时候,外层包裹一个 DIV 即可。为什么不直接用 $ 来查找呢?因为我用的是外部的 HTML 字符串…

  • JQuery 的 css() 方法,在设置的时候,比如 css(prop, value),prop 和 value 前后有空格会失效,在处理的时候都加个 trim() 处理一下去掉头尾多余的空格。好像不太会有人遇到这种情况吧,我遇到是因为用 split 解析一个 DOM 对象的内联样式,最后在 token 前后有好些空格。

  • JQuery 对 CSS 选择器支持有限,比如 :hover 这种伪类选择器(逃。对应的 JQuery 只实现了 nth 这个伪类选择器,其他的诸如 :before:after 啥的都不支持。也很好理解为什么不支持= =。不过在直接用CSS选择器进行选择时,要去掉这种类型的选择器,不然会导致 JQuery 抛出异常。至于我为什么会出现使用这种选择器的情况呢?参考上面的 CSS 解析联想一下,😖。

JQuery作为一个老牌战士,好像正在被各种拔地而起的MVVM框架,慢慢淘汰。不过作为一个工具库,它倒是不可能消失殆尽的,就比如我现在在做DOM处理,解析,用JQuery还是很方便的。同类的库在各端也都在爬虫界熠熠生辉,比如 CherrioBeautifulSoupPyQuery之类。

LazyLoad

LazyLoad 是一种前端技术,顾名思义,就是图片懒加载,在页面快滚动到图片的位置的时候由 JS 更改图片的 src 为真实的图片地址,再由浏览器加载,避免一加载页面就加载好多的图片资源。

但是搞爬虫类型的应用碰到这种东西就怪欺负人了,你前端怎么能为了用户体验和自己爽而忽略了做爬虫(哔…)。

只针对了一个网站做了分析,大致思路是判断 src 中不是 URL 的情况下,在元素的 Attribute 里找有是 URL 的属性,然后把 src 替换为这个 URL。JQuery 粗略的实现如下

1
2
3
4
5
$.each(element.attributes, (index, attr) => {
if (urlRegex.test(attr.value)) {
$(element).attr('src', attr.value);
}
})

不过有些坑,是未加载的图片,有的网站会设置为不可见,如果不携带 CSS 的情况下没有问题,不过要是把网站原来的 CSS 挂在上去,会留着这个设置,导致图片不可见。我是暴力把可见改为显示,之后再细研究这个问题。对于 LazyLoad 的图片提取算法也是有待优化的,具体之后按照这个思路处理下。

Readability

这是 Mozilla 提供的一个开源的提取网页内容的库。文档说是给 Firefox Reader View 用的,简单概括就是将复杂的网页转为阅读友好的内容,或者说是从网页中提取文章正文。

用了下效果还不错。具体的使用或者研究之后有时间搞一搞,毕竟一个文件几千行代码= =。

JavaScript

由于 Readability 的构造函数需要传 HTMLDocument 对象,而我只有 HTML 字符串。而且直接用 document 对象会对网页文档造成不可逆的改变。差来查去,发现 DOM 中有个类叫 DOMParser 可以得到一个解析器,通过这个解析器可以从 HTML 字符串生成一个 HTMLDocument 对象:

1
2
const parser = new DOMParser(),
doc = parser.parseFromString(HTML, "text/html");

以前不知道还有这些东西,看来是得找机会补一补前端的基础知识了…

看来 Mine 里有些以前因为自己的知识盲区写出来的代码要重构了(就是上面说的外层用 DIV 包裹的事情)。