';
$('.item-style').append(btn_text);
// 绑定事件
$('.return-button').on('click', function(){
$('.item-style').html('
');
$('.table_total').html('');
$('#page').html('');
});
// 采集字段表格
var sel_table_head = '
';
$('.item-style').append(sel_table_head + sel_table_body + sel_table_tail);
}
// 删除配置
function delConf (obj) {
var name = $(obj).find('h4 a').text();
// 清空模态框
$('.fade').html('');
// var reg = /\d+/;
// var num = reg.exec(name);
// 加载模态框
var text = + '
';
$('.fade').html(text);
// 移除事件
$("#delete_items").off('click');
// 绑定事件
$("#delete_items").on('click', function(){
var item_name = ($(this).parent('div').prev().text()).replace(/配置名称: /, '');
if (item_name){
$.ajax({
url: window.location.origin + '/items/delete/',
method: 'delete',
dataType: 'json',
async: true,
data: {'name': item_name},
success: function(data){
$('.fade').modal('hide');
$('.fade').html('');
if (data.status === 10000){
var text = + '
';
$('.fade').html(text);
setTimeout( function () {
$('.fade').modal('show');
}, 800
);
// 请求首页
getItemList(1, getItemListSuccess);
// 分页列表
get_count();
}else{
$('.fade').modal('hide');
$('.fade').html('');
var text = + '
';
$('.fade').html(text);
setTimeout( function () {
$('.fade').modal('show');
}, 800
);
// 请求首页
getItemList(1, getItemListSuccess);
// 分页列表
get_count();
}
},
error: function (e) {
}
})
} else{
alert('获取配置名称错误');
}
});
// 显示模态框
$('.fade').modal('show');
}
// 生成css选择器
function cssSel(obj) {
var dom = $(obj).get(0);
var path = "";
for (; dom && dom.nodeType == 1; dom = dom.parentNode) {
var index = 1;
for (var sib = dom.previousSibling; sib; sib = sib.previousSibling) {
if (sib.nodeType == 1 && sib.tagName == dom.tagName){
index++;
}
}
var xname = dom.tagName.toLowerCase();
if (dom.id) {
xname += "[@id=\"" + dom.id + "\"]";
} else {
if (index > 0)
xname += "[" + index + "]";
}
path = "/" + xname + path;
}
path = path.replace("html[1]/body[1]/","html/body/");
// 开始转换 xpath 为 css path
// 转换 // 为 " "
xpath = path.replace(/\/\//g, " ");
// 转换 / 为 >
xpath = xpath.replace(/\//g, ">");
// 转换 [elem] 为 :eq(elem) : 规则 -1
xpath = xpath.replace(/\[([^@].*?)\]/ig, function(matchStr,xPathIndex){
var cssPathIndex = parseInt(xPathIndex)-1;
return ":eq(" + cssPathIndex + ")";
});
// 1.2 版本后需要删除@
xpath = xpath.replace(/\@/g, "");
// 去掉第一个 >
xpath = xpath.substr(1);
// 返回jQuery元素
return xpath;
}
// 移动、点击事件
function mouse_event() {
// 左上角弹窗
$('.show-element').css('display', 'block');
// 鼠标移动事件
$('*:not(.select-view)').bind({
'mousemove': function (event) {
event.stopPropagation();
var cssName = cssSel($(this));
$(this).addClass('start-sel');
// console.log(cssName);
$(this).attr('title', cssName);
},
'mouseout': function (event) {
event.stopPropagation();
$(this).toggleClass('start-sel');
}
});
// 鼠标点击事件
$('*').bind({
'mousedown': function(event){
// 弹出框
event.preventDefault();
$('.select-view').css('display', 'block');
$('.select-view').unbind('mousemove').unbind('mouseout').removeClass('start-sel');
$(".select-view").find("*").unbind('mousemove').unbind('mouseout').removeClass('start-sel');
// $('.select-view').preventDefault();
// 取消默认事件、冒泡
// $('.select-view').stopPropagation();
event.stopPropagation();
$(this).addClass('end-sel');
}
});
}
function test() {
// 单击打开
$('.light.io-cursor-add-CHFG').on('click', function (e) {
// console.log(getElementXpath(e.toElement));
var tabs = "";
var ids = "";
var classs = "";
var hrefs = "";
var tabs = $(this).prop("tagName");
tabs = tabs.toLowerCase();
var ids = $(this).attr("id");
var classs = $(this).attr("class");
var hrefs = $(this).attr("href");
console.log(tabs + ';' + ids + ';' + classs + ';' + hrefs);
var content = "";
// 鼠标点击+-切换
if ($(this).hasClass('io-cursor-add-CHFG')) {
$(this).removeClass('io-cursor-add-CHFG').addClass('io-cursor-delete-CHFG');
var contents = $(this).prop('outerHTML');//点击获取数据
} else {
$(this).removeClass('io-cursor-delete-CHFG').addClass('io-cursor-add-CHFG');
content = "";
}
//滑动+点击使选中框
if ($(this).hasClass('light')) {
$(this).removeClass('light').addClass('light-green');
} else {
$(this).removeClass('light-green').addClass('light');
}
return false
});
$(this).attr()
}
// 浮动显示框
function float_frame() {
$('#button-position').on('click', function(){
// 左侧
if($(this).attr('class').indexOf('right') !== -1){
$(this).attr('class', 'glyphicon glyphicon-arrow-left button');
$('.float-frame').css({'width': $('.main').width() + $('.span2').width()});
$('.show-element').css({'float': 'right'});
// 右侧
}else{
$(this).attr('class', 'glyphicon glyphicon-arrow-right button');
$('.show-element').css({'float': 'left'});
}
})
}
================================================
FILE: server/static/js/work.js
================================================
/**
* Created by Administrator on 2017/9/14.
*/
$(function(){
"use strict";
// 下方选择栏样式
// select_frame();
// frame样式
$('iframe').css({"width": '100%', "height": "580px"});
// 浮动显示框
// float_frame();
// 模态框方法
// new_modal(jQuery);
});
// 选择字段
function iframe_mouse_event(){
// 禁用按钮
$('div.sidebar > button').attr('disabled', 'disabled');
window.collect.mouse_event();
}
// 下方选择栏
function select_frame() {
var src_posi_Y = 0, dest_posi_Y = 0, move_Y = 0, is_mouse_down = false, destHeight = 200;
$(".expander").mousedown(function(e){
src_posi_Y = e.pageY;
is_mouse_down = true;
});
$(document).bind("click mouseup",function(e){
if(is_mouse_down){
is_mouse_down = false;
}
})
.mousemove(function(e){
dest_posi_Y = e.pageY;
move_Y = src_posi_Y - dest_posi_Y;
src_posi_Y = dest_posi_Y;
destHeight = $(".sidebar").height() + move_Y;
if(is_mouse_down){
$(".sidebar").css("height", destHeight > 30 ? destHeight : 30);
}
});
}
// 保存选择
function save_select() {
if ($('#select-name').val() != ''){
// 列表多选
if ($('#multiple').attr('disabled')){
// 字段名查重
var now_field_name = [];
$('#save-sel').find('tr').each(function(){
now_field_name.push($(this).find('td:first').text());
});
if (now_field_name.indexOf($('#select-name').val()) === -1) {
} else {
$('#select-name').css({'border-color': '#F66495'});
$('#select-name').attr('data-content', '名称与已有字段重复');
$('#select-name').popover('show');
$('#select-name').focus(function(){
$('#select-name').popover('hide');
});
$('#select-name').change(function(){
$('#select-name').css({'border-color': '#FFFFFF'});
});
}
}
if ($('#multiple').is(':checked')){
return multiple_select();
}
// 字段名查重
var now_field_name = [];
$('#save-sel').find('tr').each(function(){
now_field_name.push($(this).find('td:first').text());
});
if (now_field_name.indexOf($('#select-name').val()) === -1) {
var sel_table_body = `
`;
$('#save-sel').append(sel_table_body);
$('.select-view').hide();
$('.show-element').hide();
}else {
$('#select-name').css({'border-color': '#F66495'});
$('#select-name').attr('data-content', '名称与已有字段重复');
$('#select-name').popover('show');
$('#select-name').focus(function(){
$('#select-name').popover('hide');
});
$('#select-name').change(function(){
$('#select-name').css({'border-color': '#FFFFFF'});
});
}
} else {
$('#select-name').css({'border-color': '#F66495'});
$('#select-name').attr('data-content', '名称不为空');
$('#select-name').popover('show');
$('#select-name').focus(function(){
$('#select-name').popover('hide');
});
$('#select-name').change(function(){
$('#select-name').css({'border-color': '#FFFFFF'});
});
}
}
// 多列表保存
function multiple_select() {
// 列表选择
$('#multiple').attr({'disabled': 'disabled', 'checked': 'checked'});
$('.show-element').prepend('
').css('display', 'none');
$('.select-view').css('display','none');
iframe_mouse_event();
}
// 删除选择
function save_del(obj) {
$('#modal-temp').new_modal({
backdrop: false, // 点击模态框外部时不会关闭模态框
title: '删除', // 模态框中标题
content: '确定删除 ' + $(obj).parent().siblings().eq(0).text() + ' 字段?', // 模态框中内容
enter_function: false // 确认键绑定函数
});
$('#modal-temp').find('button').eq(-1).removeAttr("data-dismiss").attr({"id": "save_del_confirm"});
$('#save_del_confirm').on('click', function () {
var iframe_css = $(obj).parent().prev().prev().prev().text();
var iframe_elem = $(window.frames["collect"].document).find(iframe_css.split('::')[0]);
// 移除边框效果
iframe_elem.removeClass('end-sel');
$('#modal-temp').modal('hide');
// 删除表格
$(obj).parent().parent().remove();
});
$('#modal-temp').modal();
}
// 浮动显示框
function float_frame() {
$('.float-frame').css('top', $('body > nav').height());
$('#button-position').on('click', function () {
// 左侧
if ($(this).attr('class').indexOf('right') !== -1) {
$(this).attr('class', 'glyphicon glyphicon-arrow-left button');
$('.float-frame').css({'right': '0px'});
// 右侧
} else {
$(this).attr('class', 'glyphicon glyphicon-arrow-right button');
$('.float-frame').css('right', '');
}
});
}
// 模态框
function new_modal($) {
$.fn.new_modal = function (options) {
options = $.extend({}, $.fn.new_modal.defaults, options || {});
if (options.backdrop) {
$(this).attr("data-backdrop", "false");
}
$(this).attr({
"class": "modal fade"
});
let enter_content;
if (!options.enter_function) {
enter_content = `
`
);
return this;
};
$.fn.new_modal.defaults = {
backdrop: true, // 点击模态框外部时不会关闭模态框
title: '默认标题', // 模态框中标题
content: '默认内容', // 模态框中内容
enter_function: false // 确认键绑定函数
};
}
================================================
FILE: server/static/js/work_iframe.js
================================================
/**
* Created by Administrator on 2017/10/23.
*/
$(function (){
location.reload();
});
// 生成css选择器
function cssSel(obj) {
// 判断是否为同一对象
var sameJqueryObject = function (a, b){
if (a.is(b) && a.length === b.length){
return true;
}else{
return false;
}
};
// class id选择器
var domSel = function (dom){
// 获取属性
var domName = dom.localName.toLowerCase();
if (dom.id){
var domId = '#' + dom.id;
return domName + domId;
}
if (dom.className){
var domClass = dom.className.split(" ").filter(val => val != 'end-sel' && val != 'start-sel' && val != 'hover').map(val => '.'+ val).join('');
return domName + domClass;
}
return domName;
};
// dom对象
var dom = $(obj).get(0);
var path = "";
// 父元素节点
for (; dom && dom.nodeType === 1; dom = dom.parentNode) {
var index = 1;
// 之前的同胞节点
for (var sib = dom.previousSibling; sib; sib = sib.previousSibling) {
// 同一标签下
if (sib.nodeType === 1) {
index++;
}
}
// 之后的同胞节点
var flag = 0;
for (var nex = $(dom).next().get(0); nex; nex = $(nex).next().get(0)) {
if (nex.nodeType === 1) {
flag = 1;
break;
}
}
// 存在兄弟节点
var thisobj = '';
if ((flag === 1) || (index > 1 && flag === 0)) {
thisobj = domSel(dom) + `:nth-child(${index})`;
// 不存在兄弟节点
} else {
thisobj = domSel(dom);
}
path = thisobj + path;
if (sameJqueryObject($(obj), $(path))) {
return path;
} else {
path = ' ' + path;
}
}
return undefined;
}
// 移动、点击事件
function mouse_event() {
// 左上角弹窗
$('.show-element', parent.document).css('display', 'block');
$('*').bind({
// 鼠标移动事件
'mousemove': function (event) {
// 阻止父类冒泡和默认事件
event.stopPropagation();
event.preventDefault();
var cssName = cssSel($(event.target));
$(event.target).addClass('start-sel');
// 写入弹窗
$('#path', parent.document).html(cssName);
var content = `
content:
${$(event.target).contents().filter(function(){ return this.nodeType === 3; }).text()}
`;
$('#attr', parent.document).html(content);
var attr_str = $(event.target).clone();
attr_str.children().remove();
attr_str = attr_str[0].outerHTML;
label_content = attr_str.match(/\w+\s*=\s*["|'].*?["|']/g);
var attr_content = '';
if (label_content && label_content.length > 0){
for (var i in label_content){
attr_content = `
${/(\w+)\s*=\s*["|\'].*?["|\']/g.exec(label_content[i])[1]}:
${/\w+\s*=\s*(["|\'].*?["|\'])/g.exec(label_content[i])[1].replace(/"/g, '')}
`;
$('#attr', parent.document).append(attr_content);
}
}
},
// 鼠标移除事件
'mouseout': function (event) {
event.stopPropagation();
$(event.target).toggleClass('start-sel', parent.document);
$(event.target).removeAttr('title', parent.document);
$('#attr', parent.document).html('');
}
});
// 鼠标点击事件
$(this).bind({'click': function(event){
// 列表第二次选择
if ($('#multiple', parent.document).attr('disabled')){
$(event.target).removeClass('start-sel').addClass('end-sel');
$(this).unbind('click');
$('*').unbind('mousemove').unbind('mouseout');
var first = $('#select-input', parent.document).val();
$('.show-element p', parent.document).remove();
$('.select-view', parent.document).css('display', 'block');
// 共有父类
var result = common_element(first, $('#path', parent.document).text());
console.log('result:', result);
if (!result){
// 隐藏窗口
$('.select-view ', parent.document).css('display','none');
$('.show-element', parent.document).css('display', 'none');
// 取消边框样式
$($('#path', parent.document).text()).removeClass('end-sel');
// 取消显示
$('#select-name').popover('hide');
// 解除按钮禁用
$('div.sidebar > button', parent.document).removeAttr('disabled');
return false;
}
// 构造表单
content = {'name': $('#select-name', parent.document).val(), 'input': result};
selectViewForm();
// 解除按钮禁用
$('div.sidebar > button', parent.document).removeAttr('disabled');
return false;
// 非列表选择
} else {
// 阻止事件冒泡
// event.stopPropagation();
$(event.target).removeClass('start-sel').addClass('end-sel');
// 解除事件
$(this).unbind('click');
$('*').unbind('mousemove').unbind('mouseout');
// 清空数据
$('#select-name', parent.document).val('');
$('#select-input', parent.document).val('');
// 构造弹出表单
content = {'input': $('#path', parent.document).text()};
selectViewForm(content);
// 启动中间弹窗
$('.select-view', parent.document).css('display', 'block');
// 绑定清除背景、隐藏窗口按钮
$('body > div.select-view > div:nth-child(1) > button', parent.document).on('click', function(){
// 隐藏窗口
$('.select-view ', parent.document).css('display','none');
$('.show-element', parent.document).css('display', 'none');
// 取消边框样式
$($('#path', parent.document).text()).removeClass('end-sel');
// 取消显示
$('#select-name').popover('hide');
});
// 解除按钮禁用
$('div.sidebar > button', parent.document).removeAttr('disabled');
// 阻止点击事件
return false;
}
}
});
}
// 构造弹出框表单
function selectViewForm(content) {
content.name ? $('#select-name').val(content.name) : $('#select-name').val('');
var sel_content = {};
for (var i=0;i<$('#attr .attribute', parent.document).length;i++){
sel_content[$('#attr .attribute', parent.document).eq(i).find('span').eq(0).text().replace(/: /g, '')] =
$('#attr .attribute', parent.document).eq(i).find('span').eq(1).text();
}
$('#select-form', parent.document).html('
');
$('#select-value', parent.document).html(sel_content['content']);
content.input ? $('#select-input', parent.document).val(content.input) : $('#select-input', parent.document).val($('#path', parent.document).text());
for (var i in sel_content){
if (i !== 'content'){
$('#select-form', parent.document).append('
');
}
}
$('#select-form', parent.document).change(function(){
$('#select-value', parent.document).html(sel_content[$(this).children('option:selected').val()]);
$('#select-input', parent.document).val($('#path', parent.document).text());
});
}
function common_element(first, second){
/*
1.同列表元素的dom层级一致
2.同列表元素至少一层父元素不一致
*/
var arr_1 = first.split(' ').map(val => val.split(':'))
var arr_2 = second.split(' ').map(val => val.split(':'))
// 同列表元素判断
if (arr_1.length == arr_2.length){
var global_element = [];
$.each(arr_1, function(index, value){
if (arr_1[index].toString() == arr_2[index].toString()){
global_element.push(arr_1[index].join(':'));
}
else{
if (arr_1[index][0] == arr_2[index][0]){
global_element.push(arr_1[index][0]);
}
}
})
var global_father = global_element.join(' ');
if ($(first).is($(global_father)) && $(second).is($(global_father))){
return global_father;
} else{
return false;
}
} else {
return false;
}
}
================================================
FILE: server/static/lib/ICanHaz.js
================================================
/*!
ICanHaz.js version 0.10.2 -- by @HenrikJoreteg
More info at: http://icanhazjs.com
*/
(function () {
/*
mustache.js — Logic-less templates in JavaScript
See http://mustache.github.com/ for more info.
*/
var Mustache = function () {
var _toString = Object.prototype.toString;
Array.isArray = Array.isArray || function (obj) {
return _toString.call(obj) == "[object Array]";
}
var _trim = String.prototype.trim, trim;
if (_trim) {
trim = function (text) {
return text == null ? "" : _trim.call(text);
}
} else {
var trimLeft, trimRight;
// IE doesn't match non-breaking spaces with \s.
if ((/\S/).test("\xA0")) {
trimLeft = /^[\s\xA0]+/;
trimRight = /[\s\xA0]+$/;
} else {
trimLeft = /^\s+/;
trimRight = /\s+$/;
}
trim = function (text) {
return text == null ? "" :
text.toString().replace(trimLeft, "").replace(trimRight, "");
}
}
var escapeMap = {
"&": "&",
"<": "<",
">": ">",
'"': '"',
"'": '''
};
function escapeHTML(string) {
return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) {
return escapeMap[s] || s;
});
}
var regexCache = {};
var Renderer = function () {};
Renderer.prototype = {
otag: "{{",
ctag: "}}",
pragmas: {},
buffer: [],
pragmas_implemented: {
"IMPLICIT-ITERATOR": true
},
context: {},
render: function (template, context, partials, in_recursion) {
// reset buffer & set context
if (!in_recursion) {
this.context = context;
this.buffer = []; // TODO: make this non-lazy
}
// fail fast
if (!this.includes("", template)) {
if (in_recursion) {
return template;
} else {
this.send(template);
return;
}
}
// get the pragmas together
template = this.render_pragmas(template);
// render the template
var html = this.render_section(template, context, partials);
// render_section did not find any sections, we still need to render the tags
if (html === false) {
html = this.render_tags(template, context, partials, in_recursion);
}
if (in_recursion) {
return html;
} else {
this.sendLines(html);
}
},
/*
Sends parsed lines
*/
send: function (line) {
if (line !== "") {
this.buffer.push(line);
}
},
sendLines: function (text) {
if (text) {
var lines = text.split("\n");
for (var i = 0; i < lines.length; i++) {
this.send(lines[i]);
}
}
},
/*
Looks for %PRAGMAS
*/
render_pragmas: function (template) {
// no pragmas
if (!this.includes("%", template)) {
return template;
}
var that = this;
var regex = this.getCachedRegex("render_pragmas", function (otag, ctag) {
return new RegExp(otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" + ctag, "g");
});
return template.replace(regex, function (match, pragma, options) {
if (!that.pragmas_implemented[pragma]) {
throw({message:
"This implementation of mustache doesn't understand the '" +
pragma + "' pragma"});
}
that.pragmas[pragma] = {};
if (options) {
var opts = options.split("=");
that.pragmas[pragma][opts[0]] = opts[1];
}
return "";
// ignore unknown pragmas silently
});
},
/*
Tries to find a partial in the curent scope and render it
*/
render_partial: function (name, context, partials) {
name = trim(name);
if (!partials || partials[name] === undefined) {
throw({message: "unknown_partial '" + name + "'"});
}
if (!context || typeof context[name] != "object") {
return this.render(partials[name], context, partials, true);
}
return this.render(partials[name], context[name], partials, true);
},
/*
Renders inverted (^) and normal (#) sections
*/
render_section: function (template, context, partials) {
if (!this.includes("#", template) && !this.includes("^", template)) {
// did not render anything, there were no sections
return false;
}
var that = this;
var regex = this.getCachedRegex("render_section", function (otag, ctag) {
// This regex matches _the first_ section ({{#foo}}{{/foo}}), and captures the remainder
return new RegExp(
"^([\\s\\S]*?)" + // all the crap at the beginning that is not {{*}} ($1)
otag + // {{
"(\\^|\\#)\\s*(.+)\\s*" + // #foo (# == $2, foo == $3)
ctag + // }}
"\n*([\\s\\S]*?)" + // between the tag ($2). leading newlines are dropped
otag + // {{
"\\/\\s*\\3\\s*" + // /foo (backreference to the opening tag).
ctag + // }}
"\\s*([\\s\\S]*)$", // everything else in the string ($4). leading whitespace is dropped.
"g");
});
// for each {{#foo}}{{/foo}} section do...
return template.replace(regex, function (match, before, type, name, content, after) {
// before contains only tags, no sections
var renderedBefore = before ? that.render_tags(before, context, partials, true) : "",
// after may contain both sections and tags, so use full rendering function
renderedAfter = after ? that.render(after, context, partials, true) : "",
// will be computed below
renderedContent,
value = that.find(name, context);
if (type === "^") { // inverted section
if (!value || Array.isArray(value) && value.length === 0) {
// false or empty list, render it
renderedContent = that.render(content, context, partials, true);
} else {
renderedContent = "";
}
} else if (type === "#") { // normal section
if (Array.isArray(value)) { // Enumerable, Let's loop!
renderedContent = that.map(value, function (row) {
return that.render(content, that.create_context(row), partials, true);
}).join("");
} else if (that.is_object(value)) { // Object, Use it as subcontext!
renderedContent = that.render(content, that.create_context(value),
partials, true);
} else if (typeof value == "function") {
// higher order section
renderedContent = value.call(context, content, function (text) {
return that.render(text, context, partials, true);
});
} else if (value) { // boolean section
renderedContent = that.render(content, context, partials, true);
} else {
renderedContent = "";
}
}
return renderedBefore + renderedContent + renderedAfter;
});
},
/*
Replace {{foo}} and friends with values from our view
*/
render_tags: function (template, context, partials, in_recursion) {
// tit for tat
var that = this;
var new_regex = function () {
return that.getCachedRegex("render_tags", function (otag, ctag) {
return new RegExp(otag + "(=|!|>|&|\\{|%)?([^#\\^]+?)\\1?" + ctag + "+", "g");
});
};
var regex = new_regex();
var tag_replace_callback = function (match, operator, name) {
switch(operator) {
case "!": // ignore comments
return "";
case "=": // set new delimiters, rebuild the replace regexp
that.set_delimiters(name);
regex = new_regex();
return "";
case ">": // render partial
return that.render_partial(name, context, partials);
case "{": // the triple mustache is unescaped
case "&": // & operator is an alternative unescape method
return that.find(name, context);
default: // escape the value
return escapeHTML(that.find(name, context));
}
};
var lines = template.split("\n");
for(var i = 0; i < lines.length; i++) {
lines[i] = lines[i].replace(regex, tag_replace_callback, this);
if (!in_recursion) {
this.send(lines[i]);
}
}
if (in_recursion) {
return lines.join("\n");
}
},
set_delimiters: function (delimiters) {
var dels = delimiters.split(" ");
this.otag = this.escape_regex(dels[0]);
this.ctag = this.escape_regex(dels[1]);
},
escape_regex: function (text) {
// thank you Simon Willison
if (!arguments.callee.sRE) {
var specials = [
'/', '.', '*', '+', '?', '|',
'(', ')', '[', ']', '{', '}', '\\'
];
arguments.callee.sRE = new RegExp(
'(\\' + specials.join('|\\') + ')', 'g'
);
}
return text.replace(arguments.callee.sRE, '\\$1');
},
/*
find `name` in current `context`. That is find me a value
from the view object
*/
find: function (name, context) {
name = trim(name);
// Checks whether a value is thruthy or false or 0
function is_kinda_truthy(bool) {
return bool === false || bool === 0 || bool;
}
var value;
// check for dot notation eg. foo.bar
if (name.match(/([a-z_]+)\./ig)) {
var childValue = this.walk_context(name, context);
if (is_kinda_truthy(childValue)) {
value = childValue;
}
} else {
if (is_kinda_truthy(context[name])) {
value = context[name];
} else if (is_kinda_truthy(this.context[name])) {
value = this.context[name];
}
}
if (typeof value == "function") {
return value.apply(context);
}
if (value !== undefined) {
return value;
}
// silently ignore unkown variables
return "";
},
walk_context: function (name, context) {
var path = name.split('.');
// if the var doesn't exist in current context, check the top level context
var value_context = (context[path[0]] != undefined) ? context : this.context;
var value = value_context[path.shift()];
while (value != undefined && path.length > 0) {
value_context = value;
value = value[path.shift()];
}
// if the value is a function, call it, binding the correct context
if (typeof value == "function") {
return value.apply(value_context);
}
return value;
},
// Utility methods
/* includes tag */
includes: function (needle, haystack) {
return haystack.indexOf(this.otag + needle) != -1;
},
// by @langalex, support for arrays of strings
create_context: function (_context) {
if (this.is_object(_context)) {
return _context;
} else {
var iterator = ".";
if (this.pragmas["IMPLICIT-ITERATOR"]) {
iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
}
var ctx = {};
ctx[iterator] = _context;
return ctx;
}
},
is_object: function (a) {
return a && typeof a == "object";
},
/*
Why, why, why? Because IE. Cry, cry cry.
*/
map: function (array, fn) {
if (typeof array.map == "function") {
return array.map(fn);
} else {
var r = [];
var l = array.length;
for(var i = 0; i < l; i++) {
r.push(fn(array[i]));
}
return r;
}
},
getCachedRegex: function (name, generator) {
var byOtag = regexCache[this.otag];
if (!byOtag) {
byOtag = regexCache[this.otag] = {};
}
var byCtag = byOtag[this.ctag];
if (!byCtag) {
byCtag = byOtag[this.ctag] = {};
}
var regex = byCtag[name];
if (!regex) {
regex = byCtag[name] = generator(this.otag, this.ctag);
}
return regex;
}
};
return({
name: "mustache.js",
version: "0.4.0",
/*
Turns a template and view into HTML
*/
to_html: function (template, view, partials, send_fun) {
var renderer = new Renderer();
if (send_fun) {
renderer.send = send_fun;
}
renderer.render(template, view || {}, partials);
if (!send_fun) {
return renderer.buffer.join("\n");
}
}
});
}();
/*!
ICanHaz.js -- by @HenrikJoreteg
*/
/*global */
(function () {
function trim(stuff) {
if (''.trim) return stuff.trim();
else return stuff.replace(/^\s+/, '').replace(/\s+$/, '');
}
// Establish the root object, `window` in the browser, or `global` on the server.
var root = this;
var ich = {
VERSION: "0.10.2",
templates: {},
// grab jquery or zepto if it's there
$: (typeof window !== 'undefined') ? window.jQuery || window.Zepto || null : null,
// public function for adding templates
// can take a name and template string arguments
// or can take an object with name/template pairs
// We're enforcing uniqueness to avoid accidental template overwrites.
// If you want a different template, it should have a different name.
addTemplate: function (name, templateString) {
if (typeof name === 'object') {
for (var template in name) {
this.addTemplate(template, name[template]);
}
return;
}
if (ich[name]) {
console.error("Invalid name: " + name + ".");
} else if (ich.templates[name]) {
console.error("Template \"" + name + " \" exists");
} else {
ich.templates[name] = templateString;
ich[name] = function (data, raw) {
data = data || {};
var result = Mustache.to_html(ich.templates[name], data, ich.templates);
return (ich.$ && !raw) ? ich.$(trim(result)) : result;
};
}
},
// clears all retrieval functions and empties cache
clearAll: function () {
for (var key in ich.templates) {
delete ich[key];
}
ich.templates = {};
},
// clears/grabs
refresh: function () {
ich.clearAll();
ich.grabTemplates();
},
// grabs templates from the DOM and caches them.
// Loop through and add templates.
// Whitespace at beginning and end of all templates inside
================================================
FILE: server/static/spider/main.html
================================================
================================================
FILE: server/static/spider/start.html
================================================
联系方式,电话 - 上海山优环保科技有限开发公司
上海山优环保科技有限开发公司
主营产品:不锈钢 油烟罩 厨房餐饮
================================================
FILE: server/static/spider/test.html
================================================
可视化采集平台
================================================
FILE: server/static/spider/work.html
================================================
可视化采集平台
================================================
FILE: server/static/views/DataPreview.html
================================================
{{#columns}}
| {{.}} |
{{/columns}}
================================================
FILE: server/static/views/SelectorEdit.html
================================================
================================================
FILE: server/static/views/SelectorEditTableColumn.html
================================================
| {{header}} |
|
|
================================================
FILE: server/static/views/SelectorList.html
================================================
{{#parentSelectors}}
- {{id}}
{{/parentSelectors}}
================================================
FILE: server/static/views/SelectorListItem.html
================================================
| {{id}} |
{{selector}} |
{{type}} |
{{multiple}} |
{{parentSelectors}} |
|
================================================
FILE: server/static/views/SitemapBrowseData.html
================================================
{{#columns}}
| {{.}} |
{{/columns}}
================================================
FILE: server/static/views/SitemapCreate.html
================================================
================================================
FILE: server/static/views/SitemapEditMetadata.html
================================================
================================================
FILE: server/static/views/SitemapExport.html
================================================
================================================
FILE: server/static/views/SitemapExportDataCSV.html
================================================
Export {{_id}} data as CSV.
Waiting for the download button to appear. >
Download now!
================================================
FILE: server/static/views/SitemapImport.html
================================================
================================================
FILE: server/static/views/SitemapList.html
================================================
================================================
FILE: server/static/views/SitemapListItem.html
================================================
| {{_id}} |
{{#startUrl.push}}
{{#startUrl}}
{{.}},
{{/startUrl}}
{{/startUrl.push}}
{{^startUrl.push}}
{{startUrl}}
{{/startUrl.push}}
|
|
================================================
FILE: server/static/views/SitemapSave.html
================================================
================================================
FILE: server/static/views/SitemapScrapeConfig.html
================================================
================================================
FILE: server/static/views/SitemapSelectorGraph.html
================================================
================================================
FILE: server/static/views/SitemapStartUrlField.html
================================================
================================================
FILE: server/static/views/Viewport.html
================================================
Move developer tools to the bottom of your browser to start using Web Scraper.
================================================
FILE: server/templates/base.html
================================================
================================================
FILE: server/templates/config.html
================================================
可视化采集平台
警告!head表单中存在空值。
警告!form表单中存在空值。
警告!cookie表单中存在空值。
{% include "base.html" %}
================================================
FILE: server/templates/data.html
================================================
可视化采集平台
{% include "base.html" %}
================================================
FILE: server/templates/error_status/404.html
================================================
可视化采集平台
404
Page Not Found
Sorry, but the page you are looking for has note been found. Try checking the URL for error, then hit the refresh button on your browser or try found something else in our app.
================================================
FILE: server/templates/error_status/500.html
================================================
可视化采集平台
500
HTTP-Internal Server Error
We've been notified about this issue and we'll take a look at it shortly.
================================================
FILE: server/templates/help.html
================================================
可视化采集平台
{% include "base.html" %}
懒得看的点这里, 直接到示例
采集示例
任务管理
界面说明
主界面:

该模块管理爬虫的运行状态, 可以暂停、停止和恢复爬虫的运行.
详情界面:

如图, 点击任务, 进入详情页, "爬虫统计"为scrapy爬虫框架的日志信息; "请求队列"为将要爬取的url; 上方有"暂停"、"恢复"和"停止"爬虫任务按钮.
主界面为管理爬虫任务状态的模块, 前后端实时同步, 前端实时接收爬虫任务日志, 后端实时响应爬虫任务状态的改变.
新增任务

配置request请求参数和入口url, 同为后续爬虫的请求参数.
1.请求参数配置
配置名称
任务ID的标识, 在数据查询时用来查找数据集合, 保存配置时用来选择请求参数和字段选择器, 唯一值, 不可重复, 不能为
config字符串.
请求方式
本平台选常用的两种请求, post和get.
入口url
爬取页面的入口页面, 选择采集参数和后续页面会在该页面进行扩展
请求参数
请求参数分为三种:
1.请求头header参数
爬虫常用的header参数有
| Header |
解释 |
示例 |
| Accept |
指定客户端能够接收的内容类型 |
Accept: text/plain, text/html |
| Accept-Charset |
浏览器可以接受的字符编码集。 |
Accept-Charset: iso-8859-5 |
| Accept-Encoding |
指定浏览器可以支持的web服务器返回内容压缩编码类型。 |
Accept-Encoding: compress, gzip |
| Accept-Language |
浏览器可接受的语言 |
Accept-Language: en,zh |
| Accept-Ranges |
可以请求网页实体的一个或者多个子范围字段 |
Accept-Ranges: bytes |
| Content-Length |
请求的内容长度 |
Content-Length: 348 |
| Content-Type |
请求的与实体对应的MIME信息 |
Content-Type: application/x-www-form-urlencoded |
| Date |
请求发送的日期和时间 |
Date: Tue, 15 Nov 2010 08:12:31 GMT |
| Proxy-Authorization |
连接到代理的授权证书 |
Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== |
| Referer |
先前网页的地址,当前请求网页紧随其后,即来路 |
Referer:
https://www.baidu.com/ |
| User-Agent |
User-Agent的内容包含发出请求的用户信息 |
User-Agent: Mozilla/5.0 (Linux; X11) |
2.cookie参数
有些网站需要验证cookie参数
3.form-data参数
选择post请求时, 提交的参数信息
如图为header参数
代理ip
针对防爬虫网站设计, 动态代理ip, 有效时间
2018-05-10 14:44:00前
动态解析
越来越多的网站选用js渲染页面, 针对非模板页面加载网站
示例url:
http://shop.99114.com/47907188/ch6
静态爬虫获取页面内容:

动态爬虫获取页面内容:

动态爬虫实际获取页面内容:

在动态爬虫获取页面的前端展现上, 已竭力实现实际获取内容, 但仍不可避免造成爬取内容和展现内容不符.
2.选择器配置

选择器配置界面分为两部分, 上方的
采集页面和下方的
选择器页面.
上方的采集页面为请求配置完成后点击"下一步"所呈现出的页面内容.
下方的选择器界面为选择将要采集的数据.
选择器界面
主界面
提供字段选择器的层级显示、增、删、改、查、预览功能
增、改
添加/修改字段选择器, 共有7中类型的字段选择器
字段选择器属性
查
显示选择器选择在页面中选择的位置
预览
显示选择器匹配的数据
选择器用来选定采集数据的位置和内容, 该界面可以用鼠标点击配置将要采集的数据, 查看采集数据的位置, 预览采集数据的内容, 根据采集数据不同, 将字段选择器分为以下7类.
字段选择器属性
| 字段选择器 |
名称 |
多元素属性(multiple) |
可选子元素 |
该类型页面唯一 |
可查看选择器 |
可预览数据 |
是否采集 |
| SelectorDetail |
详情页选择器 |
否 |
是 |
是 |
是 |
是 |
否 |
| SelectorElement |
元素集选择器 |
是 |
是 |
否 |
否 |
否 |
否 |
| SelectorElementAttribute |
元素属性选择器 |
是 |
否 |
否 |
是 |
是 |
是 |
| SelectorHTML |
html选择器 |
是 |
否 |
否 |
是 |
是 |
是 |
| SelectorImage |
图片选择器 |
是 |
否 |
否 |
是 |
是 |
是 |
| SelectorLink |
翻页选择器 |
否 |
否 |
是 |
是 |
是 |
否 |
| SelectorText |
文本选择器 |
是 |
否 |
否 |
是 |
是 |
是 |
字段使用条件
1.避免同级别multiple、非multiple属性的选择器并存
所有采集的字段(SelectorElementAttribute、SelectorHTML、SelectorImage、SelectorText)有字段选中"多元素属性"时, 不可选择其他采集的字段, 会造成两个字段集合维度混乱("多元素属性"的字段多对一非列表属性字段), 后端维度无法对应的字段值统一处理为空字符串.
如果有"多元素属性", 该父类下所有采集的字段选择器尽量全都是"多元素属性";
如果没有"多元素属性", 该父类下所有采集的字段选择器尽量都没有"多元素属性".
如图:标题、内容为可采集字段, 他们的多元素属性必须一致.
不一致时示例
2.翻页选择器只能在入口页面设置且唯一
SelectorLink(翻页选择器)在入口页面下只能存在一个.
SelectorLink是递归采集函数.
存在多个时导致系统效率降低, 稳健性下降.
3.元素集选择器下可采集的字段多元素属性以元素集选择器为准
SelectorElement(元素集选择器)的子类采集选择器(SelectorElementAttribute、SelectorHTML、SelectorImage、SelectorText)选择multiple(多元素属性)时, 会将子类数据集升维, 数据解析错误.
[详细解释见
元素集选择器的存在意义]
如图:元素集下所有的可采集字段(红圈中)的multiple(多元素属性)以元素集是否选择multiple(多元素属性)为准.
前端表单按照字段选择器名称生成模板, 暂时未判断父类属性; 后端元素SelectorElement(元素集选择器)的子类采集选择器采集时将不会判断multiple(多元素属性).统一按照父类SelectorElement(元素集选择器)是否选择multiple(多元素属性)判断.
4.详情页选择只能有一条链式选择器
前端采集页面在iframe标签内, 详情页选择后无法返回, 同级别选择器只能存在一个, 不同层次的选择器存在详情页选择器则他们必在同一条链式选择器上.
后端采集框架scrapy为异步回调, 选择器解析为链式回调, 同级别存在多个详情页选择器会导致数据混淆、丢失.
如图:"详情", "联系"为两个详情选择器, 在同一条链式选择器上
元素集选择器的存在意义
采集到所需的数据由两个因素决定:html文本和选择器, 其中html文本为页面中内容所在的html块, 选择器为指定采集的字段内容.
在使用到元素集选择器的大多数情况下html块多为列表形式存在, 选择器有多个.
不使用元素集选择器时
多个选择器按照"多元素"属性选择采集的数据, 页面本身可能存在内容块数据缺失, 造成多个选择器选择的数据列无法一一对应, 造成数据混淆、错位
示例:

"多元素属性"选择器优先遍历css选择器
新建两个"多元素属性"选择器, 第一个采集"name"字段, 第二个采集"href"字段, 当页面本身存在数据缺失时, 会使"name"和"href"数据无法一一对应."name_2" => "href_3" 导致数据错位.
使用元素集选择器时

元素集选择器优先遍历内容块, 然后遍历css选择器, 使得数据维度得到统一, 缺失值也不会影响结果值
元素集选择器优先遍历内容块, "多元素属性"的选择器会优先遍历选择器, 两个开始采集维度的不同, 会导致不同的结果.当采集字段中存在多个"多元素属性"的选择器且他们具有同一个父元素, 优先考虑将这些选择器置于元素集选择器中.
选择器树形图
显示各个层级的选择器关系
下一步/保存选择器
开始或者保存任务
查看/下载数据

查看数据和下载已采集数据的页面
读取配置

开始或删除任务
采集示例
"多元素"选择器 + 翻页(1)
http://www.51sole.com/s.aspx?q=PVC通风管
请求配置

参数配置
1.填写字段名称
商品名称(唯一), 如果重复, 选择器保存失败
2.选择 选择器类型
3."点击选择"按钮开启选择器
4-5.分别点击同一列表中同一采集内容(自动匹配通用选择器, 无法匹配时会提示; 如采集单个选择, 只用点击选择器一次即可)
6.点击确定按钮获取"css选择器"内容, 确定左侧"键盘事件"共绑定了一个按键, S键: 当前选择器, P键: 当前选择元素的父元素, C键: 当前选择元素的子元素
7.选择"多元素属性"
8.保存选择器

同上, 采集
公司url字段

点击下一页, 选择
翻页选择器

整体如下:
主页面选择器:
选择器层级:
点击
下一步
开始任务启动任务, 然后再主界面进行任务管理或者
查看数据里下载数据

元素集选择器+翻页(2)+详情
https://zhongshan.china.cn/search/fisnfv.shtml
参数配置
1.选择
元素集选择器

2.配置
翻页选择器(翻页选择器分两种, 点击选择和手动配置, 优先使用手动配置, 选择器有全局表单验证, 改起来太麻烦, 使用手动配置时, 随便填个值), 手动配置时, 翻页url中变动的参数 使用
%s代替.

3.进入
元素集选择器内, 新建
产品名称,
所在地和
公司名称选择器(在元素集选择器中, 默认以第一个元素集为模板, 其他范围内
点击选择按钮无法生效)
注意:在元素集选择器中不要选择"多元素"属性, 元素集选择器中的子选择器, 维度以
元素集选择器为准.

4.配置
详情选择器, 选择将要进入的标签, 保存

5.进入
详情选择器, 在页面中点击详情链接
6.进入详情页之后, 再配置一个
详情选择器进入联系页

7.在联系页中, 选择
联系人
电话
地址字段采集器

8.打开
选择器树形图查看选择器层级

8.
下一步开始任务, 注意请求间隔, 太快容易被封

被封

9.查看/下载数据

可能存在的问题
动态解析
动态解析网页太慢, 资源占用过高, 显示内容和爬取内容不同, 造成显示内容和爬取内容不同主要因为网站自带js的运行导致的, 如果取消爬取网站自带js, 可能会造成依赖css无法加载, 前端展示效果极差和网页布局和渲染效果缺失, 留待以后解决.
网页页模板不同
http://kungeina0315.51sole.com/companycontact.htm
http://jing18028106510.51sole.com/companycontact.htm

场景:两个详情页模板, 由于字段个数不同, 使用css选择器时, 对不同的模板选择字段, 会造成字段缺失或者错位的情况。 这两个页面中, 由于第二个页面多了"传真"和"微信"这两个字段, 导致相同的css选择器在匹配第一个页面的同时, 匹配第二个页面时"传真"字段之后的css选择器选择的字段值错位.
处理:使用css选择器选择整个内容块, 然后使用正则表达式提取采集的内容

如图所示: 采集"手机"字段, 选择整个内容块, 然后使用正则表达式提取.
选择器无法选中
点击选择按钮很难选中元素时, 可以先选择他的父元素或子元素, 然后使用键盘按钮C或P调整.
点击选择没有反应, 本平台将采集网页放在本域名内, 跨域导致有些资源加载异常, 使得采集脚本无法加载, 此时只能手动填写css选择器.
================================================
FILE: server/templates/index.html
================================================
可视化采集平台
================================================
FILE: server/templates/read.html
================================================
可视化采集平台
{% include "base.html" %}
================================================
FILE: server/templates/work.html
================================================
可视化采集平台
{% include "base.html" %}
================================================
FILE: server.py
================================================
# !/usr/bin/python
# -*- coding: utf-8 -*-
import logging
import tornado.platform.twisted
from server import __version__
from tornado.ioloop import IOLoop
from server.config.opts import opts
# 日志
logger = logging.getLogger('spider')
def setup_event_loop(use_twisted_reactor, debug=True):
"""启动event_loop"""
if use_twisted_reactor:
tornado.platform.twisted.TwistedIOLoop().install()
if debug:
print("Using Twisted reactor as a Tornado event loop")
else:
tornado.platform.twisted.install()
IOLoop.instance().set_blocking_log_threshold(0.5)
if debug:
print("Using Tornado event loop as a Twisted reactor")
def main(port, host, start_manhole, manhole_port, manhole_host, loglevel, opts):
"""主函数"""
from server.spider.handlers import get_application
from server.spider.crawler_process import MyselfCrawlerProcess
from server.spider import manhole
# 日志等级
settings = {'LOG_LEVEL': loglevel}
crawler_process = MyselfCrawlerProcess(settings)
app = get_application(crawler_process, opts)
app.listen(int(port), host)
logger.info("scrapy v%s is started on %s:%s" % (__version__, host, port))
if start_manhole:
manhole.start(manhole_port, manhole_host, {'cp': crawler_process})
logger.info("Manhole server is started on %s:%s" % (manhole_host, manhole_port))
crawler_process.start(stop_after_crawl=False)
if __name__ == '__main__':
setup_event_loop(
use_twisted_reactor=False,
debug=False
)
main(
port=int(opts['spider']['port']),
host=opts['spider']['host'],
start_manhole=opts['spider.manhole']['enabled'],
manhole_port=int(opts['spider.manhole']['port']),
manhole_host=opts['spider.manhole']['host'],
loglevel=opts['spider']['loglevel'],
opts=opts,
)