实验报告【爬取Curlie网站信息】

一、实验目标

使用scrapy框架采集Curlie网站下Kids_and_Teens的分类子目录结构以及此分类下所收集的所有站点的网站名、网站链接及网站简介。

拟突破的重难点

  1. 破解网站的反爬虫策略
  2. 跟踪URL进行多层网页链接的爬取(crawlSpider或者递归)
  3. 使用pandas进行数据清洗与处理

二、实验环境

  • 操作系统:CentOS 7.4 64位
  • python环境:python2.7.5与python2.7.12两版本共存
  • 采用的爬虫框架:scrapy1.5.0

三、实验方案设计

①分析目标网页,制定爬取规则----》②编写python代码----》③不断调试代码,得到初步结果----》④数据清洗,生成最终的结果文件

四、实验过程

1. 分析目标网页,制定爬取规则

首页的子类解析

此页面有14个子类,进入Arts分类网页。


ARTS

爬取流程如下图,即爬取多层网页的结构数据。


数据爬取流程

2. 编写python代码

  • items.py
# -*- coding: utf-8 -*-

import scrapy
class KidsItem(scrapy.Item):
    cate_path = scrapy.Field()
    sites = scrapy.Field()

在编写具体的爬虫代码时,本小组设计了两种方案。
第一种方案是在当网页

plan A:scrapy.Spider递归爬取网页数据

  • kidsspiderA.py
# -*- coding: utf-8 -*-
# Please refer to the documentation for information on how to create and manage
# your spiders.
import scrapy
from kids.items import *

class Kids(scrapy.Spider):
  name="kidsspider"
  allowed_domains=["curlie.org"]
  start_urls=[
    'http://curlie.org/Kids_and_Teens/',
  ]

  def parse(self,response):
    item=KidsItem()
    item['cate_path']=response.url.strip('http://curlie.org')
    sites=response.xpath("//section[@class='results sites']").extract()
    if sites:
      sitelist=list()
      for lp in response.xpath("//div[@id='site-list-content']/div"):
        s={}
        s['site_url']=lp.xpath("div[@class='title-and-desc']/a/@href").extract_first()
        s['site_title']=lp.xpath("div[@class='title-and-desc']/a/div/text()").extract_first()
        s['site_desc']=lp.xpath("div[@class='title-and-desc']/div[@class='site-descr ']/text()").extract_first().strip()
        sitelist.append(s)
      item['sites']=sitelist
    yield item

    for kids in response.xpath("//div[@id='subcategories-div']/section[@class='children']/div[@class='cat-list results leaf-nodes']/div"):
      link=kids.xpath("a/@href").extract()
      if link:
        link="http://curlie.org" + link[0]  #要爬取的下一个链接
        yield scrapy.Request(link, callback=self.parse)

plan B 通过Crawlspider爬取网页

  • kidsSpiderB.py
import scrapy
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
from kids.items import *

class Kids(CrawlSpider):
    name = 'kidsSpider3'
    allowed_domains = ['curlie.org']
    start_urls = ['http://curlie.org/Kids_and_Teens/']

    rules = [
        Rule(LinkExtractor(
            restrict_css=('.cat-item')
        ), callback='parse_site', follow=True),
    ]

    def parse_site(self, response):
        item=KidsItem()
        item['cate_path']=response.url.strip('http://curlie.org/')

        sites=list()
        for div in response.css('.title-and-desc'):
            site={}
            site['site_title']=div.css('.site-title::text').extract_first()
            site['site_desc']= div.css('.site-descr::text').extract_first().strip()
            site['site_url']=div.css('a::attr(href)').extract_first()
            sites.append(site)

        item['sites']=sites
        yield item

3. 调试代码,得到初步结果

经过漫长的调试,最后我们的代码终于能够成功运行了。

晚上睡觉之前在创建的screen里面打开了爬虫,然后C-a d退出,将爬虫程序置于后台运行。
第二天八点起床的时候打开电脑,登录Xshell之后,输入
screen -r,再次进入爬虫界面,发现kidsSpider已经finished了。嘻嘻~~~

  • plan A 爬取结果


    plan A结束部分截图

    可以看到爬取了7135条items

  • plan B爬取结果


    plan B结束界面截图

    可以看到一共爬取了6705条item,request请求的最大深度居然有16层。
    两种方案爬取的结果不太一样,应该是因为使用的代理IP池不同,访问某些网页的时候被重定向的情况有所不同。
    用editplus打开爬取的数据:


    plan B爬取的部分json数据

    使用JsonViwer查看具体的层级结构:
    JsonViwer查看爬取的数据的部分截图

    经过浏览发现,cate_path的顺序是乱的,并且有一些重复的目录,需要进一步的数据清洗。

4. 数据清洗,得到最终结果

1)目标:去重、cate_path字段排序,导出cate_path字段。
2)工具: pandas库
3)清洗过程

  • 安装pandas库


    安装pandas
  • 编写代码
import pandas as pd
df =pd.read_json('/home/lly/kids/kids_and_teens.json') # 读取json文件
print df.info()

#去重
df.duplicated("cate_path") #判断是否有重复项
df=df.drop_duplicates("cate_path") #去掉重复项
print df.info() #查看去重是否成功

