# JarvisCrawlerCore Development Log

### 2020-02-08

这几天重写douban，代码结构又有点小调整，感觉越来越好了。  
但是，  
这套代码其实有3种写法了，好乱，没时间整理老代码，先这样吧。

### 2019-12-26

折腾过2次windows下的项目安装，前面一次是自己的虚拟机上，因为以前装过vs，所以除了网络问题外，没遇到别的什么问题，这次是在一台服务器上，node-gyp稍折腾了下，一定要参考node-gyp的文档，先把prebuild环境装好，其实也就是一个命令行的事情。

然后麻烦在于国内网络对aws限制很死，而github很多资源都放aws上，aws好像会临时给你生成一下下载通道，超过时间就下载不到了。

hosts这些也都不行，我是找台机器下载好，然后把文件复制到npm的cache里面把问题解决掉的。

这次主要是sharp有一个libvips的依赖，在 C:\Users\Administrator\AppData\Roaming\npm-cache\_libvips 下有个文件，下载地址在这里 https://github.com/lovell/sharp-libvips/releases/download/v8.8.1/libvips-8.8.1-win32-x64.tar.gz 下载完覆盖进去就好了。

期待 github 入华。

### 2019-12-07

http request返回一般是 buffer，在 nodejs里，如果是utf8编码的，可以直接 toString 得到string。但如果是unicode编码的，则需要转码，可以通过 String.fromCharCode 转码。

### 2019-11-14

taobao的页面，一般是 https://item.taobao.com/item.htm?id=592826167303 ，重点在 itemid 。

taobao 和 tmall 页面布局差别很大，很多数据没有像 tmall 那样处理，sib.html 里，有具体 sku 的价格和库存数据。  
然后 Hub.config.sku 里是另外一部分 sku 数据，2个合起来应该就够了。

### 2019-11-05

今天遇到一个browser的bug，就是可能frames取不全，网上查了一下，有一个很详细的issue，就前不久刚发的，但那个在chrome下无效，因为一些别的原因，必须要用chrome。  
后来找到了一个``OOPIF``的issue，里面提到是浏览器bug，加个参数启动就可以解决。  

```
-disable-features=site-per-process
```

果然就好了。

然后是关于``Captcha``的，其实复杂的校验机制解决起来会很折腾，技术是一方面，剩下的更多还是动脑筋，得有技巧才行，不到万不得已，不要硬解。  
关键还是不要过分挖掘别人的数据吧，一定的自动化，提升效率为主，恶意盗取数据还是不可取的。

### 2019-10-24

``node.js`` 版本更新非常频繁，大部分时候大版本升级都是向下兼容的，但``node-gyp``相关的没那么快，所以``package-lock.json``最好还是同步一下，然后 ``Dockerfile`` 里的node版本号，至少也要给到大版本，今天``dockerhub``发布就遇到问题了，一查才发现``node.js``升级到13了，按照``node.js``的规划，奇数版本属于开发版，还是用现在的LTS版比较好。  

### 2019-10-22

JD，如果 ``.activity-banner`` 的 id 是 ``J_atmosphere_banner``，可能是双十一。  
如果是 ``banner-shangou``，则是闪购。  
``$$('.activity-message')[0].innerText``是闪购的剩余时间，好像可以取到更细的值，但估计用处不大。  
``$$('.summary-price-wrap')[0].getElementsByClassName('p-price')`` 这个是闪购价格。  
``document.getElementById('page_opprice').innerText``是原始价格。
只要不是``pingou`` 和 ``banner-shangou``，估计都是正常商品。  
这时，取``#jd-price``是价格。  
这里的价格类似￥279.30，如果要转浮点数，需要特殊处理一下。  
``#page_maprice`` 这是原价。  
``.quan-item`` 这是优惠券。  
``.prom-item`` 可以取到促销信息。  
这可能有多个，每个节点下面，前面2个em，一个是类型，一个是内容。  

