Scrapy快速入门实战

Scrapy架构图

创建一个Scrapy项目

1.创建项目文件夹tutorial

scrapy startproject tutorial

2.创建Spider类

Spider的用法

使用命令行创建一个Spider

cd tutorial
scrapy genspider quotes quotes.toscrape.com

第一个参数是Spider的名称,第二个参数是网站域名
执行完毕后,spider文件夹中多了一个quotes.py。

  • name 项目的名字,用于区分不同的Spider
  • allowed_domains,允许爬取的域名
  • start_urls,它包含了Spider在启动时爬取的url列表,初始请求是由它来定义的。
  • parse,Spider的一个方法,负责解析下载start_urls里面的连接构成的请求后返回的相应、提取数据或者进一步生成要处理的请求
# -*- coding: utf-8 -*-
import scrapy
from scrapyspider.items import QuoteItem


class QuotesSpider(scrapy.Spider):
    name = 'quotes'
    allowed_domains = ['quotes.toscrape.com']
    start_urls = ['http://quotes.toscrape.com/']

    def parse(self, response):
        quotes = response.css('.quote')
        for quote in quotes:
            item = QuoteItem()
            item['item'] = quote.css('.text::text').extract_first()
            item['author'] = quote.css('.author::text').extract_first()
            item['tags'] = quote.css('.tags::text').extract()
            yield item

        next = response.css('.pager .next a::attr(href)').extract_first()
        url = response.urljoin(next)
        yield scrapy.Request(url=url,callback=self.parse)

3.创建Item

Item是保存爬取数据的容器。
类似于字典+额外的保护机制,避免拼写错误或者定义字段错误。
创建时需要继承scrapy.Item类,并且定义类型为scrapy.Field的字段。

# -*- 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 QuoteItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    text = scrapy.Field()
    author = scrapy.Field()
    tags = scrapy.Field()

4.解析Response&使用Item

parse()方法中,直接对response变量包含的内容进行解析,解析**text、author、tags。

页面结构
def parse(self, response):
        quotes = response.css('.quote')
        for quote in quotes:
            item = QuoteItem()
            item['item'] = quote.css('.text::text').extract_first()
            item['author'] = quote.css('.author::text').extract_first()
            item['tags'] = quote.css('.tags::text').extract()
            yield item

选择器selector:CSS或XPath

Scrapy提供了两个实用的快捷方法
response.xpath()和response.css()
二者功能完全等同于
response.selector.xpath()和response.selector.css()

CSS

这里使用CSS选择器,给出CSS选择器的基础使用规则。

选择器 例子 含义
.class .aaa 选择class="aaa"的所有元素
#id #firstname 选择 id="firstname" 的所有元素
* 选择所有元素
element p 选择所有<p>元素
element element div p 选择 <div> 元素内部的所有 <p> 元素
a[title] 选取所有拥有title属性的a元素


对于text
extract_first()方法获取结果的第一个元素,可以避免空列表取[0]导致数组越界。extract_first('Default Image')匹配不到结果时返回该参数Default Image
extract()方法获取所有结果组成的列表

XPath

表达式 描述
nodename 选取此节点的所有子节点
/ 从当前节点选取直接子节点
// 从当前节点选取子孙节点
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性/属性获取
[ ] 增加限制条件
text() 文本获取

属性多值匹配-某些节点的某个属性可能有多个值。

使用contains()函数,第一个参数传入属性名称,第二个参数传入属性值,只要此属性包含所传入的属性值,就可以完成匹配了。
例如代码:

from lxml import etree
text = '''
<li class="li li-first"><a href="link.html">first item</a></li>
html = etree.HTML(text)
result = html.xpath('//li[contains(@class,"li")]/a/text()')
print(result)

//运行结果
['first item']

多属性匹配-根据多个属性确定一个节点

from lxml import etree
text = '''
<li class="li li-first" name="item"><a href="link.html">first item</a></li>
html = etree.HTML(text)
result = html.xpath('//li[contains(@class,"li") and @name="item"]/a/text()')
print(result)

//运行结果
['first item']
运算符及其介绍

正则匹配

response对象不能直接调用re()和re_first()方法。如果想要对全文进行正则匹配,可以先调用xpath()方法再正则匹配。

5.后续Request

