SHELL网络爬虫实例剖析

简介:

      前天简单分享了用 shell 写网络爬虫的一些见解,今天特地把代码发出来与51博友分享,还是那句话,爱技术、爱开源、爱linux。

       针对脚本的注解和整体构思,我会放到脚本之后为大家详解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#!/bin/bash
#
# This script is used to grab the data on the specified industry websites
# Written by sunsky
# Mail : 274546888@qq.com
# Date : 2014-09-14 3:06:00
#
 
if  [ ` echo  $UID` != 0 ]; then
   echo  'Please use the root to execute the script!'
fi
if  [ ! -f  /dataimg/years  ]; then
   echo  'Please give date file, the file path for/dataimg/years .'
fi
if  [ ! -d $TMP_DIR ]; then
   mkdir  -p $TMP_DIR
fi
if  [ ! -d $URL_MD5_DIR ]; then
   mkdir  -p $URL_MD5_DIR
fi
if  [ ! -d $HTML_DIR ]; then
   mkdir  -p $HTML_DIR
fi
 
ROOT_DIR= "/dataimg"                   # 指定脚本运行根目录
TMP_DIR= "$ROOT_DIR/tmp"               # 生成商品详细页url之前的临时数据存放目录
URL_MD5_DIR= "$ROOT_DIR/url_md5"       # 记录商品详细页url的MD5值的目录
HTML_DIR= "$ROOT_DIR/html"             # 存放下载下来的商品详细页目录
URL_MD5= "$URL_MD5_DIR/md5.$year"      # 负责记录商品详细页url的md5值
WEB_URL= "https://www.redhat.sx/"      # 所爬网站的主页url
REPORT= "$ROOT_DIR/report"             # 负责记录采集的url综合信息
CURL= "curl -A 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.102 Safari/537.36' --referer http://www.redhat.sx"
OPT0= "/dataimg/years"                                                    # 年份信息 
OPT1= "$TMP_DIR/${X1_MD5}"                                                # 品牌信息     
OPT2= "$TMP_DIR/${X1_MD5}_${X2_MD5}"                                      # 车型信息
OPT3= "$TMP_DIR/${X1_MD5}_${X2_MD5}_${X3_MD5}"                            # 装饰信息
OPT4= "$TMP_DIR/${X1_MD5}_${X2_MD5}_${X3_MD5}_${X4_MD5}"                  # 部位分类信息
OPT5= "$TMP_DIR/${X1_MD5}_${X2_MD5}_${X3_MD5}_${X4_MD5}_${URL_LIST_MD5}"  # 商品详情页url信息
 
FIFO_FILE= "/tmp/$$.fifo"
mkfifo  $FIFO_FILE
exec  9<>$FIFO_FILE
rm  -f $FIFO_FILE
 
num=10
for  ((i=0;i<$num;i++)); do
echo
done  >&9
 