如果不是pingou，这样可以找到服务提供商。  

``` js
$$('#summary-service')[0].getElementsByClassName('hl_red')
```

### 2019-10-19

jd的促销活动页面，一般是这样的，https://pro.jd.com/mall/active/3nTQQZ66AGtiwwtRcikGFnT1DVjX/index.html 。  
这个页面，其实没有太多结构化数据，简单的找到 ``a`` 即可。  

如果是 ``https://item.jd.com/`` 开头的就是商品页。  
如果是 ``https://pro.jd.com/mall/active/`` 开头的就是活动页。  

mountainsteals 商品页，下面是商品类型。  

``` js
$$('.breadcrumb-itm')
```

下面是价格购买区。  

``` js
$$('#widget_product_info_viewer')
```

下面是商品名。  
商品名里有品牌。

``` js
$$('.product_name')
```

这是品牌。  

``` js
$$('.brand')
```

商品评分。  

``` js
$$('.RatingAddtlInfo')
```

当前价格。  

``` js
$$('.price-set')
```

原价

``` js
$$('.pdp-strike-extra')
```

这个是大小、颜色的选框，可能多个，也可能是大小颜色以外的。  

``` js
$$('.po-link.po-size-link')
```

这个是选项内容。  

``` js
$$('.selector-itm-box')
```

取到这个节点的class以后，可以根据class是否有 js-size-product-thumb 或 js-color-product-thumb 判断是 size 还是 color。  

``` js
$$('.selector-itm-box')[0].getElementsByClassName('po-row')[0].getElementsByClassName('product-thumb')
```

如果是size，这样可以取到size内容。

``` js
$$('.selector-itm-box')[0].getElementsByClassName('po-row')[0].getElementsByClassName('js-size-name')
```

如果是color，这样可以取到color内容。

``` js
$$('.selector-itm-box')[1].getElementsByClassName('po-row')[0].getElementsByClassName('js-color-name')
```

下面是各种评分的人数。  
这里面，``data-bv-histogram-rating-value``是星，``data-bv-histogram-rating-count``是人数。

``` js
$$('.bv-inline-histogram-ratings-bar')
```

平均明细评分，一个标题 ``$$('.bv-secondary-rating-summary-id.bv-td')``，一个评分 ``$$('.bv-secondary-rating-summary-rating')``。  
其中，如果是 Fit，则没有数字的评分。  
Fit 还要找一些例子看。

评论，``$$('.bv-content-item.bv-content-top-review.bv-content-review')`` 这样可以找到。  

作者名。  
可能会找到多个，取第一个即可。

``` js
$$('.bv-content-item.bv-content-top-review.bv-content-review')[0].getElementsByClassName('bv-author')
```

作者位置，字符串的。  

``` js
$$('.bv-content-item.bv-content-top-review.bv-content-review')[0].getElementsByClassName('bv-author-location')
```

下面是这个用户对这件商品的评分。  
children[0] 的 attributes 里，应该有 itemprop ，且该值为 ratingValue 。

``` js
$$('.bv-content-item.bv-content-top-review.bv-content-review')[0].getElementsByClassName('bv-content-rating bv-rating-ratio')[0].children[0].content
```

评价的标题。  

``` js
$$('.bv-content-item.bv-content-top-review.bv-content-review')[0].getElementsByClassName('bv-content-title')
```

评价的内容。  

``` js
$$('.bv-content-item.bv-content-top-review.bv-content-review')[0].getElementsByClassName('bv-content-summary-body-text')
```

是否推荐，找到这个节点。  
如果有 ``bv-content-data-recommend-no`` 节点，表示不推荐。  
``'bv-content-data-recommend-yes'`` 表示推荐。

``` js
$$('.bv-content-item.bv-content-top-review.bv-content-review')[3].getElementsByClassName('bv-content-data')
```