上面操作实现从初始页面抓取内容。

  1. 我们需要从当前页面中找到信息来生成下一个请求
  2. 在下一个请求的页面里找到信息再构造再下一个请求。
  3. 这样循环往复迭代,从而实现整站的爬取。
    Next按钮

    基于按钮的链接:http://quotes.toscrape.com/page/2
    利用scrapy.Request构造下一个请求
scrapy.Request两个参数
  • url 请求链接
  • callback 回调函数。当指定了该回调函数的请求完成后,获取到响应,引擎会将该响应作为参数传递给这个回调函数。回调函数进行解析或生成下一个请求,回调函数如上文的

parse()所示。
由于每页的结构相同,可以再次使用parse()方法来做页面解析

next = response.css('.pager .next a::attr(href)').extract_first()
        url = response.urljoin(next)
        yield scrapy.Request(url=url,callback=self.parse)
  1. 获取a超链接中的href属性,这里用到了::attr(href)操作。然后再调用extract_first()方法获取内容。
  2. urljoin()方法,将相对URL构造成一个绝对的URL(补全URL)
  3. 利用scrapy.Request构造新请求

6.运行

scrapy crawl quotes

这里运行的时候有各种可能的原因导致不能从目标获取内容

  1. 网址的错误
  2. 格式错误如缩进问题
  3. 网页有反爬虫,
  • 在settings文件中添加USER_AGENT
  • 添加IP代理
  • 改机器人协议以及cookie
  • 设置延迟
  1. 分段函数中所要爬取的url有反爬虫,可以添加print(requests.get(url).status_code)调试

错误排查

7.保存到文件

Scrapy提供的Feed Exports可以轻松抓取结果输出。

#json
scrapy crawl quotes -o quotes.json
#jsonline 每一个Item输出一行JSON
scrapy crawl quotes -o quotes.jl
scrapy crawl quotes -o quotes.csv
scrapy crawl quotes -o quotes.xml
scrapy crawl quotes -o quotes.pickle
scrapy crawl quotes -o quotes.marshal
#ftp 远程输出
scrapy crawl quotes -o ftp://user::pass@ftp.example.com/path/to/quotes.csv

8.使用Item Pipeline(项目管道)

调用发生在Spider产生Item之后。

当Spider解析完Response之后,Item就会传递到Item Pipeline,被定义的Item Pipeline组件会顺次调用,完成一连串的处理过程,比如数据清洗、存储等。

主要功能有4个:

  • 清理HTML数据
  • 验证爬取数据,检查爬取字段。
  • 查重并丢弃重复内容。
  • 将爬取结果保存到数据库

将结果保存到MongoDB或者筛选Item。

核心方法

  • 必须要实现的一个方法是:process_item(item,spider)
    被定义的Item Pipeline会默认调用这个方法对Item进行处理。
    返回:Item类型的值(此Item会被低优先级的Item Pipeline的process_item()方法处理,直到所有的方法被调用完毕)或者抛出一个DropItem异常(丢弃该Item,不再进行处理)。
  • 实用方法
    open_spider(self,spider)
    在Spider开启的时候被自动调用,可以在这里做一些初始化操作,如开启数据库连接等。
  • 实用方法
    close_spider(spider)
    在Spider关闭时自动调用,可以在这里做一些收尾工作,如关闭数据库等。
  • 实用方法
    from_crawler(cls,crawler)
    是一个类方法,用@classmethod标识,是一种依赖注入的方式。可以通过crawler对象拿到Scrapy的所有核心组件。
    参数:cls就是Class
    返回:Class实例

9.Downloader Middleware的用法

下载中间件,是处于Scrapy的Request和Response之间的处理模块。

  1. 在Scheduler调度出队列的Request发送给Downloader下载之前,也就是可以在Request执行下载之前对其进行修改。
  2. 下载后生成的Response发送给Spider之前,可以在生成Response被Spider解析之前对其进行修改。
    内置的Downloader Middleware

优先级问题
数字越小越靠近Scrapy引擎,数字越大越靠近Downloader,数字小的Downloader Middleware会被优先调用。

