分类 编程语言 下的文章

用goquery从国家统计局抓取最新省市区3级行政区划代码,生成SQL文件导入数据库

想找一份最新的省市区三级联动的数据,网上的怕不够新,不够准确
正好用go从国家统计局官方网站爬一份,生成sql文件,然后导入数据库

最新省市区3级行政区划代码的地址在
http://www.stats.gov.cn/tjsj/tjbz/xzqhdm/201703/t20170310_1471429.html

数据库表结构

DROP TABLE IF EXISTS `3goals_area`;
CREATE TABLE `3goals_area` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL COMMENT '名称',
  `code` int(10) unsigned NOT NULL COMMENT '区域代码',
  `level` int(1) unsigned NOT NULL DEFAULT '0' COMMENT '层级1省2市3县区',
  `parent` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '父code',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1625 DEFAULT CHARSET=utf8mb4 COMMENT='省市区县';

代码:spider.go

package main

import (
    "log"
    "github.com/PuerkitoBio/goquery"
    "os"
    "io"
    "strings"
)

func GetSQL(){
    
    //地址
    var url string
    url = "http://www.stats.gov.cn/tjsj/tjbz/xzqhdm/201703/t20170310_1471429.html"

    doc, err := goquery.NewDocument(url)
    if err != nil{
      log.Fatal(err)
    }

    //打开area.sql文件,准备写入sql语句
    var f *os.File

    f, _ = os.Create("area.sql")

    //按顺序读取,设置当前的省code,市code,用于sql中的parent字段
    var nowProvince string = "0"

    var nowCity string = "0"

    //取到所有数据,循环,判断是省、市还是区县,做不同处理,生成sql语句
    doc.Find(".MsoNormal").Each(func(i int, s *goquery.Selection) {
        //根据页面特点,有加粗<b>标签的是省级数据
        if(s.Find("b").Text() != "") {

            nowProvince = strings.TrimSpace(s.Find("span").First().Text())

            var name string = strings.TrimSpace(s.Find("span").Last().Text())
            var code string = strings.TrimSpace(s.Find("span").First().Text())
            
            io.WriteString(f, "INSERT INTO 3goals_area(`name`,`code`,`level`,`parent`) values('" + name + "','" + code + "',1,0);\r\n")
      
        } else {
            //根据页面特点,第一个<span>标签内容是一个空格的是市级数据
            if(s.Find("span").First().Text() == " ") {

                nowCity = strings.TrimSpace(s.Find("span").Eq(1).Text())

                var name string = strings.TrimSpace(s.Find("span").Last().Text())
                var code string = strings.TrimSpace(s.Find("span").Eq(1).Text())

                io.WriteString(f, "INSERT INTO 3goals_area(`name`,`code`,`level`,`parent`) values('" + name + "','" + code + "',2," + nowProvince + ");\r\n")

            }

            //根据页面特点,第一个<span>标签内容是两个空格的是区县级数据
            if(s.Find("span").First().Text() == "  ") {

                var name string = strings.TrimSpace(s.Find("span").Last().Text())
                var code string = strings.TrimSpace(s.Find("span").Eq(1).Text())

                io.WriteString(f, "INSERT INTO 3goals_area(`name`,`code`,`level`,`parent`) values('" + name + "','" + code + "',3," + nowCity + ");\r\n")

            }
            
            
        }

    })
}

func main(){
    GetSQL()
}

安装好go环境,在spider.go文件当前目录下,运行

go run spider.go

可以看到在当前目录下,生成了area.sql文件

不想跑脚本的,可以直接拿去导入,sql内容如下:

内容太大,无法直接显示,想直接下载sql文件的,点击这里到csdn下载吧

PHP 标准规范-PSR学习

PSR 是 PHP Standard Recommendations 的简写,由 PHP FIG 组织制定的 PHP 规范,是 PHP 开发的实践标准。

PHP FIG,FIG 是 Framework Interoperability Group(框架可互用性小组)的缩写,由几位开源框架的开发者成立于 2009 年,从那开始也选取了很多其他成员进来(包括但不限于 Laravel, Joomla, Drupal, Composer, Phalcon, Slim, Symfony, Zend Framework 等),虽然不是「官方」组织,但也代表了大部分的 PHP 社区。

项目的目的在于:通过框架作者或者框架的代表之间讨论,以最低程度的限制,制定一个协作标准,各个框架遵循统一的编码规范,避免各家自行发展的风格阻碍了 PHP 的发展,解决这个程序设计师由来已久的困扰。

