weiyixi 发布的文章

<?php
$spider = new SpiderExample();
///question/267782048
$result = $spider->getData('https://www.zhihu.com/question/267782048/answer/330283932', [], $method = 'post');
var_dump($result);

/**
 * Class SpiderExample
 */
class SpiderExample
{

    private $cookie;

    /**
     * SpiderExample constructor.
     * @param string $cookieFile
     */
    public function __construct($cookieFile = './tmp')
    {
        header("Content-type: text/html; charset=utf-8");
        set_time_limit(0);
        //设置cookie文件
        $this->cookie = tempnam('./tmp', 'cookieSpiderExample' . date('Y-m-d'));
    }

    /**
     * 需要登录的先登录
     * @param $url
     * @param $postData
     * @param string $method
     * @return array|bool
     */
    public function login($url, $postData, $method = 'POST')
    {
        return $this->query(['url' => $url, 'data' => $postData], true, $method);
    }

    /**
     * 获取数据
     * @param $url
     * @param $postData
     * @param string $method
     * @return array|bool
     */

    public function getData($url, $postData, $method = 'POST')
    {
        return $this->query(['url' => $url, 'data' => $postData], false, $method);
    }

    /**
     * @param array postData
     * [
     *      'url'=>'',//链接
     *      'data'=>''//数据
     * ]
     * @param bool type 是否是登录请求
     * @param string method POST请求/get
     * @return array|bool
     */
    private function query($postData, $type = false, $method = 'POST')
    {
        $curl = curl_init();
        if ($type)
            $cookie_type = CURLOPT_COOKIEJAR;
        else
            $cookie_type = CURLOPT_COOKIEFILE;

        curl_setopt_array($curl, array(
            CURLOPT_URL => $postData['url'],
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => "",
            CURLOPT_MAXREDIRS => 10,
            $cookie_type => $this->cookie,
            CURLOPT_TIMEOUT => 120,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => $method,
            //是否验证https
            CURLOPT_SSL_VERIFYHOST => 0,
            CURLOPT_SSL_VERIFYPEER => 0,
            //是否跟随跳转
            CURLOPT_FOLLOWLOCATION=>true,
            CURLOPT_POSTFIELDS => http_build_query($postData['data']),
            CURLOPT_HTTPHEADER => array(
                "Cache-Control: no-cache",
                "Content-Type: application/x-www-form-urlencoded",
            ),
        ));
        $response = curl_exec($curl);
        $err = curl_error($curl);
        curl_close($curl);
        if ($err) {
            return false;
        } else {
            return ['code' => 0, 'data' => $response];
        }
    }

}

/**
 * 获取阶乘
 * @param int $n
 * @return float|int
 */
function factorial(int $n):int
{
    return array_product(range(1, $n));
}

/**
 * 排列数
 * @param int $n 数组长度
 * @param int $m 排列长度
 * @return float|int
 */
function A($n, $m)
{
    return factorial($n) / factorial($n - $m);
}

/**
 * 组合数
 * @param int $n 数组长度
 * @param int $m 排列长度
 * @return float|int
 */
function C($n, $m)
{
    return A($n, $m) / factorial($m);
}

/**
 * 获取组合
 * @param array $a 一个有序数组
 * @param int $m 组合长度
 * @return array
 */
function combination(array $a, int $m): array
{
    //定义空返回结果
    $r = [];
    //数组长度
    $n = count($a);
    //m 只能是小于数组长度的正整数
    if ($m <= 0 || $m > $n) {
        return $r;
    }
    //循环
    for ($i = 0; $i < $n; $i++) {
        //获取一个初始数
        $t = [$a[$i]];
        if ($m == 1) {
            //长度为1时直接给结果赋值
            $r[] = $t;
        } else {
            //取出初始值后面的数组
            $b = array_slice($a, $i + 1);
            //递归
            $c = combination($b, $m - 1);
            foreach ($c as $v) {
                //array_merge() 函数把一个或多个数组合并为一个数组
                $r[] = array_merge($t, $v);
            }
        }
    }

    return $r;
}

/**
 *
 * 全排列
 * @param array $a 一个数组
 * @param int $m 组合长度
 * @return array
 */