#排序
df=df.sort_values(by=['cate_path']) #按照cate_path进行排序
df['cate_path'].to_csv('category.csv') #输入目录结构文件cate.csv

df.to_json('results.json')

4)清洗后的最终结果
一共去除了1022条重复数据,如下图:


去重结果

爬取的目录结构,详见category.csv文件,部分截图如下:


目录结构部分截图

爬取的网站信息,详见results.json文件,部分截图如下:
results.json部分截图

注:文件中的“0”、“1”、“2”....等是该site所在目录的索引号。

五、调试过程

1.如何利用递归遍历所有目录

curlie.org此网站下每个目录下都有很多层级子目录,由于要求爬取所有子目录及其目录下的sites信息。所以,需要用到递归的算法进行遍历。参考教程
存在一些目录下并没有sites,因此对sites进行了if判断。并且对sites进行整合,将一个目录下的sites整合到一个list()中。改进后的代码见下图:

递归.png

2.破解目标网站的反爬虫机制

早就听闻,爬虫-反爬虫-反反爬虫....是爬虫蜘蛛与网站运维人员之间的一场永无止境的斗争。这一次,终于遇上了。
curlie的反爬虫机制有封禁IP、服务器端网页重定向等,在我们的本次实验中遇到的问题有:

  • TCP connection timed out: 110:
  • Connection was refused by other side: 111: Connection refused.
  • DEBUG: Redirecting (301)永久重定向
  • DEBUG: Redirecting (302)暂时重定向
  • .......

为了解决上述问题,本小组采取的解决办法有:

  • 设置随机user-agent,伪造user发送请求
  • 设置随机IP代理
  • 设置下载延迟
  • 设置不遵循robots.txt协议
  • 禁用cookie

六、爬虫策略的进一步探索

  • 实验的不足之处:不知道是否已经目标目录下的所有的站点信息;网站的目录结构存成json或者xml的树状结构更能体现目录之间的层级关系。
    为了解决上述问题,小组进行了爬虫策略的进一步探索。

1.用crontab实现定时执行scrapy任务

crontab命令常见于Unix和类Unix的操作系统之中,用于设置周期性被执行的指令。下面是使用crontab进行定时循环执行scrapy爬虫的操作:

  • 安装crontab
    yum install crontab

  • 编辑crontab服务文件
    crontab -e
    (其他参数:-u 指定用户下的crontab; -r 删除; -l 查看crontab工程目录; -i 有提示的删除)

  • 在crontab文件中添加命令以爬取name为ACW1的爬虫
    * * * * * cd /home/hyj/kids && scrapy crawl ACW1 -o ACW1.json
    详细写法参见Linux下的crontab定时执行任务命令详解

以上指令可以实现每分钟执行一次爬虫。但是在爬虫比较多时,可以采用shell脚本集中管理。
若爬虫运行时间较长,又想错开目标网站的访问高峰时间段,可结合scrapy jobs进行暂停爬取,之后再恢复运行,接着上一次的断点继续爬取数据。

  • 遇到的一些问题
    如果直接在crontab -e中输入* * * * * scrapy crawl xxx,定时任务是不会生效的,因为我们不知道crontab执行时,其所处的目录,很有可能就没有scrapy命令和crawl命令。因此,改用指令:* * * * * cd /home/hyj/kids && scrapy crawl ACW1就可以正常的执行定时任务了。

  • 查看crontab历史执行情况
    默认情况下,crontab中执行的日志写在/var/log下。可通过以下指令在root权限下访问:
    tail –f /var/log/cron

    crontab日志

2.爬取curlie全站的策略

  • 分析网站首页,改写爬虫代码


    curlie首页

可以看到首页上的打开分类目录的下一层链接都在名为"top cat"的标签里面,之后每一个大类的网站结构都跟kids_and_teens的结构一样。因此,只要在crawlSpider的rules对象中将网站url的匹配规则改写一下就可以了。代码改动部分有一下两处:


全站爬虫代码改动部分
  • 爬虫的执行
    在首页上可以看到curlie收录的站点约370万个,分别位于约100万个目录下。


    站点总数

    在没有网站的反爬虫限制,用一台服务器执行爬虫指令,遵循crawl_delay=1的协议,一切顺利的话,大约17天能爬下来所有的站点数据。

3.分布式爬虫spider-redis

为了满足在较短时间内爬取大量数据的需求,分布式的爬虫应运而生,在爬取curlie全站的时候也可以采用分布式的爬虫。要搭建分布式爬虫需要多台服务器:

  • 一台中心服务器master:不执行具体的爬虫任务,只负责request任务分配、将数据存入redis数据库、判断url是否重复等工作
  • 多台爬虫执行服务器Slaver:负责执行爬虫程序,运行过程中提交新的Request给中心服务器

Slaver从中心服务器拿任务(Request、url)进行数据抓取,在抓取数据的同时,产生新任务的Request便提交给 Master 处理;

Master端只有一个Redis数据库,负责将未处理的Request去重和任务分配,将处理后的Request加入待爬队列,并且存储爬取的数据。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,123评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,031评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,723评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,357评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,412评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,760评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,904评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,672评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,118评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,456评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,599评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,264评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,857评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,731评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,956评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,286评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,465评论 2 348

推荐阅读更多精彩内容