评论时间，字符串方式的。  
几年以前几个月以前这样的。  

``` js
$$('.bv-content-item.bv-content-top-review.bv-content-review')[2].getElementsByClassName('bv-content-datetime-stamp')
```

### 2019-10-18

jd的商品页面，在 https://item.jd.com/下面，一般是html页面，譬如https://item.jd.com/100006585530.html。  
商品类别，可以找这个 ``#crumb-wrap``，或者直接找这个 ``.crumb.fl.clearfix``。  

``` js
$$('.crumb.fl.clearfix')[0].getElementsByClassName('item')
```

这样的结果，里面去掉class包含sep的，也就是``.item.sep``的。  
里面品牌那里是个select，但我们只要文本，所以也可以很方便取到。  

``` js
$$('.crumb.fl.clearfix')[0].getElementsByClassName('item')[6].innerText
```

取名字。  

``` js
$$('.sku-name')[0].innerText
```

这里，如果里面有img，最好把alt取出来，做tag用。  

``` js
$$('.sku-name')[0].getElementsByTagName('img')[0].alt
```

下面是产品描述，不知道为啥叫new。  

``` js
$$('.news')[0].innerText
```

接下来是banner，这个应该是分类型的。

``` js
$$('.activity-banner')
```

类型，我估计可以通过这个id来判断。  
或者，取到下面的innerText也可以的。  

``` js
$$('.activity-type')[0].innerText
```

然后，估计不同的类型，会有不同的数据。

``` js
$$('.activity-message')[0].getElementsByClassName('item')
```

这里，如果是预售，第一个是预定量，第二个是剩余时间，时间是中文的剩余时间，要反推出结束时间来。

然后是价格，我估计要根据类型来。  
下面这个是尽量回避类型的取值了。

``` js
$$('.summary-price-wrap')[0].getElementsByClassName('summary-price')
```

商品摘要信息。  

``` js
$$('.summary.p-choose-wrap')
```

这个是售后提供者。  

``` js
$$('.summary-service')[0].getElementsByTagName('span')[0].innerText
```

这个是发货时间。  

``` js
$$('#summary-yushou-ship')[0].getElementsByClassName('dd')[0].innerText
```

这个是重量。  

``` js
$$('#summary-weight')[0].getElementsByClassName('dd')[0].innerText
```

下面是选择项，估计可能有多个，这个children里面，id会类似``choose-attr-1``这样。  

``` js
$$('#choose-attrs')[0].children
```

或者，这样  

``` js
$$('#choose-attrs')[0].getElementsByClassName('li p-choose')
```

取选项数据  

``` js
$$('#choose-attrs')[0].getElementsByClassName('li p-choose')[0].getElementsByClassName('item')
```

评论数据。  

``` js
$$('.comment-info.J-comment-info')
```

好评百分比。  

``` js
$$('.percent-con')[0].innerText
```

这里是评论的tag列表。  

``` js
$$('.percent-info')[0].getElementsByTagName('span')
```

这里是评论的统计。  

``` js
$$('.J-comments-list.comments-list.ETab')[0].getElementsByClassName('tab-main small')[0].getElementsByTagName('li')
```

其中，class为current的是总计，class为J-addComment的是追评，剩下的有class的都可以放弃掉。

``#detail``这个里面，找``li``。  
里面找 商品评价 。

``` js
$$('#detail')[0].getElementsByTagName('li')
```

### 2019-10-16

这几天一直发现Charles可能会卡，以为是Charles的问题，今天仔细查了一下，还是crawler的bug，有时候chrome还是会卡住。

最初想法是从grpc这边加超时，后来想到这样crawler这边还是可能会慢慢积累chrome进程，时间长了，内存会受不了的。

实际测了一下，这种情况下，很多是websocket的一个错误，因为现在一个请求，可能要几分钟时间才能返回，有可能是grpc底层网络问题吧，但如果这时重启jarviscrawlerserv，客户端那边能很快得到响应，原因待查。  

