We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
我一般都喜欢去一些技术类博客社区或者 UGC 社区浏览文章,相信与我同类的你应该也有这爱好。为了方便自己的阅读,而不用一个一个打开目标网站的地址,就基于 Node+React 写了一个小爬虫: Tech-Read,用于抓取常去的 UGC 社区的文章摘要。目前的版本大概样子如下:
在线地址:Tech-Read github 地址:tech-read
Tech-Read 是个人的一个业余项目,初衷是方便自己阅读,实在是懒于去社区网站阅读,其次用于练手喽,毕竟最近在学点新东西。
在工作上,接触的技术栈是 Node + React,所以 TR 也采用了 Node + React 的技术栈。React 用于前端界面渲染,Node 用于抓取网页,并将解析后的 DOM 数据返回给前端调用。
前端的请求是用 fetch 发起的,由于部分社区做了跨域设置,So 用 Node 能帮我解决一些跨域的问题:
fetch
以及在 fetch 中解析 DOM 时碰到的诸如 Uncaught (in promise) TypeError: unexpected token <... 等杂七杂八的错误。
Uncaught (in promise) TypeError: unexpected token <...
并且 Node 端提供了直接操作 DOM 节点的 cheerio,它是 jQuery 的一个子集实现,能非常方便的操作 DOM 元素。所以,目前我把 DOM 解析放在了 Node 端,前端只负责渲染。
cheerio
所以,现在的处理流程如下:
由于目前业务比较简单,前端的状态管理就用 Flux。Node 使用 Koa,匹配到 fetch 发起的路由后,通过 request 向目标网站发起请求,然后通过 cheerio 解析 body,获取 DOM 元素数据,以 json 形式返回给前端进行展示。
request
request 发起的异步请求的返回对象不带 Promise/Generator 等特性,所以不能同步写,但利用 Promise 简单封装下:
exports.parseBody = function (url) { return new Promise(function (resolve, reject) { request(url, (error, res, body) => { if(!error && res.statusCode === 200) { resolve(body); } else { reject(error); } }); }); };
就能同步的来写异步请求了:
let resBody = yield lib.parseBody('http://toutiao.io/').then((body) => { return body; });
另外一个选择是,利用 co-request,基于 generator 的一个网络请求库。
co-request
我个人很喜欢开发者头条,所以第一个抓取的也是开发者头条。由于我想在 TR 上直接看原文,就像这样子:
所以我需要拿到原文链接,插入到 Iframe 里面去。抓取其它社区时,能在抓取首页时顺便拿到原文链接,但是抓取开发者头条的时候,并不能,因为它的 DOM 结构是这样的:
这个 a 的 href 属性并不是原文的链接,要想拿到原文链接,还需要再向 http://toutiao.io/r/b04ku7/r/b04ku7 发起一次 get 请求:
a
href
http://toutiao.io/r/b04ku7/r/b04ku7
但这个 Location 是不能直接拿到的,因为返回的状态码是 302,页面会被直接跳转到了 Location 指向的页面。但是,request 发起请求后的 response 中则包含了 host 和 path 信息:
Location
response
host
path
req.on('response', (res) => { if(res.statusCode === 200) { urlPath = res.client._httpMessage._headers.host + res.client._httpMessage.path; resolve(urlPath); } });
将二者拼接,就能得到原文的 URL 了。
开发者头条部分实现了无限加载:
let contents = document.getElementsByClassName('toutiao-contents')[0]; let contentsHeight = contents.getBoundingClientRect().height; contents.addEventListener('scroll', (e) => { let triggerNextMinHeight = e.target.scrollHeight - e.target.scrollTop - contentsHeight; if(triggerNextMinHeight < 22) { //fetch data & update component state } },false);
无限加载调用的接口是:
http://toutiao.io/prev/date //date 形如 2016-04-26
前文以抓取开发者头条为例,简单讲述了爬虫的实现思路,接下来就简单说说怎么部署 Node 服务。
由于服务器上已经跑了自己的博客,端口 80 已经被Apache 监听了,所以,你看到的 TR 的线上端口就是奇怪的 8080 了,这是 Nginx 监听的,然后被代理到本地的 9000 端口,这也是开发用的端口。
部署之前,先安装下 Node(系统是 CentOS 6.5 64bit)。
1、安装nvm
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash
或者:
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash
建议使用 nvm 安装 Node,因为 nvm 会安装到用户的目录,而 n 会安装到全局的 /usr/ 目录下去。
/usr/
2、安装 Node
nvm install 4.4.2
如果安装了多版本,则可以将默认的 Node 版本设置成 4.4.2:
nvm alias default 4.4.2
3、安装Nginx
切换到 /etc/yum.repos.d/ 创建文件 nginx.repo ,将下面的粘贴到文件中:
/etc/yum.repos.d/
nginx.repo
[nginx] name=nginx repo baseurl=http://nginx.org/packages/centos/$releasever/$basearch/ gpgcheck=0 enabled=1
然后安装 Nginx:
yum install nginx
另一种安装方式是开启 epel-source( epel.repo 中需要有这个配置项)。
epel-source
epel.repo
首先切换到 /etc/yum.repos.d,运行 cat epel.repo:
/etc/yum.repos.d
cat epel.repo
// ... [epel] // ... enabled=1 // ... [epel-source] // ... enabled=0 // ... // ...
[epel] 里面的 enabled 是 1(如果是0要改为1), [epel-source] 里面的 enabled 是 0,将其改为1,退出保存。
[epel]
enabled
[epel-source]
运行 yum repolist 查看是否添加了 epel-source:
yum repolist
// ... epel/x86_64 Extra Packages for Enterprise Linux 7 - x86_64 12299 epel-source Extra Packages for Enterprise Linux 7 - x86_64 - Source 0
然后运行 yum install -y nginx 安装 nginx,安装完成之后可运行 nginx -v 查看 nginx 的版本。
yum install -y nginx
nginx -v
EPEL的全称叫 Extra Packages for Enterprise Linux 。EPEL是由 Fedora 社区打造,为 RHEL 及衍生发行版如 CentOS、Scientific Linux 等提供高质量软件包的项目
Nginx 的相关配置:
/etc/init.d/nginx start/restart # 启动/重启Nginx服务 /etc/init.d/nginx stop # 停止Nginx服务 /etc/nginx/nginx.conf # Nginx配置文件位置
4、配置 Nginx
切换到 /etc/nginx/conf.d ,复制 default.conf 文件,按照需要配置新的 conf 文件:
/etc/nginx/conf.d
default.conf
techread.conf 配置文件如下:
listen: 监听的线上端口 server_name: 访问的域名 root: 根目录 index: 默认访问的文件
nginx 可以有多个虚拟主机,每个虚拟主机一个对应的server配置项。
红框部分是 Nginx 的反向代理配置,常见的配置项如下:
location / { # 被代理的服务器IP proxy_pass http://10.0.0.137; # 必需 也可以是本机上的一个 node 服务地址,如127.0.0.1 # 以下是一些反向代理的配置(非必需) proxy_redirect off; # 后端的Web服务器可以通过 X-Forwarded-For 获取用户真实IP proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 允许客户端请求的最大单文件字节数 client_max_body_size 10m; # 缓冲区代理缓冲用户端请求的最大字节数 client_body_buffer_size 128k; # nginx跟后端服务器连接超时时间(代理连接超时) proxy_connect_timeout 300; # 后端服务器数据回传时间(代理发送超时) proxy_send_timeout 300; # 连接成功后,后端服务器响应时间(代理接收超时) proxy_read_timeout 300; # 设置代理服务器(nginx)保存用户头信息的缓冲区大小 proxy_buffer_size 4k; #proxy_buffers缓冲区,网页平均在32k以下的话,这样设置 proxy_buffers 4 32k; #高负荷下缓冲大小(proxy_buffers*2) proxy_busy_buffers_size 64k; #设定缓存文件夹大小,大于这个值,将从upstream服务器传 proxy_temp_file_write_size 64k; }
proxy_pass 是必备项,表示要被代理的服务地址,其它可有可无。
proxy_pass
安利一份关于解读 Nginx 源码的资源:Nginx 福利
启动 Nginx 时,默认是读取 default.conf,现在需要将其更改为读取 techread.conf。 回到 /etc/nginx 下,修改 nginx.conf 文件, 将 include 的引用改为新建的文件:techread.conf:
techread.conf
/etc/nginx
nginx.conf
include
重启 nginx,线上部署就 OK 了。
The text was updated successfully, but these errors were encountered:
小白一枚,请问作者是手动运行webpack命令构建好react部分——生成/public/assets/bundle.js?
Sorry, something went wrong.
棒
perfect
No branches or pull requests
我一般都喜欢去一些技术类博客社区或者 UGC 社区浏览文章,相信与我同类的你应该也有这爱好。为了方便自己的阅读,而不用一个一个打开目标网站的地址,就基于 Node+React 写了一个小爬虫: Tech-Read,用于抓取常去的 UGC 社区的文章摘要。目前的版本大概样子如下:
在线地址:Tech-Read
github 地址:tech-read
开发
Tech-Read 是个人的一个业余项目,初衷是方便自己阅读,实在是懒于去社区网站阅读,其次用于练手喽,毕竟最近在学点新东西。
在工作上,接触的技术栈是 Node + React,所以 TR 也采用了 Node + React 的技术栈。React 用于前端界面渲染,Node 用于抓取网页,并将解析后的 DOM 数据返回给前端调用。
前端的请求是用
fetch
发起的,由于部分社区做了跨域设置,So 用 Node 能帮我解决一些跨域的问题:以及在
fetch
中解析 DOM 时碰到的诸如Uncaught (in promise) TypeError: unexpected token <...
等杂七杂八的错误。并且 Node 端提供了直接操作 DOM 节点的
cheerio
,它是 jQuery 的一个子集实现,能非常方便的操作 DOM 元素。所以,目前我把 DOM 解析放在了 Node 端,前端只负责渲染。所以,现在的处理流程如下:
由于目前业务比较简单,前端的状态管理就用 Flux。Node 使用 Koa,匹配到
fetch
发起的路由后,通过request
向目标网站发起请求,然后通过cheerio
解析 body,获取 DOM 元素数据,以 json 形式返回给前端进行展示。request 发起的异步请求的返回对象不带 Promise/Generator 等特性,所以不能同步写,但利用 Promise 简单封装下:
就能同步的来写异步请求了:
另外一个选择是,利用
co-request
,基于 generator 的一个网络请求库。我个人很喜欢开发者头条,所以第一个抓取的也是开发者头条。由于我想在 TR 上直接看原文,就像这样子:
所以我需要拿到原文链接,插入到 Iframe 里面去。抓取其它社区时,能在抓取首页时顺便拿到原文链接,但是抓取开发者头条的时候,并不能,因为它的 DOM 结构是这样的:
这个
a
的href
属性并不是原文的链接,要想拿到原文链接,还需要再向http://toutiao.io/r/b04ku7/r/b04ku7
发起一次 get 请求:但这个
Location
是不能直接拿到的,因为返回的状态码是 302,页面会被直接跳转到了Location
指向的页面。但是,request 发起请求后的response
中则包含了host
和path
信息:将二者拼接,就能得到原文的 URL 了。
开发者头条部分实现了无限加载:
无限加载调用的接口是:
部署
前文以抓取开发者头条为例,简单讲述了爬虫的实现思路,接下来就简单说说怎么部署 Node 服务。
由于服务器上已经跑了自己的博客,端口 80 已经被Apache 监听了,所以,你看到的 TR 的线上端口就是奇怪的 8080 了,这是 Nginx 监听的,然后被代理到本地的 9000 端口,这也是开发用的端口。
部署之前,先安装下 Node(系统是 CentOS 6.5 64bit)。
1、安装nvm
或者:
建议使用 nvm 安装 Node,因为 nvm 会安装到用户的目录,而 n 会安装到全局的
/usr/
目录下去。2、安装 Node
如果安装了多版本,则可以将默认的 Node 版本设置成 4.4.2:
3、安装Nginx
切换到
/etc/yum.repos.d/
创建文件nginx.repo
,将下面的粘贴到文件中:然后安装 Nginx:
另一种安装方式是开启
epel-source
(epel.repo
中需要有这个配置项)。首先切换到
/etc/yum.repos.d
,运行cat epel.repo
:[epel]
里面的enabled
是 1(如果是0要改为1),[epel-source]
里面的enabled
是 0,将其改为1,退出保存。运行
yum repolist
查看是否添加了epel-source
:然后运行
yum install -y nginx
安装 nginx,安装完成之后可运行nginx -v
查看 nginx 的版本。Nginx 的相关配置:
4、配置 Nginx
切换到
/etc/nginx/conf.d
,复制default.conf
文件,按照需要配置新的 conf 文件:techread.conf 配置文件如下:
listen: 监听的线上端口
server_name: 访问的域名
root: 根目录
index: 默认访问的文件
nginx 可以有多个虚拟主机,每个虚拟主机一个对应的server配置项。
红框部分是 Nginx 的反向代理配置,常见的配置项如下:
proxy_pass
是必备项,表示要被代理的服务地址,其它可有可无。安利一份关于解读 Nginx 源码的资源:Nginx 福利
启动 Nginx 时,默认是读取
default.conf
,现在需要将其更改为读取techread.conf
。 回到/etc/nginx
下,修改nginx.conf
文件, 将include
的引用改为新建的文件:techread.conf:重启 nginx,线上部署就 OK 了。
The text was updated successfully, but these errors were encountered: