【6】网络与网页-1-爬虫--scrapy

一、简介

scrapy爬虫框架是实现爬虫的一个软件结构和功能组件集合

是一个半成品,能够帮助用户实现专业网络爬虫

‘5+2’ 结构

看结构: 分布式

看流程: 数据流

数据流的路径:

  1. Engine从Spider处获得爬取请求(Request)
  2. Engine将爬取请求转发给Scheduler,用于调度
  3. Engine从Scheduler处获得下一个要爬取的请求
  4. Engine将爬取请求通过中间件发送给Downloader
  5. 爬取网页后,Downloader形成响应(Response) 通过中间件发给Engine
  6. Engine将收到的响应通过中间件发送给Spider处理
  7. Spider处理响应后产生爬取项(scraped Item) 和新的爬取请求(Requests)给Engine
  8. Engine将爬取项发送给Item Pipeline(框架出口)
  9. Engine将爬取请求发送给Scheduler

Engine控制各模块数据流,不间断从Scheduler处 获得爬取请求,直至请求为空

框架入口:Spider的初始爬取请求

框架出口:Item Pipeline

Engine

* 控制所有模块之间的数据流  
* 根据条件触发事件
* 不需要用户修改

Downloader

* 根据请求下载网页
* 不需要用户修改

Scheduler

  • 对所有爬取请求进行调度管理
  • 不需要用户修改

Downloader Middleware

  • 实施Engine、Scheduler和Downloader 之间进行用户可配置的控制
  • 修改、丢弃、新增请求或响应
  • 用户可以编写配置代码

Spider

  • 解析Downloader返回的响应(Response)
  • 产生爬取项(scraped item)
  • 产生额外的爬取请求(Request)
  • 需要用户编写配置代码

Item Pipelines

  • 以流水线方式处理Spider产生的爬取项
  • 由一组操作顺序组成,类似流水线,每个操作是一个Item Pipeline类型
  • 可能操作包括:清理、检验和查重爬取项中的HTML数据、将数据存储到数据库
  • 需要用户编写配置代码

Spider Middleware

  • 对请求和爬取项的再处理
  • 修改、丢弃、新增请求或爬取项
  • “5+2”结构 Spider Middleware

安装:

pip install scrapy

查看是否安装成功

scapy -h

二、requests库和库的比较

相同点

  • 两者都可以进行页面请求和爬取,Python爬虫的两个重要技术路线
  • 两者可用性都好,文档丰富,入门简单
  • 两者都没有处理js、提交表单、应对验证码等功能(可扩展)
requests           scrapy
页面级爬虫       网站级爬虫
功能库 框架
并发性考虑不足,性能较差 并发性好,性能较高
重点在于页面下载 重点在于爬虫结构
定制灵活 一般定制灵活,深度定制困难
上手十分简单 入门稍难

非常小的需求,requests库

不太小的需求,Scrapy框架

定制程度很高的需求(不考虑规模),自搭框架,requests > Scrapy

三、scrapy爬虫的常用命令

为什么Scrapy采用命令行创建和运行爬虫?

命令行(不是图形界面)更容易自动化,适合脚本控制 本质上,Scrapy是给程序员用的,功能(而不是界面)更重要

四、scrapy项目的建立

1.建立一个Scrapy爬虫工程

    scrapy startproject python123demo

2.在工程中产生一个Scrapy爬虫

cd python123demo/
scrapy genspider demo python123.io

该命令作用:

(1) 生成一个名称为demo的spider

(2) 在spiders目录下增加代码文件demo.py

该命令仅用于生成demo.py,该文件也可以手工生成

# -*- coding: utf-8 -*-
import scrapy
class DemoSpider(scrapy.Spider):
        name = "demo"
        allowed_domains = ["python123.io"]
        start_urls = ['http://python123.io/']

        def parse(self, response):
                pass

3.配置产生的spider爬虫

(1)初始URL地址 (2)获取页面后的解析方式

# -*- coding: utf-8 -*-
import scrapy
class DemoSpider(scrapy.Spider):
        name = "demo"
        #allowed_domains = ["python123.io"]
        start_urls = ['http://python123.io/ws/demo.html']
        def parse(self, response):
                fname = response.url.split('/')[-1]
                with open(fname, 'wb') as f:
                        f.write(response.body)
                self.log('Saved file %s.' % name)

demo.py的完整写法