有个正确的判断element是否可视的接口。

``` js
/**
 * isElementVisible
 * @param {object} page - page
 * @param {object} ele - element
 * @return {bool} isvisible - is visible
 */
async function isElementVisible(page, ele) {
  const isVisibleHandle = await page.evaluateHandle((e) => {
    const style = window.getComputedStyle(e);
    return (
      style &&
      style.display !== 'none' &&
      style.visibility !== 'hidden' &&
      style.opacity !== '0'
    );
  }, ele);
  const visible = await isVisibleHandle.jsonValue();
  const box = await ele.boxModel();
  if (visible && box) {
    return true;
  }
  return false;
}
```

### 2019-10-08

关于``puppeteer``，特别需要注意要waitFor，会有大量的element都不是马上构建好的，切记一定要waitFor以后再操作。

然后就是click，最好先hover，这时会移动滚动条，保证该element可见。  
这时可能还会有些页面级的bug，譬如分页的表单，页数为1位数甚至2位数都可见，但到3位数时，可能就会看不见，这时hover或者click都会报错，这时就只能自己特殊处理了。

### 2019-09-09

今天部署了一台国内的机器，整个流程都非常的不顺，需要加各种镜像映射才行，后来还是将dockerhub弄好了，这样就不用走build docker的流程，会好很多吧。

dockerhub非常慢，build一次要18分钟。

### 2019-08-25

前几天开始把npm提交放到github的action了，这几天测了一下，没啥问题，每次合并master时，就会自动发布到npm。

然后，基于golang的jccclient差不多也完善了。

接下来，计划会升级到v0.3，主要是彻底的插件化，现在代码越来越多，不容易维护了。  
升级到v0.3以后，还有个很重要的，就是会考虑私有插件的问题。  
现在的想法比较简单，私有插件的话，自己做个私有bin就好了。  
到v0.3，就不支持v0.1的grpc接口了。

### 2019-08-07

今天开始，npm的提交基于github上的release包了。

``` sh
npm publish https://github.com/zhs007/jarviscrawlercore/archive/v0.2.62.tar.gz
```

``puppeteer``的``networkidle2``这一组感觉也不怎么靠谱，可以自己侦听 request 和 response 来处理，这样比较容易控制一些。

``` js
  page.on('request', (req) => {
    let url = req.url();
    if (url.indexOf('data:image') == 0) {
      url = 'local:imgdata-' + hashMD5(url);
    }

    const oldreq = findReq(lstReq, url);
    if (oldreq) {
      return;
    }

    console.log('request - ', url);

    lstReq.push({
      url: url,
      st: Date.now(),
      et: -1,
      status: 0,
      buflen: 0,
    });
  });

  page.on('response', async (res) => {
    let url = res.url();
    if (url.indexOf('data:image') == 0) {
      url = 'local:imgdata-' + hashMD5(url);
    }

    console.log('response - ', url);

    const req = findReq(lstReq, url);
    if (req) {
      const buf = await res.buffer();
      req.buflen = buf.byteLength;

      req.et = Date.now();
      req.status = res.status();
    } else {
      console.log('no response', url);
    }
  });
```

还有几个简单的处理函数，在这里。

``` js
/**
 * findReq - find a request
 * @param {array} reqs - request list
 * @param {string} url - url
 * @return {object} req - request
 */
function findReq(reqs, url) {
  for (let i = 0; i < reqs.length; ++i) {
    if (reqs[i].url == url) {
      return reqs[i];
    }
  }

  return undefined;
}

/**
 * isReqFinished - is request finished?
 * @param {array} reqs - request list
 * @return {bool} isfinished - is finished
 */
function isReqFinished(reqs) {
  for (let i = 0; i < reqs.length; ++i) {
    if (reqs[i].status == 0) {
      return false;
    }
  }

  return true;
}
```