function arrangement($a, $m)
{
    $r = array();
    $n = count($a);
    if ($m <= 0 || $m > $n) {
        return $r;
    }
    for ($i = 0; $i < $n; $i++) {
        $b = $a;
        //取出$t初始值 $b为剔除初始数的数组
        $t = array_splice($b, $i, 1);
        if ($m == 1) {
            $r[] = $t;
        } else {
            $c = arrangement($b, $m - 1);
            //遍历合并
            foreach ($c as $v) {
                $r[] = array_merge($t, $v);
            }
        }
    }

    return $r;
}


$res = arrangement(range(1, 3), 2);
var_dump($res);
die;

在短连接生成后我遇到一个新的问题,客户要求短连接域名不固定,这些域名会跳转到同一个解析服务器去解析到目标链接
这样我们需要设置几个重定向
首先短连接地址是 www.surl.com/XXXX.1 www.ssurl.com/XXXX.1
(解释一下:surl/ssurl为客户要求的多个地址,以'.1'为后缀方便识别跳转)
我们要跳转到解析服务器 www.sorttolong.com?key=XXXX

apache

1.apache 配置

打开httpd.conf(在那里? APACHE目录的CONF目录里面),用文本编纂器打开后,查找
(1)
Options FollowSymLinks
AllowOverride None

改为
Options FollowSymLinks
AllowOverride All

(2)去掉下面的注释
LoadModule rewrite_module modules/mod_rewrite.so

2..hdaccess

RewriteEngine On

#RewriteBase / 
#apache
RewriteRule ^(.*).1$  re_url_c.php?key=$1

五位的大概有3000万种可能 七位有几百亿可能,虽然重复概率很低,大流量商用建议继续优化

<?php
/**
 * 短链接类
 */
class urlManager {

    //前缀
    public static $prefix = 'weiyixi';

    //后缀
    public static $suffix = 'ixiyiwei';

    //
    public static $index = 0 ;

    //32个基础字符
    public static $base_key = array (
        "a" , "b" , "c" , "d" , "e" , "f" , "g" , "h" ,
        "i" , "j" , "k" , "l" , "m" , "n" , "o" , "p" , "q" , "r" , "s" , "t" ,
        "u" , "v" , "w" , "x" , "y" , "z" , "0" , "1" , "2" , "3" , "4" , "5" ,
        "6" , "7" , "8" , "9" , "A" , "B" , "C" , "D" , "E" , "F" , "G" , "H" ,
        "I" , "J" , "K" , "L" , "M" , "N" , "O" , "P" , "Q" , "R" , "S" , "T" ,
        "U" , "V" , "W" , "X" , "Y" , "Z", "-", "_");

    /**
     * 创建短链接 - 5 位
     *
    网上摘录方法:

    1)将长网址md5生成32位签名串,分为4段, 每段8个字节;

       2)对这四段循环处理, 取8个字节, 将他看成16进制串与0x3fffffff(30位1)与操作, 即超过30位的忽略处理;

       3)这30位分成6段, 每5位的数字作为字母表的索引取得特定字符, 依次进行获得6位字符串;

       4)总的md5串可以获得4个6位串; 取里面的任意一个就可作为这个长url的短url地址;
     *
     */
    public static function create5($long_url,$k=0){
        $out='';
        if($k>3) return;

        $hex = md5(self::$prefix.$long_url.self::$suffix);
        $subhex = substr($hex, $k * 8, 8);

        $int = hexdec($subhex);

        for($i=0; $i<5; $i++)
        {
            $index = $int & 0x3f;
            //最后一位如果是“-”, 则替换为a,避免部分手机上链接显示错误的问题
            if($i == 4 && $index == 62)
                $index = 0;
            $out .= self::$base_key[$index];
            $int = $int >> 6;
        }

        return $out;
    }