目前已表决通过了 6 套标准,已经得到大部分 PHP 框架的支持和认可。

PSR-1 基础编码规范

本篇规范制定了代码基本元素的相关标准,以确保共享的PHP代码间具有较高程度的技术互通性。

PSR-1详情介绍

PSR-2 编码风格规范

本篇规范是 [PSR-1][] 基本代码规范的继承与扩展。

本规范希望通过制定一系列规范化PHP代码的规则,以减少在浏览不同作者的代码时,因代码风格的不同而造成不便。

当多名程序员在多个项目中合作时,就需要一个共同的编码规范, 而本文中的风格规范源自于多个不同项目代码风格的共同特性, 因此,本规范的价值在于我们都遵循这个编码风格,而不是在于它本身。

PSR-2详情介绍

PSR-3 日志接口规范

本文制定了日志类库的通用接口规范。

本规范的主要目的,是为了让日志类库以简单通用的方式,通过接收一个 Psr\Log\LoggerInterface 对象,来记录日志信息。 框架以及CMS内容管理系统如有需要,可以 对此接口进行扩展,但需遵循本规范, 这才能保证在使用第三方的类库文件时,日志接口仍能正常对接。

PSR-3详情介绍

PSR-4 自动加载规范

本 PSR 是关于由文件路径 自动载入 对应类的相关规范, 本规范是可互操作的,可以作为任一自动载入规范的补充,其中包括 PSR-0,此外, 本 PSR 还包括自动载入的类对应的文件存放路径规范。

PSR-4详情介绍

PSR-6 缓存接口规范

介绍#

缓存是提升应用性能的常用手段,为框架中最通用的功能,每个框架也都推出专属的、功能多 样的缓存库。这些差别使得开发人员不得不学习多种系统,而很多可能是他们并不需要的功能。 此外,缓存库的开发者同样面临着一个窘境,是只支持有限数量的几个框架还是创建一堆庞 大的适配器类。

一个通用的缓存系统接口可以解决掉这些问题。库和框架的开发人员能够知道缓存系统会按照他们所 预期的方式工作,缓存系统的开发人员只需要实现单一的接口,而不用去开发各种各样的适配器。

目标#

本 PSR 的目标是:创建一套通用的接口规范,能够让开发人员整合到现有框架和系统,而不需要去 开发框架专属的适配器类。

PSR-6详情介绍

PSR-7 HTTP 消息接口规范

此文档描述了 RFC 7230 和 RFC 7231 HTTP 消息传递的接口,还有 RFC 3986 里对 HTTP 消息的 URIs 使用。

HTTP 消息是 Web 技术发展的基础。浏览器或 HTTP 客户端如 curl 生成发送 HTTP 请求消息到 Web 服务器,Web 服务器响应 HTTP 请求。服务端的代码接受 HTTP 请求消息后返回 HTTP 响应消息。

通常 HTTP 消息对于终端用户来说是不可见的,但是作为 Web 开发者,我们需要知道 HTTP 机制,如何发起、构建、取用还有操纵 HTTP 消息,知道这些原理,以助我们刚好的完成开发任务,无论这个任务是发起一个 HTTP 请求,或者处理传入的请求。

PSR-7详情介绍

chrome的APP模式与全屏模式 --app --kiosk

新建脚本start.bat

自动全屏模式

"C:\Program Files\Google\Chrome\Application\chrome.exe" --kiosk http://cn.bing.com/

"C:\Program Files\Google\Chrome\Application\chrome.exe"是chrome程序路径

这种全屏显示和正常状态下chrome按F11是不一样的,F11全屏后chrome会在左下角显示A标签的链接地址,同时鼠标移动到最上方会弹出退出全屏的提示,可以再次按F11推出全屏模式,而通过参数启动全屏之后,F11将失效,A标签提示和退出全屏提示也将消失,只能通过ALT+F4退出chrome全屏模式,是很彻底的全屏模式,通过这种全屏模式,可以将网站制作成软件形式。

APP模式

"C:\Program Files\Google\Chrome\Application\chrome.exe" --app=http://cn.bing.com/ --start-maximized

APP 模式下,系统任务栏的图标会变成网页的 favicon 图标,而且窗口标题没有了 Google Chrome 的字样;

另外,为了让bat文件后台运行,随系统启动,新建一个vbs文件

createobject("wscript.shell").run "C:\start.bat",0

C:\start.bat 是bat文件路径
最后一个参数0 表示后台运行