其实需要特殊考虑的主要就是多次的request请求，特殊处理一下就好。

### 2019-07-23

最近在日本，dtbkbot的测试用``service+dtclient2``。

### 2019-07-09

关于``puppeteer``，最近又有些感想：

1. 自己通过``page``来侦听``domcontentloaded``这些事件会比``page.waitFor``要稳定一些，细节更可控，官方的``waitFor``实现应该是太简单了，所以导致某些复杂逻辑下经常会卡主。
2. ``frame``的处理，我一般是等待该页面的``response``彻底完成，这个只能在``page``来处理。
3. ``frame``内部的``reCAPTCHA``是可以处理对的，基本上可以通过``page``处理对，但切记，要确定彻底加载成功，这个步骤非常重要，我一般通过``response``和``domcontentloaded``来确定彻底加载成功，然后通过clientRect计算出点击位置或hold位置。
4. ``page``的``viewport``在某些时候非常重要，如果element不可见，可能会导致后面的操作报错。
5. ``click``操作其实会移动屏幕，当然只限于有滚动条的情况，如果页面写得不好，对``viewport``有要求，``click``是可能报错的，这时只能通过改变``viewport``避免bug。
6. 安全稳定的操作，一定是逻辑严密的，不能所有事都依赖于sleep时间，而最后从逻辑层避免sleep。
7. 业务逻辑上，一定要处理所有的异常状况，一些未考虑的异常保留足够的输出，并安全退出，为下一次做准备。

```
node ./bin/jarviscrawler.js bt ./cfg/btcfg.yaml -n oabt
```

豆瓣查找  

```
node ./bin/jarviscrawler.js douban search -s "蜘蛛 侠" -d true -t movie
```

### 2019-06-23

关于爬虫，其实这次写这个项目，并不是希望把数据全拉下来（不现实也没啥必要）。  
只是想能有个更方便的查询方式而已。  
目的是自动化，不是拿到数据。

### 2019-06-22

今天新开了``0.2``的分支，主要是下面几个结构调整：

1. 命令行模式切换到具体模块内，这样 ``bin/jarviscrawler.js`` 代码结构会更清晰。
2. ``service``模式下，增加统一的请求协议，统一来处理协议过长需要``stream``的情况。

### 2019-06-21

crunchbase organization 页面的一点记录：

- ``cb-overflow-ellipsis`` 名字。
- 大栏目 ``layout-row section-header ng-star-inserted`` ，该元素的父节点才是card节点。
- 在 overview 和 IPO 分栏里， ``cb-text-color-medium field-label flex-100 flex-gt-sm-25 ng-star-inserted`` 是所有的小栏目，该节点的next是内容。
- 剩下几个表格card里，``tr.ng-star-inserted``取到行。

crunchbase login 页面的一点记录：
- ``tag``为``mat-form-field``的是输入框，search也是一个输入框。
- ``id``为``mat-input-1``是email，``mat-input-2``是密码。
- ``.cb-text-transform-upper.mat-raised-button.mat-primary``这个是登录按钮。

怎么判断``reCAPTCHA``？  
crunchbase页面会多产生一次跳转，且如果第二次跳转返回403，就会进入``reCAPTCHA``流程。  
譬如我们访问``https://www.crunchbase.com/organization/slack``，当mainframe第二次定位到这个url时，如果response的status是403的话，就是``reCAPTCHA``。  
今天实现了这个，但有个小问题，再登录时，有可能点击登录按钮再跳转页面的时候出现``reCAPTCHA``。

处理``captcha``  
通过``#px-captcha``得到区域，然后模拟鼠标操作。  
鼠标操作有问题，如果页面比较大，不能直接用mouse来操作，而应该自己发送event，因为client坐标需要写对。  
在控制台，这个指令可以查看``event``，``monitorEvents(document.body, "click");``。

### 2019-06-20

