d3-interpolate 是 D3 的核心模块之一,与比例尺有些类似,interpolate (插值)所做的也是一些数值映射的工作。区别是,interpolate 的定义域始终是 0 ~ 1,并且始终为线性的。所以,更多时候它用来与 D3 的一些其他模组集成使用(如 transition, scale 等)。

阅读全文 »

之前做的柱状图例子:

let data = [250, 210, 170, 100, 190]

let rectWidth = 25

svg.selectAll('rect')
.data(data)
.enter()
.append('rect')
.attr('y', (d, i) => height - d)
.attr('x', (d, i) => i * rectWidth)
.attr('height', d => d)
.attr('width', rectWidth - 2)
.attr('fill', 'steelblue')

有一个严重的问题,就是没有比例尺的概念,柱状图的高度完全由数据转换成像素值来模拟。这明显是不科学的:如果数据的值过小或过大,作出来的图就会很奇怪,同时也无法做到非线性的映射。

就跟地图需要比例尺一样,绝大多数的数据图表也需要比例尺。

Scales are a convenient abstraction for a fundamental task in visualization: mapping a dimension of abstract data to a visual representation.

比例尺 - Scale - “将某个维度的抽象数据做可视化映射”

至于可视化映射的具体实现,d3-scale 模块提供了许多方案,大致可以分为两类:

  • Continuous Scales(连续映射)
  • Ordinal Scales(散点映射)
阅读全文 »

博客再次迁移,这次是从 Wordpress 转向静态博客(自建)。

技术栈:

  • 前端:vue + vue-router + vuex + bootstrap + webpack
  • 服务端:没有
  • 数据库:没有

整站打包后,一次加载所有资源(HTML + CSS + JS + DATA)300K 不到(gzip 后 80K+),秒速渲染,与先前真的是天差地别。

图片资源从本地服务器搬迁到免费云。 写作使用 Markdown,从此 IDE 写博客不是梦。

代码地址:https://github.com/wxsms/wxsms.github.io/tree/src

在 D3 的使用过程中,我们见得最多的应当是类似如下的代码:

let div = d3.select('body')
.selectAll('p')
.data([3, 6, 9, 12, 15])
.enter()
.append('p')
.text(d => d);

将得到:

<body>
<p>3</p>
<p>6</p>
<p>9</p>
<p>12</p>
<p>15</p>
</body>

光看代码完全不能理解 D3 到底做了些什么,其实这里关键是 enter 的使用。

阅读全文 »

  1. goto ‘File | Settings | Appearance & Behavior | System Settings’;
  2. uncheck ‘Use save write’ option

Problem solved.

HTTP 是一种无状态协议,服务器与客户端之间储存状态信息主要靠 Session,但是,Session 在浏览器关闭后就会失效,再次开启先前所储存的状态都会丢失,因此还需要借助 Cookie

一般来说,网络爬虫不是浏览器,因此,只能靠手动记住 Cookie 来与服务器“保持联系”。

Cookie 是 HTTP 协议的一部分,处理流程为:

  • 服务器向客户端发送 cookie
    • 通常使用 HTTP 协议规定的 set-cookie 头操作
    • 规范规定 cookie 的格式为 name = value 格式,且必须包含这部分
  • 浏览器将 cookie 保存
  • 每次请求浏览器都会将 cookie 发向服务器

因此,爬虫要做的工作就是模拟浏览器,识别服务端发来的 Cookie 并保存,之后每次请求都带上 Cookie 头。

在 Node.js 中有很多与 Cookie 处理相关的 package,就不再赘述。

Session

Cookie 虽然方便,但是由于保存在客户端,可保存的长度有限,且可以被伪造。因此,为了解决这些问题,就有了 Session

区别:

  • Cookie 保存在客户端
  • Session 保存在服务端

Cookie 与 Session 储存的都是客户端与服务器之间的会话状态信息,它们之间主要靠一个秘钥来进行匹配,称之为 SESSION_ID ,如 express 中默认为 connect.sid 字段。只要浏览器发出的 SESSION_ID 与服务器储存的字段匹配上,那么服务器就将其认作为一个 Session,只要 SESSION_ID 的长度足够大,几乎是不可能被伪造的。因此,敏感信息储存在 Session 中要比 Cookie 安全得多。

常见的 Session 存放媒介有:

  • RAM
  • Database
  • Cache (e.g. Redis)

Session 不是爬虫可以接触到的东西。

AJAX 页面

对于静态页面(服务端渲染),使用爬虫不需要考虑太多,把页面抓取下来解析即可。但对于客户端渲染,尤其是前后端完全分离的网站,一般不能直接获取页面(甚至没有必要获取页面),而是转而分析其实际请求内容。

请求分析

