scrapy简单使用

scrapy 爬虫框架总结

一、常用命令

创建scrapy项目
scrapy startproject weather
创建Spider
scrapy genspider Cqtianqi http://weather.cma.cn #蜘蛛名 顶级域名
运行蜘蛛
scrapy crawl CQtianqi

二、工作组件及流程

2.1 图解流程

在这里插入图片描述

2.2 组件介绍

七大组件、五个关键

Scrapy Engine

引擎负责控制数据流在系统中所有组件中流动,并在相应动作发生时触发事件。

调度器(Scheduler)

调度器从引擎接受request并将他们入队,以便之后引擎请求他们时提供给引擎。

下载器(Downloader)

下载器负责获取页面数据并提供给引擎,而后提供给spider。

Spiders

Spider是Scrapy用户编写用于分析response并提取item(即获取到的item)或额外跟进的URL的类。 每个spider负责处理一个特定(或一些)网站。 更多内容请看 Spiders 。

Item Pipeline

Item Pipeline负责处理被spider提取出来的item。典型的处理有清理、 验证及持久化(例如存取到数据库中)。 更多内容查看 Item Pipeline 。

下载器中间件(Downloader middlewares)

下载器中间件是在引擎及下载器之间的特定钩子(specific hook),处理Downloader传递给引擎的response(也包括引擎传递给下载器的Request)。 其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。更多内容请看 下载器中间件(Downloader Middleware) 。

一句话总结就是:处理下载请求部分

Spider中间件(Spider middlewares)

Spider中间件是在引擎及Spider之间的特定钩子(specific hook),处理spider的输入(response)和输出(items及requests)。 其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。更多内容请看 Spider中间件(Middleware) 。

一句话总结就是:处理解析部分

2.3 数据流(Data flow)

Scrapy中的数据流由执行引擎控制,其过程如下:

  1. 引擎打开一个网站(open a domain),找到处理该网站的Spider并向该spider请求第一个要爬取的URL(s)。
  2. 引擎从Spider中获取到第一个要爬取的URL并在调度器(Scheduler)以Request调度。
  3. 引擎向调度器请求下一个要爬取的URL。
  4. 调度器返回下一个要爬取的URL给引擎,引擎将URL通过下载中间件(请求(request)方向)转发给下载器(Downloader)。
  5. 一旦页面下载完毕,下载器生成一个该页面的Response,并将其通过下载中间件(返回(response)方向)发送给引擎。
  6. 引擎从下载器中接收到Response并通过Spider中间件(输入方向)发送给Spider处理。
  7. Spider处理Response并返回爬取到的Item及(跟进的)新的Request给引擎。
  8. 引擎将(Spider返回的)爬取到的Item给Item Pipeline,将(Spider返回的)Request给调度器。
  9. (从第二步)重复直到调度器中没有更多地request,引擎关闭该网站。

三、详解各模块作用

spider

两个重要参数、一个方法、两种方式

✳️ allowed_domains:这是scrapy框架中规定爬取的url的范围,简单地说就是,我们只能在这个参数定义的url的范围下获取数据,一旦数据超出范围,那么我们的请求将会失效。

✴️ start_urls:这是scrapy框架起始访问的url,也即在最初向这个url的位置发起请求。

parse(self,response) 中,这个函数是当我们发起请求成功后的一个回调函数,如果不理解回调函数的不要紧,我们只需要知道,这个函数的触发时机是请求已经被正确处理了。于是我们可以拿到函数的传参:response的值,而这个response的值就是服务器给我们的响应。

1️⃣ response.xpath(xpath语句传入):这种方式是对response进行xpath解析,我们在括号内传入xpath语法对应的语句即可,要注意的是,普通的xpath解析,返回的是一个列表,但是在scrapy框架中的xpath解析,返回的是selector对象列表,针对selector对象列表,我们需要进一步的处理,才能真正拿到数据。

2️⃣ response.extract():这种处理,承接了上面的操作,当我们拿到了selector对象列表,通过再执行.extract()函数,即可把selector对象列表转成普通的列表,进而获取数据。