然后将此vbs文件,放到【开始】-》【所有程序】-》【启动】里(windows下)。

就可以实现开机自启动chrome,并全屏打开某网页。

ReactJS学习-使用webpack构建工程,使用materialUI构建前端,与hprose后端通讯

实现目标:

Material-UI 是一套用React写成的,符合Google Material Design 的UI组件库。

http://www.material-ui.com/

前端通过Material-UI构造界面,然后通过hprose-html5调用后端hprose服务取数据

hprose服务参考 上一篇hprose实践,

环境配置:

必须先安装nodejs与npm

新建工程目录

mkdir react-workspace
cd react-workspace
npm init

安装依赖

npm install --save react react-dom react-tap-event-plugin material-ui
npm install --save-dev babel-core babel-loader 
npm install --save-dev babel-preset-es2015 babel-preset-react babel-preset-stage-1
npm install --save-dev webpack

第一行是生产用的 React 与 Material-UI 部分。
第二行是Babel转换器的核心部分。
第三行是Babel转换器的三个额外配置:ES2015(ES6),React,Stage1(ES7)。
第四行是Webpack的部分。

第二、三、四行的内容只在工程构建之前有用(用于开发:-dev)。

安装完毕之后呢,可以先检查一下 package.json
应该会看见如下内容

"dependencies": {
    "material-ui": "^0.15.0",
    "react": "^15.1.0",
    "react-dom": "^15.1.0",
    "react-tap-event-plugin": "^1.0.0"
  },
  "devDependencies": {
    "babel-core": "^6.9.1",
    "babel-loader": "^6.2.4",
    "babel-preset-es2015": "^6.9.0",
    "babel-preset-react": "^6.5.0",
    "babel-preset-stage-1": "^6.5.0",
    "webpack": "^1.13.1"
  },

配置 Babel

在 package.json 中添加一个域"babel",与之前的"dependencies" 同级。

"babel": {
    "presets": [
      "es2015",
      "react",
      "stage-1"
    ],
    "plugins": []
  }

配置 Webpack

在项目目录新建一个webpack.config.js ,并写入:

var path = require('path');

module.exports = {
    entry: './entry.js',
    output: {
        path: path.join(__dirname, '/dist'),
        filename: 'bundle.js'
    },
    resolve: {
        extensions: ['', '.js', '.jsx']
    },
    module: {
        loaders: [
            { test: /\.jsx?$/, loaders: ['babel'] }
        ]
    }
}

这里对Webpack的打包行为做了配置,主要分为几个部分:

entry:指定打包的入口文件,每有一个键值对,就是一个入口文件
output:配置打包结果,path定义了输出的文件夹,filename则定义了打包结果文件的名称
resolve:定义了解析模块路径时的配置,常用的就是extensions,可以用来指定模块的后缀,这样在引入模块时就不需要写后缀了,会自动补全
module:定义了对模块的处理逻辑,这里可以用loaders定义了一系列的加载器,以及一些正则。当需要加载的文件匹配test的正则时,就会调用后面的loader对文件进行处理,这正是webpack强大的原因。比如这里定义了凡是.js结尾的文件都是用babel-loader做处理,而.jsx结尾的文件会先经过jsx-loader处理,然后经过babel-loader处理。当然这些loader也需要通过npm install安装
plugins: 这里定义了需要使用的插件,比如commonsPlugin在打包多个入口文件时会提取出公用的部分,生成common.js

当然Webpack还有很多其他的配置,具体可以参照它的配置文档

配置 npm 脚本
现在我们还缺少一个构建脚本,编辑package.json 中的 "scripts" 域:

"scripts": {
    "build": "webpack",
    "build-dev": "webpack -w -d"
  }

接下来我们就可以在项目目录下使用简单的构建脚本了:

$ npm run build

如果是开发中,可以使用监听式的Webpack,差量打包,提升效率。

$ npm run build-dev

按照配置,打包生成的文件为 dist/bundle.js 。

至此,基本的环境已经配置完毕,我们来尝试一下调用Material-UI库。

代码编写

创建Web入口
在项目目录下创建一个index.html,写入:

<!doctype html>
<html>
    <head>
        <meta charset="utf-8" />
    </head>
    <body>
        <script src="dist/bundle.js"></script>
    </body>
</html>

编写Webpack入口
编辑项目目录下的 entry.js,写入:

import React from 'react';
import ReactDOM from 'react-dom';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import AppBar from 'material-ui/AppBar';

