看了不少朋友圈里推荐的Python爬虫文章,都觉得太小儿科,处理内容本来就是PHP的强项,Python唯一的好处估计也就天生的Linux自带,和Perl一样,这点觉得挺不够意思的Linux,还是Mac厚道,天生就自带了Python、Perl、PHP、Ruby,当然我也很讨厌讨论一门语言的好坏,每门语言存在就一定有它的道理,反正PHP是全世界最好用的语言,大家都懂的^_^

    前几天比较火的是一个人用C#写了一个多线程爬虫程序,抓取了QQ空间3000万QQ用户,其中有300万用户是有QQ号、昵称、空间名称等信息的,也就是说,有详情也就300万,跑了两周,这没什么,为了证明PHP是全世界最好的语言,虽然大家都懂的^_^,我用PHP写了一个多进程爬虫程序,只用了一天时间,就抓了知乎100万用户,目前跑到第8圈(depth=8)互相有关联(关注了和关注者)的用户。

爬虫程序设计:

因为知乎需要登录才能获取到关注者页面,所以从chrome登录之后把cookie拷贝下来给curl程序模拟登录。

使用两大独立循环进程组(用户索引进程组、用户详情进程组),用的是php的pcntl扩展,封装了一个非常好用的类,使用起来和golang的携程也差不多了。

下面是用户详情的截图,用户索引代码类似

这里插个题外话,经过测试,我的8核的Macbook,跑16进程的速度最快,而16核的Linux服务器,居然是跑8进程最快,这点有点让我莫名其妙了,不过既然测试出最后进程数,就按照最后设置就好啦。

1、用户索引进程组先以一个用户为起点,抓取这个用户的关注了和关注者,然后合并入库,因为是多进程,所以当有两个进程在处理同一个用户入库的时候就会出现重复的用户,所以数据库用户名字段一定要建立唯一索引,当然也可以用redis这些第三方缓存来保证原子性,这个就见仁见智了。

通过步骤一之后,我们就得到下面的用户列表:

2、用户详情进程组按照时间正序,拿到最先入库的用户抓取详情,并且把更新时间更新为当前时间,这样就可以变成一个死循环,程序可以无休止的跑,不断的循环更新用户信息。

程序稳定运行到第二天,突然没有新数据了,检查了一下发现知乎改规则了,不知是为了防我,还是碰巧,反正给我返回的数据是这样的

第一感觉就是胡乱给我输出数据让我采集不到,换了IP、模拟伪装了些数据,都没用,突然感觉这个很熟悉,会不会是gzip?抱着怀疑的态度,试了试gzip,首先当然是告诉知乎不要给我gzip压缩过的数据

把 "Accept-Encoding: gzip,deflate\r\n"; 改成 "Accept-Encoding:deflate\r\n"; 去掉了 gzip,然并卵!

看来知乎是强制要给我gzip压缩数据了,既然如此,那我就解压呗,查了一下php解压gzip,发现就一个函数gzinflate,于是把获取到得内容加上:

$content = substr($content, 10);

$content = gzinflate($content));

这里我真想说,PHP真的是全世界最好的语言,就两个函数,就彻底解决了问题,程序又欢快的跑起来了。

在匹配内容的时候,知乎的细心也是给了我无数的帮助,例如我要分清用户性别:

    

哈哈开玩笑的拉,其实是样式里面有 icon-profile-female 和 icon-profile-male ^_^

我蛋疼的抓了它那么多用户,到底有什么用呢?

其实没什么用,我就是闲的蛋疼 ^_^

有了这些信息,其实就可以做一些别人开头闭口就乱吹一通的大数据分析拉

最常见的当然是:

1、性别分布

2、地域分布

3、职业分布

4、每个职业的男女比例

当然,按照关注人数、浏览人数、提问数、回答数等排序,看看人民都在关注什么,民生、社会、地理、政治,整个互联网都尽收眼底拉。。

也许,你还可以把头像拿来分析,用开源的验黄程序,把色情的筛选出来,然后去拯救东莞? ^_^

然后,你还可以看看那些大学出来的人,最后都干了什么。

有了这些数据,是不是可以打开脑洞 ^_^


<?php 
class vote extends Thread {
    public $res    = '';
    public $url    = array();
    public $name   = '';
    public $runing = false;
    public $lc     = false;
    public function __construct($name) {
        $this->res    = '暂无,第一次运行.';
        $this->param    = 0;
        $this->lurl   = 0;
        $this->name   = $name;
        $this->runing = true;
        $this->lc     = false;
    }
    public function run() {
        while ($this->runing) {
            if ($this->param != 0) {
                $nt          = rand(1, 10);
                echo "线程[{$this->name}]收到任务参数::{$this->param},需要{$nt}秒处理数据.\n";
                $this->res   = rand(100, 999);
                sleep($nt);
                $this->lurl = $this->param;
                $this->param   = '';
            } else {
                echo "线程[{$this->name}]等待任务..\n";
            }
            sleep(1);
        }
    }
}
//这里创建线程池.
$pool[] = new vote('a');
$pool[] = new vote('b');
$pool[] = new vote('c');
//启动所有线程,使其处于工作状态
foreach ($pool as $w) {
    $w->start();
}
//派发任务给线程
for ($i = 1; $i < 10; $i++) {
    $worker_content = rand(10, 99);
    while (true) {
        foreach ($pool as $worker) {
            //参数为空则说明线程空闲
            if ($worker->param=='') {
                $worker->param = $worker_content;
                echo "[{$worker->name}]线程空闲,放入参数{$worker_content},上次参数[{$worker->lurl}]结果[{$worker->res}].\n";
                break 2;
            }
        }
        sleep(1);
    }
}
echo "所有线程派发完毕,等待执行完成.\n";
//等待所有线程运行结束
while (count($pool)) {
    //遍历检查线程组运行结束
    foreach ($pool as $key => $threads) {
        if ($worker->param=='') {
            echo "[{$threads->name}]线程空闲,上次参数[{$threads->lurl}]结果[{$threads->res}].\n";
            echo "[{$threads->name}]线程运行完成,退出.\n";
            //设置结束标志
            $threads->runing = false;
            unset($pool[$key]);
        }
    }
    echo "等待中...\n";
    sleep(1);
}
echo "所有线程执行完毕.\n";

