Scrapy,Python開發(fā)的一個(gè)快速、高層次的屏幕抓取和web抓取框架
用于抓取web站點(diǎn)并從頁面中提取結(jié)構(gòu)化的數(shù)據(jù)
Scrapy用途廣泛,可以用于數(shù)據(jù)挖掘、監(jiān)測(cè)和自動(dòng)化測(cè)試
Scrapy吸引人的地方在于它是一個(gè)框架,任何人都可以根據(jù)需求方便的修改
它也提供了多種類型爬蟲的基類
如BaseSpider、sitemap爬蟲等,最新版本又提供了web2.0爬蟲的支持
創(chuàng)新互聯(lián)建站是一家專注于成都做網(wǎng)站、成都網(wǎng)站建設(shè)與策劃設(shè)計(jì),珠海網(wǎng)站建設(shè)哪家好?創(chuàng)新互聯(lián)建站做網(wǎng)站,專注于網(wǎng)站建設(shè)十年,網(wǎng)設(shè)計(jì)領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:珠海等地區(qū)。珠海做網(wǎng)站價(jià)格咨詢:028-86922220
Scrapy Engine(引擎): 分配任務(wù)給其他模塊,負(fù)責(zé)其它模塊之間的通信,數(shù)據(jù)傳遞等
Scheduler(調(diào)度器): 接受引擎發(fā)送的Request請(qǐng)求,整理入隊(duì),當(dāng)需要時(shí)返回給引擎
Downloader(下載器): 從引擎處接收并下載調(diào)度器整理后返回的Requests請(qǐng)求,并將獲取的Responses返回給引擎
Spider(爬蟲): 提供初始網(wǎng)址,接收并處理從引擎處接收下載器返回的Responses
分析并提取Item需要的數(shù)據(jù)返回給管道,并將需要跟進(jìn)的網(wǎng)址url提交給引擎
Item Pipeline(管道):它負(fù)責(zé)處理Spider中獲取到的Item,并進(jìn)行進(jìn)行后期處理(詳細(xì)分析、過濾、存儲(chǔ)等)的地方。
Downloader Middlewares(下載中間件): 你可以當(dāng)作是一個(gè)可以自定義擴(kuò)展下載功能的組件
Spider Middlewares(Spider中間件): 你可以理解為是一個(gè)可以自定擴(kuò)展和操作引擎和Spider中間通信的功能組件
(比如進(jìn)入Spider的Responses;和從Spider出去的Requests)
->Spider 提交初始爬取網(wǎng)址相關(guān)信息給Engine
->Engine根據(jù)Spider提交的數(shù)據(jù)發(fā)起Requests給Scheduler
->Scheduler將接收到的Requests進(jìn)行整理入隊(duì),再返還給Engine
->Engine將整理后的Requests請(qǐng)求發(fā)送給Downloader
->Downloader將Requests請(qǐng)求提交給網(wǎng)站服務(wù)器并將服務(wù)器返回的Responses交給Engine
->Engine將Responses交給Spider進(jìn)行處理
->Spider將從Responses中提取Item字段需要的信息和需要跟進(jìn)的url
信息交給pipelines,url則提交給Engine,進(jìn)行下一次爬取
->pipelines將完成對(duì)信息的分析,篩選和存儲(chǔ)等工作。
在Scheduler整理的Requests請(qǐng)求隊(duì)列全部執(zhí)行并處理完畢后,程序結(jié)束。
由于在Engine主要用于個(gè)模塊之間的信息傳遞,可以簡化工作流程如下:
Spider發(fā)送初始url ---------------> Scheduler整理請(qǐng)求并入隊(duì)(Engine發(fā)起請(qǐng)求)
Scheduler 發(fā)送整理后的請(qǐng)求 ----------------->Downloader向網(wǎng)址提交請(qǐng)求并獲取responses
Downloader發(fā)送獲取的responses ------------------>Spider分析并提取Item所需信息和需要跟進(jìn)的url
Spider發(fā)送Item所需信息 ----------------->pipelines分析,篩選,存儲(chǔ)信息
Spider發(fā)送需要跟進(jìn)的url -----------------> Scheduler整理請(qǐng)求并入隊(duì)(Engine發(fā)起請(qǐng)求)
pip install scarpy
1.新建項(xiàng)目 (scrapy startproject projectname)
2.確定目標(biāo) (編寫items.py)(即編寫需要獲取的Item字段)
3.制作爬蟲 (編寫spiders/xxspider.py)(分析responses并提取數(shù)據(jù))
4.存儲(chǔ)內(nèi)容 (編寫pipelines.py)(分析篩選數(shù)據(jù)并儲(chǔ)存)
命令:scrapy startproject projectname
projectname為需要指定的項(xiàng)目名
進(jìn)入項(xiàng)目并利用tree命令輸出項(xiàng)目結(jié)構(gòu)
scrapy.cfg: 項(xiàng)目的配置文件。
TestSpider/: 項(xiàng)目的Python模塊,將會(huì)從這里引用代碼。
TestSpider/items.py: 項(xiàng)目的目標(biāo)文件。
TestSpider/pipelines.py: 項(xiàng)目的管道文件。
TestSpider/settings.py: 項(xiàng)目的設(shè)置文件。
TestSpider/spiders/: 存儲(chǔ)爬蟲代碼目錄。
我們以爬取菜鳥教程為例,網(wǎng)址: http://www.runoob.com/
需要的數(shù)據(jù)為 教程名 圖片url 簡要描述 教程url
編輯items.py如下:
Item定義了一個(gè)結(jié)構(gòu)化數(shù)據(jù)字段,類似于字典,用于保存爬取到的數(shù)據(jù)
import scrapy
class TestspiderItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
name = scrapy.Field() # 教程名
img_url = scrapy.Field() # 圖片地址
desc = scrapy.Field() # 描述
url = scrapy.Field() # 教程鏈接
在項(xiàng)目目錄中輸入命令
命令:scrapy genspider spidername 'start_url'
spidername 為需要指定的爬蟲名稱
start_url為初始爬取地址
此時(shí)會(huì)在spiders目錄中創(chuàng)建spidername.py文件,并添加必須的代碼
import scrapy
class RunoobSpider(scrapy.Spider):
name = 'runoob'
allowed_domains = ['www.runoob.com']
start_urls = ['http://www.runoob.com/']
def parse(self, response):
pass
當(dāng)然,使用命令不是必需的,也可以選擇自己創(chuàng)建和編寫
但使用命令幫我們免去了寫一些必須代碼的麻煩
在此基礎(chǔ)上,根據(jù)我們的需求編寫爬蟲代碼
編寫代碼如下:
import scrapy
# 導(dǎo)入在items中寫好的類
from TestSpider.items import TestspiderItem
# 編寫爬蟲
class RunoobSpider(scrapy.Spider):
name = 'runoob' # 文件名
allowed_domains = ['www.runoob.com'] # 允許訪問的網(wǎng)址
start_urls = ['http://www.runoob.com/'] # 開始訪問的網(wǎng)址
def parse(self, response):
course = TestspiderItem() # 實(shí)例化一個(gè)Item數(shù)據(jù)對(duì)象
# 獲取class為"item-top item-1"的節(jié)點(diǎn)
courseInfos = response.xpath('//a[@class="item-top item-1"]')
# 遍歷節(jié)點(diǎn)
for courseInfo in courseInfos:
# 根據(jù)需求提取指定數(shù)據(jù)并存入Item的對(duì)象中
course['url'] = courseInfo.xpath('@href')[0].extract()
course['name'] = courseInfo.xpath('.//h5/text()')[0].extract()
course['img_url'] = courseInfo.xpath('.//img/@src')[0].extract()
course['desc'] = courseInfo.xpath('.//strong/text()')[0].extract()
# 輸出測(cè)試文件觀察獲取數(shù)據(jù)是否正確
#open('test.log','w').write('%s\n%s\n%s\n%s'%(type(course['url']),course['name'],type(course['img_url']),type(course['desc'])))
# 返回?cái)?shù)據(jù)
yield course
查看test.log中的數(shù)據(jù)
scrapy crawl spidername -o spidername.(json|jsonl|csv|xml)
以json / json lines / csv / xml格式存儲(chǔ)在當(dāng)前路徑下
存儲(chǔ)的csv文件,默認(rèn)按照ASCII碼編碼順序排列
定義一個(gè)管道類,完成寫入操作
>>>>>>>>
保存至文件,保存json格式數(shù)據(jù),文件名為runoob.txt
class TestspiderPipeline(object):
# 以‘只寫’方式打開runoob.txt文件
def __init__(self):
self.f = open('runoob.txt','w')
# pipeline中執(zhí)行的程序
def process_item(self, item, spider):
# 測(cè)試語句,item返回的是獲取到的Item數(shù)據(jù)類型(前面定義過的類型)
# open('runoob.log','w').write(str(type(item)))
# 存儲(chǔ)為json格式,不使用ascii編碼,縮進(jìn)為4
import json
line = json.dumps(dict(item),ensure_ascii=False,indent=4)
self.f.write(line+'\n')
return item
# 關(guān)閉文件
def close_spider(self):
self.f.close()
>>>>>>>>
保存至MySQL數(shù)據(jù)庫
創(chuàng)建數(shù)據(jù)庫runoob并指定utf8編碼格式(create database runoob default charset=utf8;)
class MysqlPipeline(object):
def __init__(self):
# 構(gòu)造時(shí)鏈接數(shù)據(jù)庫
import pymysql
self.conn =pymysql.connect(
host='localhost',
user='root',
password ='redhat',
database ='runoob',
charset ='utf8',
autocommit = True
)
# 創(chuàng)建游標(biāo)
self.cur = self.conn.cursor()
# 創(chuàng)建數(shù)據(jù)表
create_sqli = 'create table if not exists course(教程名稱 varchar(50),鏈接 varchar(300),教程簡介 varchar(200))'
self.cur.execute(create_sqli)
def process_item(self, item, spider):
# 插入數(shù)據(jù)
insert_sqli = 'insert into course values("%s","%s","%s") '%(item['name'],item['url'],item['desc'])
self.cur.execute(insert_sqli)
return item
def close_spider(self):
# 關(guān)閉游標(biāo)和連接
self.cur.close()
self.conn.close()
>>>>>>>>
保存媒體圖片
# 圖片存儲(chǔ)
class ImagePipeline(ImagesPipeline):
# 獲取媒體請(qǐng)求
def get_media_requests(self, item, info):
# 測(cè)試語句
# open('mooc.log','w').write(item['img_url'])
# 返回圖片
yield scrapy.Request(item['img_url'])
# results是返回的一個(gè)元組(True ,{'url':xxx,'path':xxx,'checksum':xxx})
# info返回的是一個(gè)對(duì)象scrapy.pipelines.media.MediaPipeline.SpiderInfo
def item_completed(self, results, item, info):
# 測(cè)試語句
# for i in results:
# open('ni.txt', 'w').write(str(i)+'\n'+str(info))
# 獲取results中的path
image_path = [x['path'] for ok,x in results if ok]
# path為None,則不包含圖片,否則返回item
if not image_path:
raise Exception('不包含圖片')
else:
return item
保存圖片還需要在settings.py中設(shè)置圖片保存的路徑
管道默認(rèn)不執(zhí)行,需要在settings.py中修改設(shè)置
后面的數(shù)字設(shè)定優(yōu)先級(jí),數(shù)字越小,優(yōu)先級(jí)越高
在工程路徑中輸入命令
命令: scrapy crawl spidername
spidername為爬蟲文件名
文件
數(shù)據(jù)庫
圖片