import scrapy
from weather.items import WeatherItem
class CqtianqiSpider(scrapy.Spider):
    name = 'CQtianqi'
    
    allowed_domains = ['weather.cma.cn']
    start_urls = []
    start_urls.append('http://weather.cma.cn/web/weather/57083.html/')
    
    def parse(self, response):
        n_url = response.xpath('//*[@id="cityPosition"]/div[5]/ul//a/@href').extract()
        for i in n_url:
            real_url = 'http://weather.cma.cn' + ''.join(i) + '/'
            yield scrapy.Request(real_url, callback=self.parse)
    
     date = response.xpath('//*[@id="dayList"]/div/div[1]/text()[2]').extract()
     week = response.xpath('//*[@id="dayList"]/div/div[1]/text()[1]').extract()
     
 	 item = WeatherItem()
     item['date'] = date
     item['week'] = week
     yield item
parse()方法工作机制及应用
  1. 因为使用的yield,而不是return。parse函数将会被当做一个生成器使用。scrapy会逐一获取parse方法中生成的结果,并判断该结果是一个什么样的类型;
  2. 如果是request则加入爬取队列,如果是item类型则使用pipeline处理,其他类型则返回错误信息。
  3. scrapy取到第一部分的request不会立马就去发送这个request,只是把这个request放到队列里,然后接着从生成器里获取;
  4. 取尽第一部分的request,然后再获取第二部分的item,取到item了,就会放到对应的pipeline里处理;
  5. parse()方法作为回调函数(callback)赋值给了Request,指定parse()方法来处理这些请求 scrapy.Request(url, callback=self.parse)
  6. Request对象经过调度,执行生成 scrapy.http.response()的响应对象,并送回给parse()方法,直到调度器中没有Request(递归的思路)
  7. 取尽之后,parse()工作结束,引擎再根据队列和pipelines中的内容去执行相应的操作;
  8. 程序在取得各个页面的items前,会先处理完之前所有的request队列里的请求,然后再提取items。

items.py

作用:从非结构化的数据源中提取出结构化的数据
Scrapy支持以下类型的项目,通过 itemadapter 类库:

  1. dictionaries
  2. Item objects
  3. dataclass objects
  4. attrs objects

我们选用可自定义字段名称的Item对象来提取数据
定义Item非常简单,只需要继承scrapy.Item类,并将所有字段都定义为scrapy.Field类型即可

# items.py
from scrapy.item import Item, Field

class WeatherItem(Item):
date = Field()
week = Field()
Cqtianqi.py
......
#与之对应
 item = WeatherItem()
     item['date'] = date
     item['week'] = week
     yield item

其他三种的使用看这里

pipelines.py

每一个Item Pipeline都有三个方法,process_item(self,item,spider)最为重要

(1)process_item(self, item, spider)
每一个Item Pipeline都会调用这个方法,用来处理Item,返回值为item或dict。
这个方法还可以抛出一个DropItem异常,这样将会不再继续调用接下来的Item Pipeline。
参数item(Item对象或者Dict) 是parse方法传来的。
参数spider(Spider对象) - 抓取这个Item的Spider。

(2)open_spider(self, spider)
​​​​​​​这个方法将会在Spider打开时调用。

(3)close_spider(self, spider)
​​​​​​​这个方法将会在Spider关闭时调用。

import os,json
import pymysql
import requests
from scrapy import Request

pathdir = os.getcwd()
class WeatherPipeline(object):

    def process_item(self, item, spider):
        base_dir = os.getcwd()
        # 文件存在data目录下的weather.txt文件内
        fiename = pathdir + '\\data\\weather.txt'
        # 从内存以追加的方式打开文件,并写入对应的数据
        with open(fiename, 'a', encoding='utf8') as f:
            for i in range(7):
                f.write('日期:' + item['date'][i] + '\n')
                f.write('星期:' + item['week'][i] + '\n')
        return item

还需在setting文件中配置

ITEM_PIPELINES = {
    'weather.pipelines.WeatherPipeline': 300,
}

settings.py

各变量的作用

# -*- coding: utf-8 -*-
 
