有一种业务需求是每天都要去采集很多链接,然后保存到数据库;有一种情况是你采集回来的链接有很多可能是不存在的页面,或者说过两天就不存在了,统称死链接;还有一种需求就是我要确保我采集回来的链接都不会是404的,至少能够把404的情况降到最低。
可能有人会想,那简单,在采集回来的链接,循环每一条链接地址,然后获取它的头部信息返回的状态码判断是不是404,如果不是才保存到数据库,这不是最简单直接的方式吗?
如果只是采集那么一千几百条的,那没有问题,也就是耗点时间,在php里面get_headers函数大概一秒能够获取到4条链接的头部信息,而且还是在网络好的情况下,如果采集上万条,那就会随随便便给你报个502或者504错误,你可能会想着把php-fpm.conf里面的request_terminate_timeout =300的请求时间改大一点,再把php的set_time_limit(3600)改大一点,因为一般的请求都不会超过5分钟,如果超过了,就会报上面错误,但只是改大一些参数,我觉得只是治标不治本,而且这种请求很容易会拖慢整个网站的性能。
我暂时能够想到的就是shell+php的处理方式,弄个定时任务每天跑。
#!/bin/bash
Urls='/data/urls.txt'
Http_Code='/data/status_code.txt'
####php######
>$Urls
>$Http_Code
/usr/local/php/bin/php -q /www/do.php 0000
##############
THREAD_NUM=100
mkfifo tmp
exec 9<>tmp
for ((i=0;i<$THREAD_NUM;i++))
do
echo -ne "\n" 1>&9
done
for urlstr in `cat $Urls`
do
{
read -u 9
{
arr_u=(${urlstr//|=|/ })
line=${arr_u[0]}
url_id=${arr_u[1]}
status_code=`curl -I -m 5 -o /dev/null -s -w %{http_code} $line`
#echo -e "$line\t$status_code" |tee -a $Http_Code
if [ "$status_code" = "404" -o "$status_code" = "400" ]
then
#codemd5=`printf $line|md5sum|tr -d " -"`
#echo -e "$line|$url_id|$status_code" | tee -a $Http_Code
echo "$url_id" >> $Http_Code
fi
echo -ne "\n" 1>&9
}&
}
done < $Urls
wait
echo "complete!"
rm tmp
###############################
/usr/local/php/bin/php -q /www/do.php 0000 del
上面的脚本代码的功能在于,一个是放链接地址的urls.txt文件,另一个是存查出来的结果的status_code.txt文件,脚本执行的时候,先把这两个文件清空,然后再执行php,php里面会从数据库里把链接查出来,然后以\n连接保存到urls.txt文件,php后面的000只是我自定义的参数,由于考虑到性能与可用性,我使用并发请求,这样能够提高处理速度,并发数设置100,循环获取链接之后把字符串切割开,切割的字符串为‘|=|’,这个字符串是我在php里面定义的。例如保存的时候我会把链接跟id一起保存(http://aaa.com/abc.php|=|123456)。
所以。需要切割后单独使用链接地址以及id数据,在接下来的代码里面,通过curl获取到链接的返回状态码,判断如果是404或者400的情况就把对应的id值保留在status_code.txt里面。这样执行完成后,就过滤出我们所需要的链接所对应的数据库id
最后一步也是执行php,在php里面根据生成的status_code.txt里面的id值,直接把数据从数据表里面删除,同时生成一个死链文档,其实在php里面所做的处理要看你具体业务,这里只是简单说明一下。
使用shell+php的方式,15000条数据的处理也只是用了2分钟左右,那么最后,就可以把shell的执行交给定时任务,每天执行一次,就可以达到我想要的效果了。