这个可以通过crunchbase查询公司情况，这个接口是查询公司的，可以根据常规的名字查到公司代码。
```
node ./bin/jarviscrawler.js crunchbase companies -c Facebook
```

这个用来查询公司明细，需要传入companycode才行。

```
node ./bin/jarviscrawler.js crunchbase company -c slack
```

crunchbase前面有个检查，暂时没有处理，有些时候需要主动点一下。

这个可以下载blob的图片。  
```
node ./bin/jarviscrawler.js playngo blobimg -g gameofgladiators
```

### 2019-06-03

```
node ./bin/jarviscrawler.js yc company
```

### 2019-05-23

今天增加了zdreview.com

```
node ./bin/jarviscrawler.js getarticle -o 123.pb https://zdreview.com -d true
```

### 2019-04-23

今天发布了一个非docker版本，这样发布，可以直接安装。

```
npm publish https://github.com/zhs007/jarviscrawlercore/archive/v0.1.41.tar.gz
```

后来处理了techinasia, iheima, smzdm。

```
node ./bin/jarviscrawler.js getarticles -o 123.pb https://www.techinasia.com

node ./bin/jarviscrawler.js getarticles -o 123.pb http://www.iheima.com -d true

node ./bin/jarviscrawler.js getarticles -o 123.pb https://post.smzdm.com -d true

node ./bin/jarviscrawler.js getarticles -o 123.pb https://news.smzdm.com -d true
```

### 2019-04-18

```
node ./bin/jarviscrawler.js dtbkbot ./cfg/dttestbot.yaml -h false -d false -m gametodaydata

node ./bin/jarviscrawler.js dtbkbot ./cfg/dttestbot.yaml -h false -d false -m gamedatareport -s 2019-04-17 -e 2019-04-17
```

### 2019-04-17

从昨天开始，我发现其实很多``evaluate``的事情，其实``$eval``和``$$eval``也都能做。

### 2019-04-15

关于``puppeteer``的几个问题：  

1. 感觉一直在wait那块有bug，我的感觉是有些时候，调用的时间点，event已经触发过了，所以就只能timeout，其实这种可以用我们zhihu的一些处理方法，就是在浏览器环境里，加标志量，在nodejs里wait那个变量就好，这样不会出问题。  
2. 还有就是前几天处理``techinasia``遇到的，那个是页面动态加载的，如果取了动态加载部分，再来设置``setContent``就会卡timeout，后来回避了这个问题，就好了。  
3. 就是可能会出现``$ is not defined``的错误，我觉得是``addScriptTag``没有等待加载完成，就返回导致的，现在可以用``attachJQuery``这个接口来加载``jquery``了，这个接口会检查是否需要加载，并等待加载完成。
4. 尽量用``waitForFunction``，这个目前看来是最不容易timeout的了，然后这个里面其实也可以改浏览器环境下的值，只是依靠返回值决定是否放开waitfor而已。
5. 今天尝试过侦听2个``framenavigated``事件，发现很容易卡住，感觉并发处理有些问题，这个对调用先后有要求，所以尽量顺序调用吧。

### 2019-04-13

今天处理了``medium``、``techcrunch``。

```
node ./bin/jarviscrawler.js exparticle https://medium.com/@sean22492249/%E5%88%A9%E7%94%A8-rasa-n-rasa-core-%E4%BE%86%E5%BB%BA%E7%AB%8B%E4%B8%AD%E6%96%87%E7%9A%84-chatbot-aa65436efa5f -o ./output/abc.pdf -m pdf -h false -i true -q true -d true

node ./bin/jarviscrawler.js exparticle https://techcrunch.com/2019/04/03/ruhnn-ipo/ -o ./output/a.pdf -m pdf -h false -i true -q true -d true

node ./bin/jarviscrawler.js exparticle https://www.techinasia.com/3-vietnamese-platforms-visited-ommerce-sites-sea -o ./output/abc.pdf -m pdf -h false -i true -q true -d true
```

