nodejs爬虫模拟登陆如何模拟百度帐号登录

如何使用nodejs做爬虫程序? - 知乎250被浏览<strong class="NumberBoard-itemValue" title="2分享邀请回答select * from html where url="http://finance.yahoo.com/q?s=yhoo" and xpath='//div[@id="yfi_headlines"]/div[2]/ul/li/a'
这个查询由两个参数组成:“url” 和 “xpath”。网址大家都知道。XPath 包含一个 XPath 字符串,告诉 YQL 应该返回 HTML 的哪一部分。在查询一下试试。还有一些可用的参数包括
browser (布尔型),charset(字符串)和 compat(字符串)。我没有使用这些参数,但如果你有特别需要的话可以参考文档。XPath 感觉不舒服?很不幸,XPath 不是一个获取 HTML 属性结构的常用方式。对于新手读和写都可能很复杂。看看下一个表,可以完成同样的事,但使用 CSS 做替代data.html.cssselect 表data.html.cssselect 表是我推荐的爬取页面 HTML 方式。和 html 表用相同的方式工作,但可以用 CSS 替代 XPath。实际上,这个表默默把 CSS 转换为 XPath,然后调用 html 表,所以会有一点慢。对于爬取网页来说,区别可以忽略不计。使用这个表的通常方式是:select * from data.html.cssselect where url="www.yahoo.com" and css="#news a"
可以看到,整洁许多。我建议在尝试用 YQL 爬取网页的时候优先尝试这个方法。 在查询一下试试。htmlstring 表htmlstring 表在尝试从网页爬取大量格式化文本的时候用。用这个表可以用一个单独的字符串抓取网页的全部 HTML 内容,而不是基于 DOM 结构切分的 JSON。例如,一个爬取 &a& 标签的常规 JSON 返回:"results": {
"href": "...",
"target": "_blank",
"content": "Apple Chief Executive Cook To Climb on a New Stage"
看到 attribute 如何定义为 property 了吧?相反,htmlstring 表的返回看起来会像这样:"results": {
"result": {
"&a href=\"…\" target="_blank"&Apple Chief Executive Cook To Climb on a New Stage&/a&
所以,为什么要这么用呢?从我的经验来看,尝试爬取大量格式化文本的时候会相当有用。例如下面的片段:&p&Lorem ipsum &strong&dolor sit amet&/strong&, consectetur adipiscing elit.&/p&
&p&Proin nec diam magna. Sed non lorem a nisi porttitor pharetra et non arcu.&/p&
使用 htmlstring 表,可以把这个 HTML 获取为字符串,然后用正则移除 HTML 标签,留下的就只有文本了。这比 JSON 根据页面的 DOM 结构分为属性和子对象的迭代更容易。在 NodeJS 里用 YQL现在我们了解了一些 YQL 中可用的表,让我们用 YQL 和 NodeJS 实现一个网络爬虫。幸运的是,相当简单,感谢 Derek Gathright 写的 node-yql 模块。可以用 npm 安装它:npm install yql
这个模块极为简单,只包括一个方法:YQL.exec() 方法。定义如下:function exec (string query [, function callback] [, object params] [, object httpOptions])
我们 require 它然后调用 YQL.exec() 就可以用了。例如,假设要抓取 Nettuts 主页所有文章的标题:var YQL = require("yql");
new YQL.exec('select * from data.html.cssselect where url="http://net.tutsplus.com/" and css=".post_title a"', function(response) {
//response consists of JSON that you can parse
YQL 最棒的就是能够实时测试查询然后确定会返回的 JSON。去
用一下试试,或者点击查看原生 JSON。params 和 httpOptions 对象是可选的。参数可以包括像 env(是否为表使用特定的环境) 和 format (xml 或 json)这样的属性。所有传给 params 的属性都是 URI 编码然后附到查询字符串的尾端。httpOptions 对象被传递到请求头中。例如这里你可以指定是否想启用 SSL。叫做 yqlServer.js 的 JavaScript 文件,包含使用 YQL 爬取所需的最少代码。可以在终端里用以下命令来运行它:node yqlServer.js
例外情况和其它知名工具YQL 是我推荐的爬取静态网页内容的选择,因为读起来简单、用起来也简单。然而,如果网页有 robots.txt 文件来拒绝响应,YQL 就会失败。在这种情况下,可以看看下面提到的工具,或者用下一节会讲的 PhantomJS。 是一个实用的 Node 工具,为数据爬取而特别设计。可以创建接受输入,处理并返回某些输出的作业。Node.io 在 GitHub 上关注量很高,有一些实用的例子帮你上手。 是一个很流行的项目,用 JavaScript 实现了 W3C DOM。当提供 HTML 时,它可以构造一个能够与之交互的 DOM。查看文档,了解如何使用 JSDOM 和任意 JS 库(如 jQuery )一起从网页抓取数据。从页面抓取动态内容到目前为止,我们已经看过一些工具,可以帮助我们抓取静态内容的网页。有了YQL,相当简单。不幸的是,我们经常看到一些内容是用JavaScript动态加载的页面。在这些情况下,页面最初通常为空,然后随后附加内容。如何处理这个问题呢?例子我提供了一个例子;我上传了一个简单的 HTML 文件到我自己的网站,document.ready() 函数被调用后两秒通过 JavaScript 附加了一些内容。可以在查看这个页面。源文件如下:&!DOCTYPE html&
&title&Test Page with content appended after page load&/title&
Content on this page is appended to the DOM after the page is loaded.
&div id="content"&
&script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"&&/script&
$(document).ready(function() {
setTimeout(function() {
$('#content').append("&h2&Article 1&/h2&&p&Lorem ipsum dolor sit amet, consectetur adipiscing elit.&/p&&h2&Article 2&/h2&&p&Ut sed nulla turpis, in faucibus ante. Vivamus ut malesuada est. Curabitur vel enim eget purus pharetra tempor id in tellus.&/p&&h2&Article 3&/h2&&p&Curabitur euismod hendrerit quam ut euismod. Ut leo sem, viverra nec gravida nec, tristique nec arcu.&/p&");
现在尝试用 YQL 从 &div id=“content”& 中抓取文本。var YQL = require("yql");
new YQL.exec('select * from data.html.cssselect where url="http://tilomitra.com/repository/screenscrape/ajax.html" and css="#content"', function(response) {
//This will return undefined! The scraping was unsuccessful!
console.log(response.results);
你会发现 YQL 返回了 undefined,因为页面被加载后,&div id=“content”& 是空的。内容还没有被附加上去。可以在自己尝试一下。来看看如何解决这个问题!PhantomJSPhantomJS 可以加载网页,并模仿基于 Webkit 的浏览器,然而并没有 GUI。从这类站点爬取信息我建议的方式是使用
。PhantomJS 形容自己是“用 JavaScript API 的无用户界面 Webkit。“简单来说,表示 PhantomJS 可以加载网页然后模仿基于 Webkit 的浏览器,然而并没有GUI。作为一个开发者,可以调用 PhantomJS 提供的特定方法在页面上执行代码。由于它的行为像浏览器,网页上的脚本就像在一个普通的浏览器中运行。为了从我们的页面获取数据,要使用 PhantomJS-Node,这是一个很小的开源项目,它将 PhantomJS 与NodeJS 桥接起来。此模块默默把 PhantomJS 作为一个子进程运行。安装 PhantomJS在安装
NPM 模块之前,必须安装 PhantomJS。但安装和构建 PhantomJS 可能有点棘手。首先,去
并为操作系统下载相应的版本。我是Mac OSX。下载后,将其解压到某个位置,例如/ Applications /。接下来,您要将其添加到PATH:sudo ln -s /Applications/phantomjs-1.5.0/bin/phantomjs /usr/local/bin/把 1.5.0 替换为你下载的 PhantomJS 版本。请注意,并非所有系统都具有/ usr / local / bin /。一些系统将有:/ usr / bin /,/ bin /或usr / X11 / bin。对于 Windows 用户,看这里的
教程。如果你打开终端,输入 phantomjs 并且没有任何错误,就安装完成了。如果你不想编辑 PATH,记下你解压 PhantomJS 的地方,我会在下一节中展示另一种设置方法,虽然我建议你编辑 PATH。安装 PhantomJS-Node设置 PhantomJS-Node 就简单多了。如果已经安装了 NodeJS,可以通过 npm 来安装它:npm install phantom如果你在前一节安装 PhantomJS 的时候没有编辑 PATH,可以去 npm pull 下来的 phantom/ 目录,在 phantom.js 里编辑这一行。ps = child.spawn('phantomjs', args.concat([__dirname + '/shim.js', port]));把路径改为:ps = child.spawn('/path/to/phantomjs-1.5.0/bin/phantomjs', args.concat([__dirname + '/shim.js', port]));完成后,可以运行这段代码进行测试:var phantom = require('phantom');
phantom.create(function(ph) {
return ph.createPage(function(page) {
return page.open("http://www.google.com", function(status) {
console.log("opened google? ", status);
return page.evaluate((function() {
return document.
}), function(result) {
console.log('Page title is ' + result);
return ph.exit();
在命令行运行它应该会有如下输出:opened google?
Page title is Google
如果正确得到了,就已经设置完成。如果没有,在现在评论一下我会试着帮你解决!使用 PhantomJS-Node为了让你更容易,我已经在下载中包含了一个名为 phantomServer.js 的 JS 文件,使用了一些 PhantomJS 的 API 来加载网页。等待 5 秒后执行 JavaScript 来爬取页面。你可以通过导航到该目录并在终端中使用以下命令来运行它:node phantomServer.js我将概述一下它在这里是如何工作的。首先,我们需要 PhantomJS:var phantom = require('phantom’);接下来,利用
实现一些方法。也就是说,我们创建一个实例页面,然后调用open()方法:phantom.create(function(ph) {
return ph.createPage(function(page) {
//From here on in, we can use PhantomJS' API methods
return page.open("http://tilomitra.com/repository/screenscrape/ajax.html",
function(status) {
//The page is now open
console.log("opened site? ", status);
页面打开后,我们可以注入一些 JavaScript 到页面上。通过 page.injectJS() 方法来注入 jQuery:phantom.create(function(ph) {
return ph.createPage(function(page) {
return page.open("http://tilomitra.com/repository/screenscrape/ajax.html", function(status) {
console.log("opened site? ", status);
page.injectJs('http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js', function() {
//jQuery Loaded
//We can use things like $("body").html() in here.
jQuery 现在加载好了,但我们不知道页面上的动态内容是否加载完毕。为了解决这个问题,我通常会把我的爬虫代码放在一个 setTimeout() 函数中,在特定时间间隔后执行。如果你想要一个更灵活的方案,PhantomJS API 允许监听和模仿指定事件。看一下简单的例子:setTimeout(function() {
return page.evaluate(function() {
//Get what you want from the page using jQuery.
//A good way is to populate an object with all the jQuery commands that you need and then return the object.
var h2Arr = [], //array that holds all html for h2 elements
pArr = []; //array that holds all html for p elements
//Populate the two arrays
$('h2').each(function() {
h2Arr.push($(this).html());
$('p').each(function() {
pArr.push($(this).html());
//Return this data
h2: h2Arr,
}, function(result) {
console.log(result); //Log out the data.
ph.exit();
全部放在一起后,我们的 phantomServer.js 看起来会像这样:var phantom = require('phantom');
phantom.create(function(ph) {
return ph.createPage(function(page) {
return page.open("http://tilomitra.com/repository/screenscrape/ajax.html", function(status) {
console.log("opened site? ", status);
page.injectJs('http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js', function() {
//jQuery Loaded.
//Wait for a bit for AJAX content to load on the page. Here, we are waiting 5 seconds.
setTimeout(function() {
return page.evaluate(function() {
//Get what you want from the page using jQuery. A good way is to populate an object with all the jQuery commands that you need and then return the object.
var h2Arr = [],
pArr = [];
$('h2').each(function() {
h2Arr.push($(this).html());
$('p').each(function() {
pArr.push($(this).html());
h2: h2Arr,
}, function(result) {
console.log(result);
ph.exit();
这个实现有一些粗糙、无组织性,但重点找到了。使用 PhantomJS,能够抓取具有动态内容的页面!控制台应输出以下内容:→ node phantomServer.js
opened site?
{ h2: [ 'Article 1', 'Article 2', 'Article 3' ],
[ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'Ut sed nulla turpis, in faucibus ante. Vivamus ut malesuada est. Curabitur vel enim eget purus pharetra tempor id in tellus.',
'Curabitur euismod hendrerit quam ut euismod. Ut leo sem, viverra nec gravida nec, tristique nec arcu.' ] }
总结在本教程中讲了实现网络爬虫的两种不同方式。抓取静态网页可以用 YQL,很容易设置和使用。另一方面,对于动态站点可以用 PhantomJS。设置起来更麻烦,但提供更多功能。记住:也可以使用PhantomJS 抓取静态网站!如果你对这个话题有任何疑问,可以在下面随时询问,我会尽我所能帮助你。94 条评论分享收藏感谢收起如何使用nodejs做爬虫程序? - 知乎250被浏览<strong class="NumberBoard-itemValue" title="2分享邀请回答github.com/alsotang/node-lessons/tree/master/lesson3174 条评论分享收藏感谢收起/**
* 爬虫抓取主入口
const start = async() =& {
let booklistRes = await bookListInit();
if (!booklistRes) {
logger.warn('书籍列表抓取出错,程序终止...');
logger.info('书籍列表抓取成功,现在进行书籍章节抓取...');
let chapterlistRes = await chapterListInit();
if (!chapterlistRes) {
logger.warn('书籍章节列表抓取出错,程序终止...');
logger.info('书籍章节列表抓取成功,现在进行书籍内容抓取...');
let contentListRes = await contentListInit();
if (!contentListRes) {
logger.warn('书籍章节内容抓取出错,程序终止...');
logger.info('书籍内容抓取成功');
// 开始入口
if (typeof bookListInit === 'function' && typeof chapterListInit === 'function') {
// 开始抓取
引入的 bookListInit ,chapterListInit,contentListInit,
三个方法booklist.js/**
* 初始化方法 返回抓取结果 true 抓取成果 false 抓取失败
const bookListInit = async() =& {
logger.info('抓取书籍列表开始...');
const pageUrlList = getPageUrlList(totalListPage, baseUrl);
let res = await getBookList(pageUrlList);
chapterlist.js/**
* 初始化入口
const chapterListInit = async() =& {
const list = await bookHelper.getBookList(bookListModel);
if (!list) {
logger.error('初始化查询书籍目录失败');
logger.info('开始抓取书籍章节列表,书籍目录共:' + list.length + '条');
let res = await asyncGetChapter(list);
content.js/**
* 初始化入口
const contentListInit = async() =& {
//获取书籍列表
const list = await bookHelper.getBookLi(bookListModel);
if (!list) {
logger.error('初始化查询书籍目录失败');
const res = await mapBookList(list);
if (!res) {
logger.error('抓取章节信息,调用 getCurBookSectionList() 进行串行遍历操作,执行完成回调出错,错误信息已打印,请查看日志!');
内容抓取的思考书籍目录抓取其实逻辑非常简单,只需要使用async.mapLimit做一个遍历就可以保存数据了,但是我们在保存内容的时候 简化的逻辑其实就是 遍历章节列表 抓取链接里的内容。但是实际的情况是链接数量多达几万 我们从内存占用角度也不能全部保存到一个数组中,然后对其遍历,所以我们需要对内容抓取进行单元化。普遍的遍历方式 是每次查询一定的数量,来做抓取,这样缺点是只是以一定数量做分类,数据之间没有关联,以批量方式进行插入,如果出错 则容错会有一些小问题,而且我们想一本书作为一个集合单独保存会遇到问题。因此我们采用第二种就是以一个书籍单元进行内容抓取和保存。这里使用了 `async.mapLimit(list, 1, (series, callback) =& {})`这个方法来进行遍历,不可避免的用到了回调,感觉很恶心。async.mapLimit()的第二个参数可以设置同时请求数量。/*
* 内容抓取步骤:
* 第一步得到书籍列表, 通过书籍列表查到一条书籍记录下 对应的所有章节列表,
* 第二步 对章节列表进行遍历获取内容保存到数据库中
* 第三步 保存完数据后 回到第一步 进行下一步书籍的内容抓取和保存
* 初始化入口
const contentListInit = async() =& {
//获取书籍列表
const list = await bookHelper.getBookList(bookListModel);
if (!list) {
logger.error('初始化查询书籍目录失败');
const res = await mapBookList(list);
if (!res) {
logger.error('抓取章节信息,调用 getCurBookSectionList() 进行串行遍历操作,执行完成回调出错,错误信息已打印,请查看日志!');
* 遍历书籍目录下的章节列表
* @param {*} list
const mapBookList = (list) =& {
return new Promise((resolve, reject) =& {
async.mapLimit(list, 1, (series, callback) =& {
let doc = series._
getCurBookSectionList(doc, callback);
}, (err, result) =& {
if (err) {
logger.error('书籍目录抓取异步执行出错!');
logger.error(err);
reject(false);
resolve(true);
* 获取单本书籍下章节列表 调用章节列表遍历进行抓取内容
* @param {*} series
* @param {*} callback
const getCurBookSectionList = async(series, callback) =& {
let num = Math.random() * 1000 + 1000;
await sleep(num);
let key = series.
const res = await bookHelper.querySectionList(chapterListModel, {
if (!res) {
logger.error('获取当前书籍: ' + series.bookName + ' 章节内容失败,进入下一部书籍内容抓取!');
callback(null, null);
//判断当前数据是否已经存在
const bookItemModel = getModel(key);
const contentLength = await bookHelper.getCollectionLength(bookItemModel, {});
if (contentLength === res.length) {
logger.info('当前书籍:' + series.bookName + '数据库已经抓取完成,进入下一条数据任务');
callback(null, null);
await mapSectionList(res);
callback(null, null);
数据抓取完了 怎么保存是个问题这里我们通过key 来给数据做分类,每次按照key来获取链接,进行遍历,这样的好处是保存的数据是一个整体,现在思考数据保存的问题 1 可以以整体的方式进行插入 优点 : 速度快 数据库操作不浪费时间。 缺点 : 有的书籍可能有几百个章节 也就意味着要先保存几百个页面的内容再进行插入,这样做同样很消耗内存,有可能造成程序运行不稳定。 2可以以每一篇文章的形式插入数据库。优点 : 页面抓取即保存的方式 使得数据能够及时保存,即使后续出错也不需要重新保存前面的章节, 缺点 : 也很明显 就是慢 ,仔细想想如果要爬几万个页面 做 几万次*N 数据库的操作 这里还可以做一个缓存器一次性保存一定条数 当条数达到再做保存这样也是一个不错的选择。/**
* 遍历单条书籍下所有章节 调用内容抓取方法
* @param {*} list
const mapSectionList = (list) =& {
return new Promise((resolve, reject) =& {
async.mapLimit(list, 1, (series, callback) =& {
let doc = series._
getContent(doc, callback)
}, (err, result) =& {
if (err) {
logger.error('书籍目录抓取异步执行出错!');
logger.error(err);
reject(false);
const bookName = list[0].bookN
const key = list[0].
// 以整体为单元进行保存
saveAllContentToDB(result, bookName, key, resolve);
//以每篇文章作为单元进行保存
// logger.info(bookName + '数据抓取完成,进入下一部书籍抓取函数...');
// resolve(true);
两者各有利弊,这里我们都做了尝试。 准备了两个错误保存的集合,errContentModel, errorCollectionModel,在插入出错时 分别保存信息到对应的集合中,二者任选其一即可。增加集合来保存数据的原因是 便于一次性查看以及后续操作, 不用看日志。(PS ,其实完全用 errorCollectionModel 这个集合就可以了
,errContentModel这个集合可以完整保存章节信息)//保存出错的数据名称
const errorSpider = mongoose.Schema({
chapter: String,
section: String,
url: String,
key: String,
bookName: String,
author: String,
// 保存出错的数据名称 只保留key 和 bookName信息
const errorCollection = mongoose.Schema({
key: String,
bookName: String,
我们将每一条书籍信息的内容 放到一个新的集合中,集合以key来进行命名。总结 写这个项目 其实主要的难点在于程序稳定性的控制,容错机制的设置,以及错误的记录,目前这个项目基本能够实现直接运行 一次性跑通整个流程。 但是程序设计也肯定还存在许多问题 ,欢迎指正和交流。彩蛋写完这个项目 做了一个基于React开的前端网站用于页面浏览 和一个基于koa2.x开发的服务端, 整体技术栈相当于是 React + Redux + Koa2 ,前后端服务是分开部署的,各自独立可以更好的去除前后端服务的耦合性,比如同一套服务端代码,不仅可以给web端 还可以给 移动端 ,app 提供支持。目前整个一套还很简陋,但是可以满足基本的查询浏览功能。希望后期有时间可以把项目变得更加丰富。本项目地址
地址 : [guwen-spider]()对应前端 React + Redux + semantic-ui
地址 : [guwen-react]()对应Node端 Koa2.2 + mongoose
地址 : [guwen-node]()项目挺简单的 ,但是多了一个学习和研究 从前端到服务端的开发的环境。感谢阅读!以上です3添加评论分享收藏感谢收起没有更多推荐了,
不良信息举报
举报内容:
node爬虫进阶之——登录
举报原因:
原文地址:
原因补充:
最多只允许输入30个字
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!没有更多推荐了,
不良信息举报
举报内容:
node爬虫(免登陆)
举报原因:
原文地址:
原因补充:
最多只允许输入30个字
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!怎么把nodeJS获取的爬虫数据放到html页面
控制台输出了,不过我想用静态html网页展示出来怎么弄呢?
var http = require('https');
var url = 'https://www.baidu.com/';
http.get(url,function(res){
var html = '';
res.on('data',function(data){
res.on('end',function(){
console.log(html);
}).on('error',function(){
console.log("获取出错");
浏览 602回答 1
var http = require(&#39;https&#39;);
var fs = require(&#39;fs&#39;);
var url = &#39;https://www.baidu.com/&#39;;
http.get(url,function(res){
var html = &#39;&#39;;
res.on(&#39;data&#39;,function(data){
res.on(&#39;end&#39;,function(){
console.log(html);
fs.writeFile(&#39;./baidu.html&#39;,html,function(err){
if (err) throw new Error(&#39;写文件失败&#39; + err);
console.log(&#39;成功写入文件&#39;);
res.on(&#39;error&#39;,function(){
console.log(&获取出错&);
});帮你稍微改了下。
随时随地看视频

我要回帖

更多关于 nodejs 爬虫 的文章

 

随机推荐