while  read  X1; do
{
   URL1= "${WEB_URL}/model/YMMTSelects.cfc?method=getMakes&PassYear=$X1"
   X1_MD5=` echo  $URL1|cksum| cut  -d ' '  -f1`
   if  ls  $OPT1 >& /dev/null ; then
     $CURL -s $URL1| awk  'BEGIN{RS="<"}{print $0}' | awk  -F '>'  '{print $2}' | sed  '1,9d' | sed  '$d' | grep  - v  '^$'  > $OPT1
   fi
   while  read  X2; do
     X2=` echo  $X2| sed  's# #%20#g' `
     URL2= "${URL1}&PassMakeName=$X2"
     #X2_MD5=`echo $URL|cksum|cut -d' ' -f1`
     if  ls  $OPT2 >& /dev/null ; then
       $CURL -s $URL2| awk  'BEGIN{RS="<"}{print $0}' | awk  -F '>'  '{print $2}' | sed  '1,6d' | sed  '$d' | grep  - v  '^$'  > $OPT2
     fi
     while  read  X3; do
       X3=` echo  $X3| sed  's#[[:space:]]#%20#g' `
       URL3= "${URL2}&PassModel=$X3"
       X3_MD5=` echo  $URL3|cksum| cut  -d ' '  -f1`
       if  ls  $OPT3 >& /dev/null ; then
         $CURL -s $URL3| sed  's#[[:space:]]##g' | awk  'BEGIN{RS="<|=|>"}{print $0}' | egrep  '^[0-9]+$'  > $OPT3
       fi
       while  read  X4; do
         X4=` echo  $X4| sed  's# #%20#g' `
         URL4= "${URL3}&PassVehicleID=$X4"
         X4_MD5=` echo  $URL4|cksum| cut  -d ' '  -f1`
         if  ls  "${OPT4}"  >& /dev/null ; then
           $CURL -s $URL4| awk  'BEGIN{RS="<"}{print $0}' | awk  -F '[>;]'  '{print $2}' | sed  -e  '1,3d'  -e  '$d'  -e  '/^$/d'  > $OPT4
         fi
         while  read  X5; do
           X5=` echo  $X5| sed  's# #%20#g' `
           URL_LIST= "${WEB_URL}index.cfm?fuseaction=store.sectionSearch&YMMTyears=$X1&YMMTmakenames=$X2&YMMTmodelnames=$X3&YMMTtrimnames=$X4&YMMTsectionnames=$X5"
           URL_LIST_MD5=` echo  "$URL_LIST" |md5sum| awk  '{print $1}' `
           if  grep  -q $URL_LIST_MD5  "$URL_MD5"  ; then
             $CURL -s  "$URL_LIST"  "$URL_MD5_DIR/$URL_LIST_MD5"
             NUM=` grep  'View page'  "$URL_MD5_DIR/$URL_LIST_MD5" | wc  -l`
             NUM2=$(($NUM /2 ))
             echo  > $OPT5
             grep  'a href="index.cfm?fuseaction=store.PartInfo&PartNumbe'  "$URL_MD5_DIR/$URL_LIST_MD5 "|cut -d'" ' -f2 > $OPT5
             while  [ $NUM2 - ge  2 ]; do
               URL_LIST=` grep  "View page $NUM2"  "$URL_MD5_DIR/$URL_LIST_MD5" | awk  -F '[" ]'   '{a[$9]=$9}END{for(i in a)print a[i]}' `
               $CURL -s  "$URL_LIST" | grep  'a href="index.cfm?fuseaction=store.PartInfo&PartNumbe' | cut  -d '"'  -f2 >> $OPT5
               NUM2=$(($NUM2-1))
             done
             echo  $URL_LIST_MD5 >>  "$URL_MD5"
           fi
           while  read  X6; do
             URL_DETAIL= "${WEB_URL}${X6}"
             URL_DETAIL_MD=` echo  $URL_DETAIL|md5sum| awk  '{print $1}' `
             if  grep  -q $URL_DETAIL_MD  "$URL_MD5"  >& /dev/null ; then  # 该判断以商品列表详细页URL的md5值为基准,负责URL的重复项判定
               $CURL -s  "$URL_DETAIL"  "$HTML_DIR/$URL_DETAIL_MD"
               LABEL=` grep  'diagram-label'  "$HTML_DIR/$URL_DETAIL_MD" | awk  -F '[<>]'  '{print $5}' `   # 商品标签
               GIF_URL=` grep  -B 10 partInfo  "$HTML_DIR/$URL_DETAIL_MD" | grep  -o  "https.*gif" | awk  '{a=$0}END{print a}' # 产品对应的图片URL
               PRODUCT_ID=` grep  'productID'  "$HTML_DIR/$URL_DETAIL_MD" | awk  -F '[<>]'  '{print $3}' # 产品零件号码
               GIFILE=${GIF_URL #*/}   # 去除了https:/后的图片URL信息,as:/a/b.gif
               GIF_IMG= "${ROOT_DIR}${GIFILE}"  # 图片存到本地后的绝对路径,as:/dataimg/a/b.gif
               U4=` grep  -B 10  '<!-- start opentop -->'  "$HTML_DIR/$URL_DETAIL_MD" | grep  javascript| awk  -F '[<>]'  '{print $3}' `
               ls  $GIF_IMG >&  /dev/null  && wget -q -m -k -P  "$ROOT_DIR"  "$GIF_URL"
               echo  $URL_DETAIL_MD >>  "$URL_MD5"
               echo  "$(date +%m%d%T)+++$X1+++$X2+++$X3+++$U4+++$X5+++$URL_DETAIL+++$URL_DETAIL_MD+++$LABEL+++$PRODUCT_ID+++$GIF_IMG+++$URL_LIST"  >>  "$REPORT"
             fi
           done  < $OPT5   # 传入商品详细列表url信息,进行循环
         done  < $OPT4     # 传入产品部位分类信息,进行循环
       done  < $OPT3       # 传入装饰信息,进行循环
     done  < $OPT2         # 传入车型信息,进行循环
   done  < $OPT1           # 传入品牌信息,进行循环
   echo  >&9
}&
done  < $OPT0             # 传入年份信息,进行循环
 
wait
 
exec  9<&-

       OK!

       以上就是脚本的全部内容,整体脚本主要包含了组合目标URL和抓取目标URL两个大方向,围绕这两个大方向,主要是使用 curl 来做数据抓取,是用sed、awk、grep、cut来做兴趣数据的抽取。

       由于所要抓取的目标URL必须经过几个选项匹配,最终才能得到想要结果,因此我们在抓取目标URL之前添加了组合目标URL这一操作。整体这2个方向,我通过多层的while循环嵌套,来实现对参数的复用和一层一层的输入挖掘。

       为了优化速度以及控制速度,采用了 shell 的多进程和数据智能判重的方式。

       采用 shell 的多进程目的是为了增大操作数来缩短整体完成时间,提高抓取效率。

       shell 多进程主要依托 循环 + { } + & 来实现。如果多进程的进程数量有指定数值,那么我们可以使用for和while都而已,如果多进程的进程数量没有指定数值,那么我们最好使用while循环语句。通过将 { }& 嵌套在循环中实现将 {}内的命令群组放到后台去自动执行,然后完成本次 { }& 操作,使得循环可以进入下一次。

       以上并未实现该shell 在后台开启进程数的控制,假设你的需要执行一万次,如果你未控制速度,就可能会导致直接触发着一万次操作,同时放到后台执行,这样对系统是致命的伤害。另一方面,作为爬虫,你对目标网站的并发量也不能太大。出于这两方面的考虑,我们需要控制 shell 多进程每次放入后台执行的数量。针对这一行为,我们主要通过文件描述符来实现。通过新建一临时管道文件,然后为该文件打开一个文件描述符,并为其传递指定数量的空行(本文传递了10个空行),这样做的目的是为了实现对进程并发量的控制。接着,在下面循环中, { }&操作的前面使用read -u9(这里9为本文使用的文件描述符)来从9这个文件描述符中获取一行,如果获取到就能继续往下执行,如果获取不到就在这里等待。

       通过以上的2者结合,就能实现对 shell 多进程的智能管控。

       采用数据智能判重的目的在于,在脚本调试中发现速度的执行瓶颈在于curl的速度,即网络速度,因此一旦脚本异常中断后,恢复之后,又要重复进行curl操作,这样就极大增加了脚本执行时间。因此通过智能判重,完美实现了curl时间消耗过久的以及数据重复采集的问题。以下是数据只能判重的逻辑图:

wKioL1QUnWzyFIJZAAMW8rN7OK8488.jpg

      针对脚本中变量的取值意义,我已经在上面的脚本中进行了详细的注释,这里不在复述。

      其它细枝末节的一些使用方法和技巧,这里不再一一解释。对 shell 感兴趣的朋友可以和我一起交流,一起进步。










本文转自 aaao 51CTO博客,原文链接:http://blog.51cto.com/nolinux/1552472,如需转载请自行联系原作者

目录
相关文章
|
2月前
|
弹性计算 监控 数据库
制造企业ERP系统迁移至阿里云ECS的实例,详细介绍了从需求分析、数据迁移、应用部署、网络配置到性能优化的全过程
本文通过一个制造企业ERP系统迁移至阿里云ECS的实例,详细介绍了从需求分析、数据迁移、应用部署、网络配置到性能优化的全过程,展示了企业级应用上云的实践方法与显著优势,包括弹性计算资源、高可靠性、数据安全及降低维护成本等,为企业数字化转型提供参考。
70 5
|
2月前
|
网络协议 Go
Go语言网络编程的实例
【10月更文挑战第27天】Go语言网络编程的实例
35 7
|
3月前
|
Shell 应用服务中间件 网络安全
|
3月前
|
SQL Shell 数据库
在TDengine容器中创建初始化数据库的Shell命令实例
以上就是在Docker容器环境中部署并初始化TDengine数据库的全过程,希望对你有所帮助。
120 0
|
5月前
|
存储 Linux 网络安全
【Azure 应用服务】App Service For Linux 如何在 Web 应用实例上住抓取网络日志
【Azure 应用服务】App Service For Linux 如何在 Web 应用实例上住抓取网络日志
|
6月前
|
Shell Linux C语言
|
7月前
|
监控 网络协议 安全
Socket网络编程中的常见应用场景与实例分析
Socket网络编程中的常见应用场景与实例分析
|
7月前
|
存储 弹性计算 网络协议
阿里云服务器ECS计算型c7实例详解_网络PPS_云盘IOPS性能参数
阿里云ECS计算型c7实例,基于三代神龙架构,采用Intel Ice Lake CPU,2.7 GHz基频,3.5 GHz全核睿频,提供高性能计算、存储和网络能力。支持vTPM和Enclave特性,适用于高网络负载、游戏、数据分析等场景。实例规格从2核4GB至128核256GB,最大网络收发包可达2400万PPS。详细规格及性能参数见官方页面。
132 1
|
7月前
|
运维 关系型数据库 MySQL
PolarDB产品使用问题之怎么把将客户端所在的网络和实例配置到同一环境去
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。
|
7月前
|
数据采集 Python 前端开发
python语言通过简单爬虫实例了解文本解析与读写
python|通过一个简单爬虫实例简单了解文本解析与读写