const App = () => (
  <MuiThemeProvider muiTheme={getMuiTheme()}>
    <AppBar title="Hello, Material-UI!" />
  </MuiThemeProvider>
);

let app = document.createElement('div');
ReactDOM.render(<App />, app);
document.body.appendChild(app);

运行构建脚本:

npm run build

输出
6.png

现在可以看到目录下有了一个dist/bundle.js

打开index.html

7.png
成功

以上内容严重参考:http://blog.csdn.net/zccz14/article/details/51421324

浏览器自动刷新

如果需要一直输入 npm run build 确实是一件非常无聊的事情,幸运的是,我们可以把让他安静的运行,让我们设置 webpack-dev-server。

npm install --save webpack-dev-server

修改package.json文件的scripts:

"scripts": {
        "build": "webpack",
        "dev": "webpack-dev-server --devtool eval --progress --colors --hot --content-base build"
      }

当你在命令行里运行 npm run dev 的时候他会执行 dev 属性里的值。这是这些指令的意思:

webpack-dev-server - 在 localhost:8080 建立一个 Web 服务器
--devtool eval - 为你的代码创建源地址。当有任何报错的时候可以让你更加精确地定位到文件和行号
--progress - 显示合并代码进度
--colors - Yay,命令行中显示颜色!
--content-base build - 指向设置的输出目录
总的来说,当你运行 npm run dev 的时候,会启动一个 Web 服务器,然后监听文件修改,然后自动重新合并你的代码。真的非常简洁!

访问 http://localhost:8080 你会看到效果。

访问发现 “Cannot GET /” 错误

因为我们的index.html不是在build目录下

调整项目结构为

  • /app
    • main.js
    • component.js
  • /build
    • bundle.js (自动创建)
    • index.html
  • package.json
  • webpack.config.js

修改index.html

<!doctype html>
    <html>
        <head>
            <meta charset="utf-8" />
        </head>
        <body>
            <script src="bundle.js"></script>
        </body>
    </html>

将entry.js移动到app/main.js

修改webpack.config.js,将入口文件设置为app/main.js,output设为build目录下的bundle.js,
并新增入口点,使得浏览器在文件修改之后会自动刷新。

var path = require('path');

module.exports = {
    entry: [
      'webpack/hot/dev-server',
      'webpack-dev-server/client?http://localhost:8080',
      path.resolve(__dirname, 'app/main.js')
    ],
    output: {
        path: path.join(__dirname, '/build'),
        filename: 'bundle.js'
    },
    resolve: {
        extensions: ['', '.js', '.jsx']
    },
    module: {
        loaders: [
            { test: /\.jsx?$/, loaders: ['babel'] }
        ]
    }
}

运行命令 npm run dev, 然后访问http://localhost:8080/
看到之前同样页面,然后修改一下main.js

<AppBar title="Hello, Material-UI!" />

修改为

<AppBar title="Hello, World!" />

保存一下,再回到浏览器,会发现自动刷新了,内容也变成了Hello, World!

通过hprose-html5取数据

下面通过后端服务取数据,来替代Hello,World!

现已有hprose for php 构建的hprose服务,在远程服务器http://xx.xx.xx.xx:8080/
方法为getUserByID

修改index.html,引入hprose-html5.js,使用的是bootcss提供的cdn

<script type="text/javascript" src="http://cdn.bootcss.com/hprose-html5/2.0.8/hprose-html5.js"></script>

修改main.js

import React from 'react';
import ReactDOM from 'react-dom';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import AppBar from 'material-ui/AppBar';

const muiTheme = getMuiTheme();

class App extends React.Component{

  constructor(props){
    super(props);
    this.state = {
      data:"none",
    };
    this.componentDidMount = this.componentDidMount.bind(this);
  }

  componentDidMount() {
      this.setState({data: "block"});
      let self = this;
      let client = hprose.Client.create("http://xx.xx.xx.xx:8080/", ["getUserByID"]);
      client.getUserByID(2, function(result) {
            console.log(result.player_name);
            self.setState({data: result.player_name});
        }, function(name, err) {
            console.log(err);
        });
  }
  render() {
    return (
    <MuiThemeProvider muiTheme={muiTheme}>
    <AppBar title={this.state.data} />
    </MuiThemeProvider>
    );
  }
}

let app = document.createElement('div');
ReactDOM.render(<App />, app);
document.body.appendChild(app);

查看结果

5.png

从后端服务取到了数据“keyunqqq”