### 2019-04-12

今天articles也支持了service。

### 2019-04-11

```
node ./bin/jarviscrawler.js getarticles -o 123.pb http://www.baijingapp.com

node ./bin/jarviscrawler.js getarticles -o 123.pb -q true https://36kr.com

node ./bin/jarviscrawler.js getarticles -o 123.pb https://www.geekpark.net

node ./bin/jarviscrawler.js getarticles -o 123.pb https://www.huxiu.com

node ./bin/jarviscrawler.js getarticles -o 123.pb https://www.lieyunwang.com

node ./bin/jarviscrawler.js getarticles -o 123.pb https://www.tmtpost.com

node ./bin/jarviscrawler.js getarticles -o 123.pb https://techcrunch.com
```

### 2019-04-08

这几天把翻译的功能接入到``JarvisTeleBot``，现在双向翻译方便很多了。

这几天简单处理了一下电商网站，有几个小技巧，其实就是改本地``DOM``，怎么方便怎么来，没必要太拘泥于一些”好“的实现，浏览器都在手上了，能控制住的就控制住，省得进一步折腾。

今天开始将``exportarticle``移植到``service``里去，支持了长消息（``grpc``不允许大于4mb的消息）。

然后就是，代码量越来越大了，需要有单元测试才好，否则后面代码重构风险越来越大。  
目前想到的还是单独写单元测试，一方面可以测代码本身，另外还可以测网站是否升级，尽可能用现成的代码来做单元测试。

### 2019-04-05

切换到``grpc-tools``了，如果要重新buildproto，需要先装``grpc-tools``。

```
npm install -g grpc-tools
```

今天把服务也写好了，你可以通过

```
node ./bin/jarviscrawler.js startservice ./cfg/service.yaml
```

写了个简单的client，可以这样

```
node ./src/service/client.js
```

### 2019-04-04

今天把google翻译支持了。

```
node ./bin/jarviscrawler.js googletranslate "你好 你很好,哈哈" -h true -s zh-CN -d en

node ./bin/jarviscrawler.js googletranslate "@Peter Walker I am sure there is a problem with excel file, I need more time to check it." -h true -s en -d zh-CN
```

### 2019-04-02

今天调整了``exparticle``的参数。  
取消了以前可以同时输出``protobuf``、``pdf``、``jpg``的方案，现在一次只能输出一种格式。  

```
node ./bin/jarviscrawler.js exparticle https://post.smzdm.com/p/alpzl63o/ -o ./output/abc.pdf -m pdf -h true -i true

node ./bin/jarviscrawler.js exparticle https://post.smzdm.com/p/alpzl63o/ -o ./output/abc.jpg -m jpg -h true -j 80

node ./bin/jarviscrawler.js exparticle https://post.smzdm.com/p/alpzl63o/ -o ./output/abc.pb -m pb -h true -i true

node ./bin/jarviscrawler.js exparticle https://zhuanlan.zhihu.com/p/60881398 -o ./output/abc.pdf -m pdf -h true -i true -q true

node ./bin/jarviscrawler.js exparticle https://www.zhihu.com/question/295675918/answer/600007589 -o ./output/abc.pdf -m pdf -h true -i true -q true

node ./bin/jarviscrawler.js exparticle https://36kr.com/p/5191170 -o ./output/abc.pdf -m pdf -h true -i true -q true

node ./bin/jarviscrawler.js exparticle http://www.baijingapp.com/article/22290 -o ./output/abc.pdf -m pdf -h true -i true

node ./bin/jarviscrawler.js exparticle https://www.huxiu.com/article/292563.html -o ./output/abc.pdf -m pdf -h true -i true

node ./bin/jarviscrawler.js exparticle https://www.tmtpost.com/3859873.html -o ./output/abc.pdf -pdf -h true -i true

node ./bin/jarviscrawler.js exparticle https://www.geekpark.net/news/240120 -o ./output/abc.pdf -pdf -h true -i true

node ./bin/jarviscrawler.js exparticle https://www.lieyunwang.com/archives/453240 -o ./output/abcdf -m pdf -h true -i true
```