# Scrapy settings for demo1 project
#
# For simplicity, this file contains only settings considered important or
# commonly used. You can find more settings consulting the documentation:
#
#     http://doc.scrapy.org/en/latest/topics/settings.html
#     http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html
#     http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html
 
BOT_NAME = 'weather'   #Scrapy项目的名字,这将用来构造默认 User-Agent,同时也用来log,当您使用 startproject 命令创建项目时其也被自动赋值。
 
SPIDER_MODULES = ['weather.spiders']   #Scrapy搜索spider的模块列表 默认: [xxx.spiders]
NEWSPIDER_MODULE = 'weather.spiders'   #使用 genspider 命令创建新spider的模块。默认: 'xxx.spiders'
 
 
#爬取的默认User-Agent,除非被覆盖,需要改,模拟正常用户访问
#USER_AGENT = 'demo1 (+http://www.yourdomain.com)'
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'

 
#如果启用,Scrapy将会采用 robots.txt策略,建议不遵循
ROBOTSTXT_OBEY = False
 
#Scrapy downloader 并发请求(concurrent requests)的最大值,默认: 16
CONCURRENT_REQUESTS = 32
 
#为同一网站的请求配置延迟(默认值:0)
# See http://scrapy.readthedocs.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs  
#DOWNLOAD_DELAY = 3   下载器在下载同一个网站下一个页面前需要等待的时间,该选项可以用来限制爬取速度,减轻服务器压力。同时也支持小数:0.25 以秒为单位
 
    
#下载延迟设置只有一个有效
#CONCURRENT_REQUESTS_PER_DOMAIN = 16   对单个网站进行并发请求的最大值。
#CONCURRENT_REQUESTS_PER_IP = 16       对单个IP进行并发请求的最大值。如果非0,则忽略 CONCURRENT_REQUESTS_PER_DOMAIN 设定,使用该设定。 也就是说,并发限制将针对IP,而不是网站。该设定也影响 DOWNLOAD_DELAY: 如果 CONCURRENT_REQUESTS_PER_IP 非0,下载延迟应用在IP而不是网站上。
 
#禁用Cookie(默认情况下启用)
#COOKIES_ENABLED = False
 
#禁用Telnet控制台(默认启用)
#TELNETCONSOLE_ENABLED = False 
 
#覆盖默认请求标头:
#DEFAULT_REQUEST_HEADERS = {
#   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
#   'Accept-Language': 'en',
#}
 
#启用或禁用蜘蛛中间件
# See http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html
#SPIDER_MIDDLEWARES = {
#    'demo1.middlewares.Demo1SpiderMiddleware': 543,
#}
 
#启用或禁用下载器中间件
# See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html
#DOWNLOADER_MIDDLEWARES = {
#    'demo1.middlewares.MyCustomDownloaderMiddleware': 543,
#}
 
#启用或禁用扩展程序
# See http://scrapy.readthedocs.org/en/latest/topics/extensions.html
#EXTENSIONS = {
#    'scrapy.extensions.telnet.TelnetConsole': None,
#}
 
#配置项目管道
# See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
   'weather.pipelines.WeatherPipeline': 300,
}
 
#启用和配置AutoThrottle扩展(默认情况下禁用)
# See http://doc.scrapy.org/en/latest/topics/autothrottle.html
#AUTOTHROTTLE_ENABLED = True
 
#初始下载延迟
#AUTOTHROTTLE_START_DELAY = 5
 
#在高延迟的情况下设置的最大下载延迟
#AUTOTHROTTLE_MAX_DELAY = 60
 
 
#Scrapy请求的平均数量应该并行发送每个远程服务器
#AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
 
#启用显示所收到的每个响应的调节统计信息:
#AUTOTHROTTLE_DEBUG = False
 
#启用和配置HTTP缓存(默认情况下禁用)
# See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
#HTTPCACHE_ENABLED = True
#HTTPCACHE_EXPIRATION_SECS = 0
#HTTPCACHE_DIR = 'httpcache'
#HTTPCACHE_IGNORE_HTTP_CODES = []
#HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
 