返回类型不同,产生的效果也不同。

  • None。Scrapy继续处理该Request,按照设置的优先级顺序依次执行其他Downloader Middleware的process_request()方法对Request进行修改,一直到Downloader把Request执行后得到Response才结束。
  • Response对象。不再继续调用更低优先级的Downloader Middleware的process_request()和process_exception()方法,每个Downloader Middleware的process_response()方法转而被依次调用。调用完毕之后,直接将Response对象发送给Spider来处理。
  • Request对象。停止执行更低优先级的Downloader Middleware的process_request()方法。将此Request重新放到调度队列里,相当于一个全新的Request,等待被调度。
  • IgnoreRequest异常抛出。所有的Downloader Middleware的process_exception()方法会依次执行。若没有一个方法处理这个异常,则Request的errorback()方法就会回调。如果该异常还没有被处理,那么忽略。

核心方法有三个,只需要实现至少一个方法,就可以定义一个Downloader Middleware

  • process_request(request, spider)
    Request被Scrapy引擎调度给Downloader之前,该方法会被调用。也就是在Request从队列里调度出来到Downloader下载执行之前,我们都可以用process_request()方法对Request进行处理。
    返回:None/Response/Request/抛出IgnoreRequest异常。
  • process_response(request, response, spider)
    收到Response后,在Scrapy引擎将Response发送给Spider进行解析之前,可以用该方法对Response进行处理。
    返回:Request对象/Response对象/抛出IgnoreRequest异常。
  • process_exception(request, exception, spider)
    当Downloader或process_request()方法抛出异常时该方法被调用。
    返回:None/Response对象/Request对象。

修改请求时的User-Agent的两种方式:

  1. 只需要在setting.py中添加一行USER_AGENT的定义即可。
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'
  1. 设置随机的User-Agent,需要借助Downloader Middlewareprocess_request()方法。
    在middlewares.py里面添加一个RandomUserAgentMiddleware的类,如下所示:
import random

class RandomUserAgentMiddleware():
  def __init__(self):
    self.user_agents = [    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
    "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)",
    "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
    "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)",
    "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
    "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)",
    "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)",
    "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)",
    "Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6",
    "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1",
    "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0",
    "Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5",
    "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20",
    "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52"
    ]
  def process_request(self, request, spider):
    request.headers['User-Agent'] = random.choice(self.user_agents)

要使之生效,我们需要去调用这个Downloader Middleware。在settings.py中将DOWNLOADER_MIDDLEWARES取消注释并设置成如下内容:

DOWNLOADER_MIDDLEWARES = { 'scrapydownloadertest.middlewares.RandomUserAgentMiddleware': 543,
}

注意:scrapydownloadertest是你项目的文件夹名称,记得修改哦。

10. Spider Middleware的用法

scrapy提供了内置的基础Spider Middleware。如果想要扩展其功能,只需要实现某几个方法即可。
每个Spider Middleware都定义了以下一个或多个方法的类,只需要实现其中一个方法就可以定义一个Spider Middleware,核心方法有如下4个:

  • process_spider_input(response,spider)
    当Response被Spider Middleware处理时,该方法被调用。
    参数:response,即被处理的Response;spider,即该Response对应的Spider。
    返回:None(继续处理Response,调用其他的Spider Middleware,指导Spider处理该Response)或抛出一个异常(调用Request的errback()方法,errback的输出重新输入到中间件中,使用process_spider_output()方法来处理,当其抛出异常时则调用process_spider_exception()来处理)
  • process_spider_output(response,result,spider)
    当Spider处理Response返回结果时,process_spider_output()方法被调用。
    返回:包含Request或Item对象的可迭代对象。
  • process_spider_exception(response,exception,spider)
    当Spider或Spider Middleware的process_spider_input()方法抛出异常时,该方法被调用。
    返回:None(Scrapy继续调用其他Spider Middleware中的process_spider_exception()方法处理该异常,直到所有Spider Middleware都被调用);可迭代对象(其他Spider Middleware的process_spider_output()方法被调用)
  • process_start_requests(start_requests,spider)
    以Spider启动的Request为参数被调用,执行过程类似于process_spider_output(),但没有相关联的Response,并且必须返回Request。
    返回:必须返回一个包含Request对象的可迭代对象。

可迭代对象:Python中经常使用for来对某个对象进行遍历,此时被遍历的这个对象就是可迭代对象,像常见的list,tuple都是。如果给一个准确的定义的话,就是只要它定义了可以返回一个迭代器的__ iter __ 方法,或者定义了可以支持下标索引的__ getitem __方法(这些双下划线方法会在其他章节中全面解释),那么它就是一个可迭代对象。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容