PS:React在ES6的实现中去掉了getInitialState这个hook函数,规定state在constructor中实现,如下:

Class App extends React.Component {

constructor(props) {
super(props);
this.state = {};
}
...
}

Babel的Blog上还有一种实现方法,即直接使用赋值语句:

Class App extends React.Component {

constructor(props) {
super(props);
}

state = {}
...
}

ES6中this需要手动绑定:

this.componentDidMount = this.componentDidMount.bind(this);

基于swoole的Hprose for PHP 开发后端API服务 实践


注:Hprose for PHP 已更新到2.0 本文代码已失效 请参阅最新官方文档
https://github.com/hprose/hprose-php/wiki


Hprose(High Performance Remote Object Service Engine)
是一款先进的轻量级、跨语言、跨平台、无侵入式、高性能动态远程对象调用引擎库。它不仅简单易用,而且功能强大。
你无需专门学习,只需看上几眼,就能用它轻松构建分布式应用系统。
http://hprose.com/

Hprose for PHP
https://github.com/hprose/hprose-php

swoole PHP的异步、并行、高性能网络通信引擎
http://www.swoole.com/

简单示例,目标:开发一个后台服务,通过id查询数据库的用户表数据,
当服务器端发布的方法被调用前,通过Hprose提供的onBeforeInvoke函数来验证IP

软件安装:
mariadb 10.1.13
php 5.6.21
swoole 1.8.4

https://github.com/hprose/hprose-php/tree/master/src
hprose文件下载到工作目录下

服务端:
目录结构
QQ图片20160524142310.png

数据库配置文件:config.php

<?php
/**
 *  数据库连接配置
 */
define('DB_NAME', 'xxx');
define('DB_USER', 'xxx');
define('DB_PWD', 'xxx');
define('DB_HOST', '127.0.0.1');
define('DB_PORT','3306');
define('DB_TYPE','mysql');
define('DB_CHARSET','utf8');
define('PAGINATE_LIMIT', '5');
define('PREFIX', '');

User类文件:User.php

<?php
class User {
    
    public function getUserByID($user_id) {
        
        try {

            $dbh = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PWD);

            $rs = array();
            
            foreach($dbh->query("SELECT * from XXXX WHERE `id` = {$user_id}") as $row) {
                $rs[] = $row;
            }
            
            return $rs;

        } catch (PDOException $e) {
            return "Error!: " . $e->getMessage() . "<br/>";
        }
    }
}

服务器文件:server.php

<?php
/**
 *  配置文件    
 */
require_once("config.php");

/**
 *  引入Hprose
 */
require_once("Hprose.php");

/**
 *  autoload加载,将文件都放在classes目录下
 */
spl_autoload_register(function ($class) {
    include 'classes/' . $class . '.php';
});

/**
 *  使用 HproseSwooleServer 来创建一个独立的 hprose 服务
 *  绑定8080端口
 */
$server = new HproseSwooleServer('http://0.0.0.0:8080/');

/**
 *  addInstanceMethods用来发布指定对象上的指定类层次上声明的所有public实例方法
 *  它有三个参数,其中后两个是可选参数。
 *  如果您在使用addInstanceMethods方法时,不指定类层次(或者指定为NULL),则发布这个对象所在类上声明的所有public实例方法
 *  设置别名为my
 */
$server->addInstanceMethods(new User(), NULL, 'my');

/**
 *  onBeforeInvoke 会在接口被调用前执行
 *  因此可以用来验证IP
  */
$server->onBeforeInvoke = function ($name, $args, $byref, $context) {
    if($context->request->server['remote_addr'] == '127.0.0.1') {

    } else {
        //不是正确的IP则抛出异常,终止服务
        throw new Exception("unknow client IP");    
    }
};

//启动服务
$server->start();

客户端:

<?php
require_once("Hprose.php");
$client = new HproseSwooleClient('http://localhost:8080');
//调用getUserByID方法,需要加上设置的别名my_
print_r($client->my_getUserByID(4003));

直接打印出了数据,则走通了整个流程。

Hprose for HTML5客户端调用跨域问题

用Hprose for HTML5的客户端调用,发现不能跨域,
查源码,发现Hprose提供了解决办法,server.php启动服务前加上以下语句,
允许跨域,并将调用的域名加入AccessControlAllowOrigin

$server->setCrossDomainEnabled();
$server->addAccessControlAllowOrigin('http://xxxxxxxxxx');

可解决跨域问题。