中文文档

scrapy中文文档

四、其他问题

域名和url的区别

域名

域名是由一串用“.”分隔的名字组成的Internet上某一台计算机或计算机组的名称。
作用:用来将主机名和域名转换为IP地址的工作。

URL

URL以字符串的抽象形式来描述一个资源在万维网上的地址。一个URL唯一标识一个Web资源,通过与之对应的URL即可获得该资源。可以用来定位网页,多媒体文件等

常用的URL格式如下:协议类型://服务器地址[:端口号]/路径/文件名[参数=]

URL和域名的区别
域名,Domain Name,通常指一个网址的顶级域名。
URL,website address,网页或网站的地址。

URL中包含了网站的域名.
比如一个网址:www.cnblogs.com/gopark/p/8430916.html
其中cnblogs.com是域名,cnblogs是网站名字,com是域名后缀;www.cnblogs.com代表一个二级域名,通常www被用来用为首页标识;
https://www.cnblogs.com/gopark/p/8430916.html,这个则是一个完整的网站首页URL地址。https://,这是一个协议,是网站在网上传输的协议。

scrapy如何保证不重复请求同一个url,如何改变

scrapy默认去重、一般无需修改
(1)跟进的url不重复时
当spider返回需要跟进的url时,如果没有指定dont_filter的值,那么默认为False,即自动过滤重复请求,当需要重复请求同一个url时,需要将dont_filter的值设为True

# 默认过滤重复请求
scrapy.Request(url=addr, callback=self.self.parse)
# 设置dont_filter = True不过滤
scrapy.Request(url=addr, callback=self.new_parse, dont_filter=True)

(2)
若在 scrapy 中,不过滤任何 request 请求,可以自定义如下文件

from scrapy.dupefilter import RFPDupeFilter
class CloseDupefilter(RFPDupeFilter):
    def request_seen(self, request):
        return False

然后在settings.py中添加如下代码

DUPEFILTER_CLASS = 'scraper.duplicate_filter.CustomFilter'

增量爬取时如需重复爬取可参考
增量去重爬取

回调函数

字面上的理解,回调函数就是一个参数,将这个函数作为参数传到另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数。这个过程就叫做回调。

x-path语言

用途:提取网页数据
通常返回选择器对象

表达式描述
/根节点选取或下级
//任意节点,不考虑位置
.当前节点
当前节点的父节点
@选取属性
*匹配任何节点
[]根据节点筛选
contains(@属性,“包含的内容”)模糊查询
text()文本内容

3.案例

取得class为classify的h3标签下a标签内的内容
//h3[@class="classify"]/a/text()
利用模糊查询查找class类名中包含classify_list的所有div标签下的span下的a标签的文本内容
//div[contains(@class,"classify_list")]/span/a/text()

MutableMapping

映射类型

映射类型

什么是可散列对象

如果一个对象是可散列的,那么在这个对象的生命周期中,他的散列值是不会变的(它需要实现__hash__()方法)。它可以与其他对象作比较(还需实现__eq__()方法)。如果一个可散列对象与另一个可散列对象是相等的,那么他们的散列值hash value一定是相等的。

原子不可变数据类型(str,bytes和数值类型)都是hashable类型,frozenset也是hashable的,因为根据其定义,frozenset里只可容纳可散列类型。元组也是hashable的,但只有当元组包含的所有元素都是hashable类型的情况下它才是可散列的。
简单来说,如果一个对象是可散列的数据类型的话,那它应是不可变的。

test = (1,2,(3,4))
hash(test)
-2725224101759650258
test1 = (1,2,[3,4])
hash(test1)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: unhashable type: 'list'

list等可变对象是不可散列的,因为随着数据的改变他们的哈希值会变化导致进入错误的哈希表。

一般用户自定义的类型的对象都是可散列的,散列值就是它们的id()函数的返回值,所以所有这些对象在比较的时候都是不想等的。如果一个对象实现了__eq__()方法,并且在方法中用到了这个对象的内部状态的话,那么只有当所有这些内部状态都是不可变的情况下,这个对象才是可散列的。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值