    /**
     * 创建短链接 - 7 位
     *
    网上摘录方法:

    1)将长网址md5生成32位签名串,分为3段, 每段42位;

       2)这42位分成7段, 每6位的数字作为字母表的索引取得特定字符, 依次进行获得7位字符串;

     *
     */
    public static function create7($long_url,$k=0){
        $out='';
        $hex = md5(self::$prefix.$long_url.self::$suffix);
        switch($k)
        {
            case 0:
                $subhex = substr($hex, 21, 11); //最后42位
                break;

            case 1:
                $subhex = substr($hex, 11, 11);    //中间42位
                break;

            case 2:
                $subhex = substr($hex, 0, 11);    //最前面的42位,最前2位抛弃
                break;

            default:
                return;
        }

        $int = hexdec($subhex);
        if($k==1)
            $int = $int >> 2; //此处最后2位已被$k=0时所用,故抛弃

        for($i=0; $i<7; $i++)
        {
            $index = $int & 0x3f;
            //最后一位如果是“-”, 则替换为a,避免部分手机上链接显示错误的问题
            if($i == 6 && $index == 62)
                $index = 0;
            $out .= self::$base_key[$index];
            $int = $int >> 6;
        }

        return $out;
    }
}

JSON作为以键值对为主的存储形式,符合很多业务场景,尤其对于python中dict字典数据类型,能直接相互转换,更加适应了python的某些使用情况。
此外,JSON作为不限定固定结构的键值对,能方便灵活的使用在数据变动性比较大的场景,如对于网络数据的爬取收集等,当然其中也存在一些问题,下文再分析。
这里本文先列举MySQL中对一些JSON数据类型的常用语句和函数(参考MySQL官方文档,建议大家有疑惑的直接去查阅官方文档)。
说明:json除了键值对的形式以外,还包括Array数列形式,这在mysql5.7及以上也是支持的,对Array中元素的操作相当于直接对键值对中的值操作,少了“键”的定位这一步,这里不单独展示,所有的函数都都同时支持,大同小异

1. insert(插入)操作

先用insert给出操作面向的数据对象

insert into jwc(st_num,info) value('131141','{"数学":[95,4],"英语":{"英语上":[87,3],"英语下":[82,3]},"体育":"不及格"}')

2. select(投影)操作

对于JSON数据的选择有一个重要的函数——json_extract()

//选择“数学”对应的值
select json_extract(info,"$.数学") from jwc where id='131141'
也可以用info->”$.数学”代替json_extract()函数
//选择“数学”对应的值
select info->"$.数学" from jwc where id='131141'
用json_keys()函数选择所有的键值
select json_keys(info) from jwc
json中的path路径 

.a.b:表示键a下的键b.a[1]:表示键a对应的值(如果是列表的话)的第二个元素

3. where选择操作

json_contains(column,elem,key)函数判断包含带有elem元素的key的column属性是否存在

//注意中间elem(95)一定要用字符串形式表示
select * from jwc where json_contains(info,'95','$.数学')
json_search()函数:直接对“值”(且值必须是字符串,数列或者键值对都不行)或数列中的元素操作
//json_search的第二个参数one表示找一个,all表示找到所有
select * from jwc where json_search(info,'one',"%及格") is not null;
直接利用键值进行选择
select * from jwc where info->'$.数学[0]'>90;

4. update更新操作

json_array_append()函数:直接在定位元素的后面添加

//对于非列表的值直接扩展成列表
 update personal_info set info=json_array_append(info,'$."数学"',4) where id=6;
json_array_insert()函数:在指定位置插入元素
 update personal_info set info=json_array_insert(info,'$."数学"[0]',4) where id=6;
json_set():直接设定某值
update personal_info set info=json_set(info,'$."数学"',4) where id=6;
json_merge(column,key-value)函数:向json类型字段中添加元素
update personal_info set info=json_merge(info,'{"提示":"this is a test"}') where id=6;
json_replace()函数
update personal_info set info=json_replace(info,'$."提示"','this is another test') where id=6;

缺陷与思考

上述 是关于mysql对json数据类型的操作介绍,下面分析一下json数据类型使用的一些问题。
所有的函数在选择时候的依据都是“值”,不能依靠“键”,如json_search,json_contain,这样对于键的选择就必须要指明,这在一些结构不固定,“键”不明确的情况不太方便。
没有发现对“键”的模糊查找
很多情况下,对值的修改都限定为字符串,不能改为数列或者键值对形式。如下将数学的值直接改为数列是不能执行的:

update personal_info set info=json_replace(info,'$."提示"','this is another test','$."数学"',[94,3]) where id=6;

也就说,对数列和字典的支持还不够。
以上提出的这些缺陷或许是因为我对mysql了解不够深入,或许mysql有相应的解决办法,这里暂时存疑,希望知道的朋友能告知,以上。