# -*- coding: utf-8 -*-
import scrapy
class DemoSpider(scrapy.Spider):
        name = "demo"
        #allowed_domains = ["python123.io"]
        def start_requests(self):
             urls = ['http://python123.io/ws/demo.html]
             for url in urls:
                    yield scrapy.Request(url=url,callback=self.parse)

        def parse(self, response):
                fname = response.url.split('/')[-1]
                with open(fname, 'wb') as f:
                        f.write(response.body)
                self.log('Saved file %s.' % name)

4.运行爬虫,获取网页

 scrapy crawl demo

五、yield

包含yield语句的函数是一个生成器

生成器每次产生一个值(yield语句),函数被冻结,被唤醒后再产生一个值

生成器是一个不断产生值的函数

def gen(n):
    for i in range(n):
        yield i**2

for i in gen(5):
    print i

生成器每调用一次在yield位置产生一个值,直到函数执行结束

生成器相比一次列出所有内容的优势

  1. 更节省存储空间
  2. 响应更迅速
  3. 使用更灵活

六、scrapy爬虫使用的步骤

  1. 创建一个工程和Spider模板
  2. 编写Spider
  3. 编写Item Pipeline
  4. 优化配置策略

scrapy爬虫的数据类型

Request类:

class scrapy.http.Request()

Request对象表示一个HTTP请求,由Spider生成,由Downloader执行

属性或方法 说明

.url Request对应的请求URL地址
.method 对应的请求方法,'GET' 'POST'等
.headers 字典类型风格的请求头
.body 请求内容主体,字符串类型
.meta 用户添加的扩展信息,在Scrapy内部模块间传递信息使用 .copy() 复制该请求

Response类:

class scrapy.http.Response()

Response对象表示一个HTTP响应

由Downloader生成,由Spider处理

属性或方法 说明

.url Response对应的URL地址
.status HTTP状态码,默认是200
.headers Response对应的头部信息
.body Response对应的内容信息,字符串类型
.flags 一组标记
.request 产生Response类型对应的Request对象
.copy() 复制该响应

Item类

class scrapy.item.Item()

Item对象表示一个从HTML页面中提取的信息内容

由Spider生成,由Item Pipeline处理

Item类似字典类型,可以按照字典类型操作

Scrapy爬虫支持多种HTML信息提取方法:

  • Beautiful Soup
  • lxml
  • re
  • XPath Selector
  • CSS Selector

CSS Selector

<HTML>.css('a::attr(href)').extract()
标签名称 标签属性
CSS Selector由W3C组织维护并规范

七、案例分析

1.爬取股票信息

1.建立工程

    scrapy startproject BaiduStocks
    cd BaiduStocks/
    scrapy genspider stocks baidu.com

2.进一步修改spiders中的stocks.py

# -*- coding: utf-8 -*-
import scrapy
import re


class StocksSpider(scrapy.Spider):
        name = "stocks"
        start_urls = ['http://quote.eastmoney.com/stocklist.html']

        def parse(self, response):
                for href in response.css('a::attr(href)').extract():
                        try:
                                stock = re.findall(r"[s][hz]\d{6}", href)[0]
                                url = 'https://gupiao.baidu.com/stock/' + stock + '.html'
                                yield scrapy.Request(url, callback=self.parse_stock)
                        except:
                                continue

        def parse_stock(self, response):
                infoDict = {}
                stockInfo = response.css('.stock-bets')
                name = stockInfo.css('.bets-name').extract()[0]
                keyList = stockInfo.css('dt').extract()
                valueList = stockInfo.css('dd').extract()
                for i in range(len(keyList)):
                        key = re.findall(r'>.*</dt>', keyList[i])[0][1:-5]
                        try:
                                val = re.findall(r'\d+\.?.*</dd>', valueList[i])[0][0:-5]
                        except:
                                val = '--'
                        infoDict[key]=val

                infoDict.update(
                        {'股票名称': re.findall('\s.*\(',name)[0].split()[0] + \
                         re.findall('\>.*\<', name)[0][1:-1]})
                yield infoDict

3.编写Pipelines

配置pipelines.py的文件

定义对爬取项(scraped item的)的处理类

下面是pipelines.py文件源代码:

# -*- coding: utf-8 -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html


class BaidustocksPipeline(object):
        def process_item(self, item, spider):
                return item

class BaidustocksInfoPipeline(object):
        def open_spider(self, spider):
                self.f = open('BaiduStockInfo.txt', 'w')

        def close_spider(self, spider):
                self.f.close()

        def process_item(self, item, spider):
                try:
                        line = str(dict(item)) + '\n'
                        self.f.write(line)
                except:
                        pass
                return item

配置ITEM_PIPELINES选项

下面是settings.py文件中被修改的区域:

# Configure item pipelines
# See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
        'BaiduStocks.pipelines.BaidustocksInfoPipeline': 300,
}

4.启动程序

scrapy crawl stocks

5.优化速度

settings.py文件

CONCURRENT_REQUESTS Downloader最大并发请求下载数量,默认32
CONCURRENT_ITEMS Item Pipeline最大并发ITEM处理数量,默认100
CONCURRENT_REQUESTS_PER_DOMAIN 每个目标域名最大的并发请求数量,默认8
CONCURRENT_REQUESTS_PER_IP 每个目标IP最大的并发请求数量,默认0,非0有效

八、我的案例

参考资料:

北京理工大学 嵩天老师的课件

个人公众号,比较懒,很少更新,可以在上面提问题,如果回复不及时,可发邮件给我: tiehan@sina.cn

Sam avatar
About Sam
专注生物信息 专注转化医学