通过一些请求拦截分析工具(如 Chrome 开发者工具)可以截获网站向服务器发送的所有请求以及相应的回复。

包括(不限于)以下信息:

  • 请求地址
  • 请求方法(GET / POST 等)
  • 所带参数
  • 请求头

只要把信息尽数伪造,那么爬虫发出的请求照样可以从服务器取得正确的结果。

秘钥处理

一些请求中会带有秘钥(token / sid / secret),可能随除了请求方法外的任一个位置发出,也可能都带有秘钥。更可能不止一个秘钥。

理论上来说,正常客户端取得秘钥有两种方式:

  • 服务端提供
  • 客户端自行计算,由服务端校对

对于服务端提供给客户端的秘钥,只要仔细分析 HTML 或服务端返回的 Cookie Header 就一定能发现。

而对于客户端自行计算的秘钥则比较麻烦了,尤其是在 JS 代码加密、混淆的情况下。这种时候,只能自己去用开发者工具调试原始站点代码,找出加密代码段,并在爬虫中实现。这里面有许多技巧,如各种断点、单步调试等。

表单处理

表单实际上也是 HTTP 请求,使用 GET / POST 等方法即可模拟表单提交。然而这不是重点。重点是表单常常伴随着验证码而存在。

验证码的识别暂未涉及。

浏览器模拟

爬虫的下下策才是使用浏览器完全模拟用户操作。实在是属于无奈之举。Nodejs 可以驱动 Chrome 与 Firefox 浏览器,存在相应的 Package,但是,更方便的是使用各种 E2E Testing 工具。

比如 Night Watch JS:

module.exports = {
'Demo test Google' : function (client) {
client
.url('http://www.google.com')
.waitForElementVisible('body', 1000)
.assert.title('Google')
.assert.visible('input[type=text]')
.setValue('input[type=text]', 'rembrandt van rijn')
.waitForElementVisible('button[name=btnG]', 1000)
.click('button[name=btnG]')
.pause(1000)
.assert.containsText('ol#rso li:first-child',
'Rembrandt - Wikipedia')
.end();
}
};

在这种模式下,Cookie / Session / 请求等各种细节都不用关心了。只需要按部就班地执行操作即可。模拟浏览器的代价是效率太低,内存开销大,但在某些特定需求情况下,却比一般爬虫要简单得多。

 

D3 (Data-Driven Documents) 是一个 JavaScript Library,用来做 Web 端的数据可视化实现以及各种绘图。

D3.js is a JavaScript library for manipulating documents based on data. D3 helps you bring data to life using HTML, SVG, and CSS.

学习 D3 需要很多预备知识:

  1. HTML / DOM
  2. CSS
  3. JavaScript (better with jQuery)
  4. SVG

HTML / CSS 不必多说,因为 D3 含有大量链式操作函数以及选择器等,因此如果有 jQuery 基础将轻松很多。此外,由于一般采用 SVG 方式进行绘图,所以 SVG 基础知识也需要掌握。

虽然必须的预备知识如此之多,但 D3 的定位其实是 Web 前端绘图的底层工具,所谓底层,即是操作复杂而功能强大者。

阅读全文 »

忍无可忍,长期更新。

(其实我很想自己重新做一个 blog,但是太麻烦,也没什么实践价值了,无非 CRUD,而且维护起来很容易忽略 blog 本身的目的所在)

阅读全文 »

项目地址:https://github.com/wxsms/zhihu-spider

简介:使用 Node.js 实现的一个简单的知乎爬虫,可以以一个用户为入口,爬取其账号下的一些基本信息,关注者,关注话题等。再通过关注者的 ID 继续爬取其他用户,以此循环。

实现功能:登录知乎(因为调用一些知乎 API 需要保存 session),解析页面,访问 AJAX API,保存到数据库。

阅读全文 »

知乎上有一个黑 JavaScript 的段子,大概是说:

N 年后,外星人截获了 NASA 发射的飞行器并破解其源代码,翻到最后发现好几页的 }}}}}}……

这是因为 NASA 近年发射过使用 JavaScript 编程的飞行器,而 Node.js 环境下的 JavaScript 有个臭名昭著的特色:Callback hell(回调地狱的意思)

JavaScript Promise 是一种用来取代超长回调嵌套编程风格(特指 Node.js)的解决方案。

比如:

getAsync("/api/something", (error, result) => {
if(error){
//error
}
//success
});

将可以写作:

let promise = getAsyncPromise("/api/something"); 
promise.then((result) => {
//success
}).catch((error) => {
//error
});

乍一看好像并没有什么区别,依然是回调。但最近在做的一个东西让我明白,Promise 的目的不是为了干掉回调函数,而是为了干掉嵌套回调函数。

阅读全文 »
0%