转自:http://www.cnblogs.com/jackluo/p/3919168.html


下载:

http://www.golangtc.com/download

安装:

tar -C /usr/local -xzf go1.4.2.darwin-amd64-osx10.8.tar.gz

设置环境变量:

vim ~/.profile

export GOROOT=/usr/local/go

export GOPATH=/data/golang

export PATH=/usr/local/go/bin:/data/golang/bin:$PATH

编写第一个程序:

package main
import "fmt"
func main() {
    fmt.Printf("hello, world\n")
}

运行:

$ go run hello.go
hello, world

安装程序:

将程序放到 /data/golang/src 目录下,如:

 /data/golang/src/demo

/data/golang/src/demo/hello.go

这时就可以用

go install demo

这样就会在 /data/golang/bin 目录下生成可执行文件 demo,which demo 即可看到

可以把/data/golang/bin/demo 这个文件放到任何地方执行,也可以直接输入 demo 执行,因为上面已经把 /data/golang/bin 设置到PATH去了

注意:test是golang的测试关键字,所以上面的demo目录不能为test


1、下载libmemcached库 地址在https://code.launchpad.net/libmemcached

wget https://launchpad.net/libmemcached/1.0/1.0.18/+do...

2、下载php memcached扩展,地址在http://pecl.php.net/package/memcached

wget http://pecl.php.net/get/memcached-2.2.0.tgz

或者用pecl下载:

pecl download memcached

3、解压

[root@localhost solf]# tar zxvf libmemcached-1.0.18.tar.gz 

[root@localhost solf]# tar zxvf memcached-2.2.0.tgz

4、安装 libmemcached

[root@localhost libmemcached-1.0.18]# ./configure --prefix=/usr/local/libmemcached --with-memcached

[root@localhost libmemcached-1.0.18]# make && make install

5、安装PHPmemcached扩展

运行phpize命令生成configure配置文件

[root@localhost memcached-2.2.0]# phpize

安装,因为机子上可能没有sasl,这里暂时不启用,加上参数--disable-memcached-sasl,否则--enable-sasl

[root@localhost memcached-2.2.0]# ./configure --enable-memcached --with-libmemcached-dir=/usr/local/libmemcached/ --prefix=/usr/local/phpmemcached --with-memcached --disable-memcached-sasl

make && make install

到些安装完成

6、配置php,加载memcahed扩展

extension=memcached.so


今天发现Mysql的主从数据库没有同步

先上Master库:

mysql>show processlist; 查看下进程是否Sleep太多。发现很正常。

show master status; 也正常。

mysql> show master status;

+-------------------+----------+--------------+-------------------------------+

| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |

+-------------------+----------+--------------+-------------------------------+

| mysqld-bin.000001 | 3260 | | mysql,test,information_schema |

+-------------------+----------+--------------+-------------------------------+

1 row in set (0.00 sec)

再到Slave上查看

mysql> show slave status\G

Slave_IO_Running: Yes

Slave_SQL_Running: No

可见是Slave不同步

下面介绍两种解决方法:

方法一:忽略错误后,继续同步

该方法适用于主从库数据相差不大,或者要求数据可以不完全统一的情况,数据要求不严格的情况

解决:

stop slave;

#表示跳过一步错误,后面的数字可变

set global sql_slave_skip_counter =1;

start slave;

之后再用mysql> show slave status\G 查看:

Slave_IO_Running: Yes

Slave_SQL_Running: Yes

ok,现在主从同步状态正常了。。。

方式二:重新做主从,完全同步

该方法适用于主从库数据相差较大,或者要求数据完全统一的情况

解决步骤如下:

1.先进入主库,进行锁表,防止数据写入

使用命令:

mysql> flush tables with read lock;

注意:该处是锁定为只读状态,语句不区分大小写

2.进行数据备份

#把数据备份到mysql.bak.sql文件

[root@server01 mysql]#mysqldump -uroot -p -hlocalhost > mysql.bak.sql

这里注意一点:数据库备份一定要定期进行,可以用shell脚本或者python脚本,都比较方便,确保数据万无一失

3.查看master 状态

mysql> show master status;

+-------------------+----------+--------------+-------------------------------+

| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |

+-------------------+----------+--------------+-------------------------------+

| mysqld-bin.000001 | 3260 | | mysql,test,information_schema |

+-------------------+----------+--------------+-------------------------------+

1 row in set (0.00 sec)

4.把mysql备份文件传到从库机器,进行数据恢复

#使用scp命令

[root@server01 mysql]# scp mysql.bak.sql root@192.168.128.101:/tmp/

5.停止从库的状态

mysql> stop slave;

6.然后到从库执行mysql命令,导入数据备份

mysql> source /tmp/mysql.bak.sql

7.设置从库同步,注意该处的同步点,就是主库show master status信息里的| File| Position两项

change master to master_host = '192.168.128.100', master_user = 'rsync', master_port=3306, master_password='', master_log_file = 'mysqld-bin.000001', master_log_pos=3260;

8.重新开启从同步

mysql> stop slave;

9.查看同步状态

mysql> show slave status\G 查看:

Slave_IO_Running: Yes

Slave_SQL_Running: Yes

好了,同步完成啦。