Scrapy是一个用Python写的Crawler Framework,简单轻巧,并且非常方便。Scrapy使用Twisted这个异步网络库来处理网络通信,架构清晰,并且包含了各种中间件接口,可以灵活地完成各种需求。
引擎负责控制数据流在系统的所有组件中流动,并在相应动作发生时触发事件。
调度器从引擎接受Request并将它们入队,以便之后引擎请求request时提供给引擎。
下载器负责获取页面数据并提供给引擎,而后提供给Spider。
Spider是Scrapy用户编写用于分析Reponse并提取Item(即获取到的需要的数据)或额外跟进的URL的类。每一个Spider负责处理一个特定(或一些)网站。
Item Pipeline负责处理被Spider提取出来的Item。典型的处理有:清理验证及持久化(例如储存到数据库中)
下载器中间件是在引擎及下载器之间的特定钩子,处理下载器传递给引擎的Response。其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。
Spider中间件是在引擎及Spider之间的特定钩子,吹了Spider的输入(response)和输出(Items和Requests)。其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。
通过数据流的流向,我们可以清楚地看到Scrapy的工作流程。
Scrapy的工作流程由执行引擎(Engine)控制,其过程如下:
1.引擎打开一个网站,找到处理该网站的Spider并向该Spider请求第一个要爬取的URL
2.引擎把这个URL转化为Request对象,并把这个Request对象传递给调度器(Scheduler)
3.引擎向调度器请求下一个要爬取的URL,并把该URL请求通过下载器中间件(Downloader middlewares)传递给下载器(Downloader)
4.等待网页下载完毕,下载器会生成一个Response对象,并将其通过下载器中间件返回引擎,引擎通过Spider中间件把该Response传递给Spider
5.Spider处理Response并提取出数据或者URL,生成Items和Requests并移交给引擎。
6.引擎将Items传递给Item Pipeline,将Requests传递给调度器。
7.重复3-7,直到调度器中没有更多的Request,引擎关闭该网站。
安装scrapy(Python 2.7)
pip install scrapy
windows系统还要安装pywin32
pip install pywin32
初始化项目
scrapy startproject demo
项目文件就会自动被创建和初始化
初始化项目后,项目的结构是这样的:
items.py 定义一些储存数据的类
middlewares.py 定义中间件(下载器中间件、Spider中间件)
pipelines.py 定义处理Items的类
settings.py 顾名思义,scrapy全局设置
spiders/ 文件夹下放爬虫文件,一个py文件就是一个spider
scrapy.cfg 一般不需要改
1.items.py 定义一条数据的结构
# -*- coding: utf-8 -*-
# Define here the models for your scraped items
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/items.html
import scrapy
class FangItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
title = scrapy.Field()
area = scrapy.Field()
price = scrapy.Field()
2.pipelines.py
# -*- coding: utf-8 -*-
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html
class DemoPipeline(object):
def process_item(self, item, spider):
self.cu.execute("INSERT INTO fang VALUES(NULL, ?, ?, ?)", (item['title'], item['price'], item['area']))
self.conn.commit()
return item
def __init__(self):
import sqlite3
self.conn = sqlite3.connect('fang_data.db')
self.cu = self.conn.cursor()
self.cu.execute("CREATE TABLE fang (id INTEGER PRIMARY KEY AUTOINCREMENT ,"
"title TEXT, "
"price FLOAT, "
"area TEXT)")
3.settings.py
激活pipeline
# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
'demo.pipelines.DemoPipeline': 300,
}
4.编写spider
spiders/fang.py
# coding=utf-8
import scrapy
from demo.items import FangItem
class FangSpider(scrapy.Spider):
name = "fang" # 爬虫的名称
allowed_domains = ["fang.com"] # 允许的域名
start_urls = ["http://esf.nc.fang.com/"] # 首次爬取的URL
def parse(self, response):
# 处理首次爬取的结果
areas = response.xpath("//div[@class='qxName']/a")
areas = areas[1:]
areaUrls = ['http://esf.nc.fang.com' + area.xpath("@href").extract()[0] for area in areas]
print(areaUrls)
print(len(areaUrls))
for i, area in enumerate(areas):
if i == 0:
continue
areaName = area.xpath("text()")[0].extract()
areaUrl = area.xpath("@href")[0].extract()
areaRequest = scrapy.Request('http://esf.nc.fang.com' + areaUrl, meta={'areaName': areaName}, callback=self.parse_area)
yield areaRequest
def parse_area(self, response):
# 处理各个地区的页面
nextPage = response.xpath("//a[@id='PageControl1_hlk_next']/@href")
if nextPage:
nextPage = nextPage[0].extract()
nextPageRequest = scrapy.Request('http://esf.nc.fang.com' + nextPage, meta=response.meta, callback=self.parse_area)
print(nextPage)
yield nextPageRequest
houseList = response.xpath("//div[@class='houseList']/dl")
for house in houseList:
title = house.xpath("//p[@class='title']/a/text()")[0].extract()
price = house.xpath("//span[@class='price']/text()")[0].extract()
item = FangItem(title=title, price=price, area=response.meta['areaName'])
yield item