### 2019-03-29

今天加了confluence的bot，现在可以获取更新。

```
node ./bin/jarviscrawler.js confluencebot ./cfg/confluence.yaml -h false
```

### 2019-03-28

这几天还在考虑数据存储到底是用``html``还是``markdown``，``html``的格式化现在做得差不多了，但如果最后要自己渲染的话，``markdown``其实还是有些优势的，特别是代码段这些，会方便很多。  

今天支持了smzdm，发现几个新的问题，包括当前页面请求图片可能也会出现跨域问题(#15)，和images库对图片格式支持不全(#16)。

```
node ./bin/jarviscrawler.js exparticle https://post.smzdm.com/p/alpzl63o/ -o ./output/abc.pb -p ./output/abc.pdf -f A4 -h true
```

今天还支持了知乎，解决了CSP的问题，解决了jquery加载的问题，解决了图片延迟加载的问题。

```
node ./bin/jarviscrawler.js exparticle https://zhuanlan.zhihu.com/p/59909721 -o ./output/abc. -p output/abc.pdf -f A4 -h true
```

### 2019-03-27

今天把``exparticle``重构了一版，这个版本功能基本能达到目前的需求了。  
数据没有用``json``的，而是直接用了``protobuf``，支持未压缩的文件和zip压缩文件，把图片打包进去了。

今天把baijingapp的处理完了，可以通过下面命令行使用。

```
node ./bin/jarviscrawler.js exparticle http://www.baijingapp.com/article/22156 -p ./output/abc.pdf -f A4 -h true -o ./output/abc.pb
```

今天晚上把huxiu也处理完了，huxiu的代码质量更好一些，这个可以作为后续的样板。

```
node ./bin/jarviscrawler.js exparticle https://www.huxiu.com/article/291141.html -o ./output/abc.pb -p ./output/abc.pdf -f A4 -h true
```

### 2019-03-26

关于puppeteer，留点记录。

首先，``$()``和``$eval()``是不一样的，``$()``返回一个nodejs对象，``$eval()``其实是做页面操作，而且``$eval()``里面的输出也在浏览器里面。  
还有点需要注意的，就是``$eval()``的返回值是你自己控制的，可以用来传递跨域对象。

如果需要加载页面以外的js，可以用  
``` js
// url
await page.addScriptTag({url: 'https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/index.js'});
// local file
await page.addScriptTag({path: './browser/base64.js'});
```

运行js代码，用``page.evaluate``，这个里面是页面的js空间，类似chrome控制台操作，这个函数的返回值是你自己控制的，可以用来传递js对象。  

页面可以用``fetch``取数据，没有跨域问题。  
```
const response = await fetch(curimg[0].src);
```
返回是一个``ReadableStream``，可以转``arrayBuffer``、``json``、``text``等。

关于``chrome``和``nodejs``层变量传递问题，``ArrayBuffer``是传不过来的，可以通过``base64``以后传``string``。

### 2019-03-25

今天发现知乎的图片是延迟加载的，本来想到的是移动屏幕，等加载完就好，后来看别人提了个思路，是直接改img的css，改成直接加载就好，可以试试。

### 2019-03-20

今天把Docker部署完成了，最折腾的其实是字体问题，最后找到了Adobe的SourceHanSans，看起来效果还不错。

### 2019-03-19

这算是第3次写爬虫框架了，现在各种基础库比上次写的时候要完善很多，希望这次能做出点东西来。  

现在几乎完全基于puppeteer的，轻量级的后面如果有需要再加。

核心是插件系统。  
接下来应该还会做开放的插件管理器，就是不放代码库里的插件。