一圃郊、實驗目標
使用scrapy框架采集Curlie網(wǎng)站下Kids_and_Teens的分類子目錄結(jié)構(gòu)以及此分類下所收集的所有站點的網(wǎng)站名靡狞、網(wǎng)站鏈接及網(wǎng)站簡介。
擬突破的重難點
- 破解網(wǎng)站的反爬蟲策略
- 跟蹤URL進行多層網(wǎng)頁鏈接的爬取(crawlSpider或者遞歸)
- 使用pandas進行數(shù)據(jù)清洗與處理
二谚中、實驗環(huán)境
- 操作系統(tǒng):CentOS 7.4 64位
- python環(huán)境:python2.7.5與python2.7.12兩版本共存
- 采用的爬蟲框架:scrapy1.5.0
三、實驗方案設計
①分析目標網(wǎng)頁,制定爬取規(guī)則----》②編寫python代碼----》③不斷調(diào)試代碼宪塔,得到初步結(jié)果----》④數(shù)據(jù)清洗磁奖,生成最終的結(jié)果文件
四、實驗過程
1. 分析目標網(wǎng)頁某筐,制定爬取規(guī)則
此頁面有14個子類比搭,進入Arts分類網(wǎng)頁。
爬取流程如下圖南誊,即爬取多層網(wǎng)頁的結(jié)構(gòu)數(shù)據(jù)身诺。
2. 編寫python代碼
- items.py
# -*- coding: utf-8 -*-
import scrapy
class KidsItem(scrapy.Item):
cate_path = scrapy.Field()
sites = scrapy.Field()
在編寫具體的爬蟲代碼時,本小組設計了兩種方案抄囚。
第一種方案是在當網(wǎng)頁
plan A:scrapy.Spider遞歸爬取網(wǎng)頁數(shù)據(jù)
- 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("http://section[@class='results sites']").extract()
if sites:
sitelist=list()
for lp in response.xpath("http://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("http://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爬取網(wǎng)頁
- 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. 調(diào)試代碼霉赡,得到初步結(jié)果
經(jīng)過漫長的調(diào)試,最后我們的代碼終于能夠成功運行了幔托。
晚上睡覺之前在創(chuàng)建的screen里面打開了爬蟲同廉,然后C-a d退出,將爬蟲程序置于后臺運行柑司。
第二天八點起床的時候打開電腦迫肖,登錄Xshell之后,輸入
screen -r
,再次進入爬蟲界面攒驰,發(fā)現(xiàn)kidsSpider已經(jīng)finished了蟆湖。嘻嘻~~~
-
plan A 爬取結(jié)果
plan A結(jié)束部分截圖
可以看到爬取了7135條items
-
plan B爬取結(jié)果
plan B結(jié)束界面截圖
可以看到一共爬取了6705條item,request請求的最大深度居然有16層玻粪。
兩種方案爬取的結(jié)果不太一樣隅津,應該是因為使用的代理IP池不同,訪問某些網(wǎng)頁的時候被重定向的情況有所不同劲室。
用editplus打開爬取的數(shù)據(jù):
plan B爬取的部分json數(shù)據(jù)
使用JsonViwer查看具體的層級結(jié)構(gòu):
JsonViwer查看爬取的數(shù)據(jù)的部分截圖
經(jīng)過瀏覽發(fā)現(xiàn)伦仍,cate_path的順序是亂的,并且有一些重復的目錄很洋,需要進一步的數(shù)據(jù)清洗充蓝。
4. 數(shù)據(jù)清洗,得到最終結(jié)果
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') #輸入目錄結(jié)構(gòu)文件cate.csv
df.to_json('results.json')
4)清洗后的最終結(jié)果
一共去除了1022條重復數(shù)據(jù)协怒,如下圖:
爬取的目錄結(jié)構(gòu)涝焙,詳見category.csv文件,部分截圖如下:
爬取的網(wǎng)站信息孕暇,詳見results.json文件仑撞,部分截圖如下:
注:文件中的“0”赤兴、“1”、“2”....等是該site所在目錄的索引號隧哮。
五搀缠、調(diào)試過程
1.如何利用遞歸遍歷所有目錄
curlie.org此網(wǎng)站下每個目錄下都有很多層級子目錄,由于要求爬取所有子目錄及其目錄下的sites信息近迁。所以,需要用到遞歸的算法進行遍歷簸州。參考教程
存在一些目錄下并沒有sites鉴竭,因此對sites進行了if判斷。并且對sites進行整合岸浑,將一個目錄下的sites整合到一個list()中搏存。改進后的代碼見下圖:
2.破解目標網(wǎng)站的反爬蟲機制
早就聽聞,爬蟲-反爬蟲-反反爬蟲....是爬蟲蜘蛛與網(wǎng)站運維人員之間的一場永無止境的斗爭矢洲。這一次璧眠,終于遇上了。
curlie的反爬蟲機制有封禁IP读虏、服務器端網(wǎng)頁重定向等责静,在我們的本次實驗中遇到的問題有:
- TCP connection timed out: 110:
- Connection was refused by other side: 111: Connection refused.
- DEBUG: Redirecting (301)永久重定向
- DEBUG: Redirecting (302)暫時重定向
- .......
為了解決上述問題,本小組采取的解決辦法有:
- 設置隨機user-agent盖桥,偽造user發(fā)送請求
- 設置隨機IP代理
- 設置下載延遲
- 設置不遵循robots.txt協(xié)議
- 禁用cookie
六灾螃、爬蟲策略的進一步探索
- 實驗的不足之處:不知道是否已經(jīng)目標目錄下的所有的站點信息;網(wǎng)站的目錄結(jié)構(gòu)存成json或者xml的樹狀結(jié)構(gòu)更能體現(xiàn)目錄之間的層級關(guān)系揩徊。
為了解決上述問題腰鬼,小組進行了爬蟲策略的進一步探索。
1.用crontab實現(xiàn)定時執(zhí)行scrapy任務
crontab命令常見于Unix和類Unix的操作系統(tǒng)之中塑荒,用于設置周期性被執(zhí)行的指令熄赡。下面是使用crontab進行定時循環(huán)執(zhí)行scrapy爬蟲的操作:
安裝crontab
yum install crontab
編輯crontab服務文件
crontab -e
(其他參數(shù):-u 指定用戶下的crontab; -r 刪除齿税; -l 查看crontab工程目錄彼硫; -i 有提示的刪除)在crontab文件中添加命令以爬取name為ACW1的爬蟲
* * * * * cd /home/hyj/kids && scrapy crawl ACW1 -o ACW1.json
詳細寫法參見Linux下的crontab定時執(zhí)行任務命令詳解
以上指令可以實現(xiàn)每分鐘執(zhí)行一次爬蟲。但是在爬蟲比較多時凌箕,可以采用shell腳本集中管理乌助。
若爬蟲運行時間較長,又想錯開目標網(wǎng)站的訪問高峰時間段陌知,可結(jié)合scrapy jobs進行暫停爬取他托,之后再恢復運行,接著上一次的斷點繼續(xù)爬取數(shù)據(jù)仆葡。
遇到的一些問題
如果直接在crontab -e中輸入* * * * * scrapy crawl xxx
赏参,定時任務是不會生效的志笼,因為我們不知道crontab執(zhí)行時,其所處的目錄把篓,很有可能就沒有scrapy命令和crawl命令纫溃。因此,改用指令:* * * * * cd /home/hyj/kids && scrapy crawl ACW1
就可以正常的執(zhí)行定時任務了韧掩。-
查看crontab歷史執(zhí)行情況
默認情況下,crontab中執(zhí)行的日志寫在/var/log下紊浩。可通過以下指令在root權(quán)限下訪問:
tail –f /var/log/cron
crontab日志
2.爬取curlie全站的策略
-
分析網(wǎng)站首頁疗锐,改寫爬蟲代碼
curlie首頁
可以看到首頁上的打開分類目錄的下一層鏈接都在名為"top cat"的標簽里面坊谁,之后每一個大類的網(wǎng)站結(jié)構(gòu)都跟kids_and_teens的結(jié)構(gòu)一樣。因此滑臊,只要在crawlSpider的rules對象中將網(wǎng)站url的匹配規(guī)則改寫一下就可以了口芍。代碼改動部分有一下兩處:
-
爬蟲的執(zhí)行
在首頁上可以看到curlie收錄的站點約370萬個,分別位于約100萬個目錄下雇卷。
站點總數(shù)
在沒有網(wǎng)站的反爬蟲限制鬓椭,用一臺服務器執(zhí)行爬蟲指令,遵循crawl_delay=1的協(xié)議关划,一切順利的話小染,大約17天能爬下來所有的站點數(shù)據(jù)。
3.分布式爬蟲spider-redis
為了滿足在較短時間內(nèi)爬取大量數(shù)據(jù)的需求贮折,分布式的爬蟲應運而生氧映,在爬取curlie全站的時候也可以采用分布式的爬蟲。要搭建分布式爬蟲需要多臺服務器:
- 一臺中心服務器master:不執(zhí)行具體的爬蟲任務脱货,只負責request任務分配岛都、將數(shù)據(jù)存入redis數(shù)據(jù)庫、判斷url是否重復等工作
- 多臺爬蟲執(zhí)行服務器Slaver:負責執(zhí)行爬蟲程序振峻,運行過程中提交新的Request給中心服務器
Slaver從中心服務器拿任務(Request臼疫、url)進行數(shù)據(jù)抓取,在抓取數(shù)據(jù)的同時扣孟,產(chǎn)生新任務的Request便提交給 Master 處理烫堤;
Master端只有一個Redis數(shù)據(jù)庫,負責將未處理的Request去重和任務分配凤价,將處理后的Request加入待爬隊列鸽斟,并且存儲爬取的數(shù)據(jù)。