精通Scrapy网络爬虫(txt+pdf+epub+mobi电子书下载)


发布时间:2021-05-16 07:46:15

点击下载

作者:刘硕

出版社:清华大学出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

精通Scrapy网络爬虫

精通Scrapy网络爬虫试读:

前言

关于本书

如今是互联网的时代,而且正在迈入智能时代。人们早已意识到互联网中的数据是有待开采的巨大金矿,这些数据将会改善我们的生活,网络爬虫开发工作岗位的出现和不断增加正是基于对数据价值的重视。优秀的爬虫框架就像是开采金矿的强力挖掘机,如果你能娴熟地驾驶它们,就能大幅提高开采效率。

本书讲解目前最流行的Python爬虫框架Scrapy,它简单易用、灵活易拓展、文档丰富、开发社区活跃,使用Scrapy可以高效地开发网络爬虫应用。本书的读者只需要有Python语言基础即可,我们从零基础、逐步由浅入深进行讲解。第1~8章讲解Scrapy开发的核心基础部分,其中包括:

● 初识Scrapy

● 编写Spider

● 使用Selector提取数据

● 使用Item封装数据

● 使用Item Pipeline处理数据

● 使用Link Extractor提取链接

● 使用Exporter导出数据

● 项目练习

第9~14章讲解实际爬虫开发中使用频率最高的一些实用技术,其中包括:

● 下载文件和图片

● 模拟登录

● 爬取动态页面

● 存入数据库

● 使用HTTP代理

● 分布式爬取本书特色

本书的宗旨是以实用和实战为教学目标,主要特色是:

● 所有基础部分的讲解都配有代码示例,而不仅仅是枯燥的文档。

● 案例选材方面以讲解知识点为核心,尽量选择专门供练习爬虫技术的网站(不易变动)或贴近日常生活的网站(京东、知乎、豆瓣、360)进行演示。

● 在讲解某些知识点时,对Scrapy源码进行分析,让读者能够“知其然并知其所以然”。

另外,Python是一门简单易学、功能强大、开发效率极高的语言,近年来在网络爬虫、数据分析、机器学习等领域得到广泛认可。虽然Python很容易上手,但想灵活恰当地运用它也并不简单。作者在慕课网(www.imooc.com)上推出了一套《Python高级进阶实战》课程,可供有需求的读者进行参考:http://coding.imooc.com/class/62.html。致谢

感谢康烁和陈渝老师在清华大学信息研究院工作期间对我在专业方面的耐心指导。

感谢清华大学出版社的王金柱编辑给予我这次写作的机会以及在写作方面的指点。

感谢赵佳音同事认真阅读全书并提出了许多的宝贵建议。

感谢剑超和任怡同学认真审阅全书并对书中代码在多个Python版本上进行测试。

感谢女儿刘真,她的笑容化解了写作本书时偶尔的小烦躁。编者2017年8月8日第1章初识Scrapy

本章首先介绍爬虫的基本概念、工作流程,然后介绍Scrapy的安装和网络爬虫项目的实现流程,使读者对网络爬虫有一个大致的了解,并且建立起网络爬虫的编写思路。本章重点讲解以下内容:

● 网络爬虫及爬虫的工作流程。

● Scrapy的介绍与安装。

● 网络爬虫编写步骤。1.1网络爬虫是什么

网络爬虫是指在互联网上自动爬取网站内容信息的程序,也被称作网络蜘蛛或网络机器人。大型的爬虫程序被广泛应用于搜索引擎、数据挖掘等领域,个人用户或企业也可以利用爬虫收集对自身有价值的数据。举一个简单的例子,假设你在本地新开了一家以外卖生意为主的餐馆,现在要给菜品定价,此时便可以开发一个爬虫程序,在美团、饿了么、百度外卖这些外卖网站爬取大量其他餐馆的菜品价格作为参考,以指导定价。

一个网络爬虫程序的基本执行流程可以总结为以下循环:

1.下载页面

一个网页的内容本质上就是一个HTML文本,爬取一个网页内容之前,首先要根据网页的URL下载网页。

2.提取页面中的数据

当一个网页(HTML)下载完成后,对页面中的内容进行分析,并提取出我们感兴趣的数据,提取到的数据可以以多种形式保存起来,比如将数据以某种格式(CSV、JSON)写入文件中,或存储到数据库(MySQL、MongoDB)中。

3.提取页面中的链接

通常,我们想要获取的数据并不只在一个页面中,而是分布在多个页面中,这些页面彼此联系,一个页面中可能包含一个或多个到其他页面的链接,提取完当前页面中的数据后,还要把页面中的某些链接也提取出来,然后对链接页面进行爬取(循环1-3步骤)。

设计爬虫程序时,还要考虑防止重复爬取相同页面(URL去重)、网页搜索策略(深度优先或广度优先等)、爬虫访问边界限定等一系列问题。

从头开发一个爬虫程序是一项烦琐的工作,为了避免因制造轮子而消耗大量时间,在实际应用中我们可以选择使用一些优秀的爬虫框架,使用框架可以降低开发成本,提高程序质量,让我们能够专注于业务逻辑(爬取有价值的数据)。接下来,本书就带你学习目前非常流行的开源爬虫框架Scrapy。1.2Scrapy简介及安装

Scrapy是一个使用Python语言(基于Twisted框架)编写的开源网络爬虫框架,目前由Scrapinghub Ltd维护。Scrapy简单易用、灵活易拓展、开发社区活跃,并且是跨平台的。在Linux、 MaxOS以及Windows平台都可以使用。Scrapy应用程序也使用Python进行开发,目前可以支持Python 2.7以及Python 3.4+版本。

在任意操作系统下,可以使用pip安装Scrapy,例如: $ pip install scrapy

为确认Scrapy已安装成功,首先在Python中测试能否导入Scrapy模块: >>> import scrapy >>> scrapy.version_info (1, 3, 3)

然后,在shell中测试能否执行Scrapy这条命令: $ scrapy Scrapy 1.3.3 - no active project Usage: scrapy [options] [args] Available commands: bench Run quick benchmark test commands fetch Fetch a URL using the Scrapy downloader genspider Generate new spider using pre-defined templates runspider Run a self-contained spider (without creating a project) settings Get settings values shell Interactive scraping console startproject Create new project version Print Scrapy version view Open URL in browser, as seen by Scrapy [ more ] More commands available when run from project directory Use "scrapy -h" to see more info about a command

通过了以上两项检测,说明Scrapy安装成功了。如上所示,我们安装的是当前最新版本1.3.3。1.3编写第一个Scrapy爬虫

为了帮助大家建立对Scrapy框架的初步印象,我们使用它完成一个简单的爬虫项目。1.3.1 项目需求

在专门供爬虫初学者训练爬虫技术的网站(http://books.toscrape.com)上爬取书籍信息,如图1-1所示。图1-1

该网站中,这样的书籍列表页面一共有50页,每页有20本书,第一个例子应尽量简单。我们下面仅爬取所有图书(1000本)的书名和价格信息。1.3.2 创建项目

首先,我们要创建一个Scrapy项目,在shell中使用scrapy startproject命令: $ scrapy startproject example New Scrapy project 'example', using template directory '/usr/local/lib/python3.4/dist-packages/scrapy/templates/project', created in: /home/liushuo/book/example You can start your first spider with: cd example scrapy genspider example example.com

创建好一个名为example的项目后,可使用tree命令查看项目目录下的文件,显示如下:

随着后面逐步深入学习,大家会了解这些文件的用途,此处不做解释。1.3.3 分析页面

编写爬虫程序之前,首先需要对待爬取的页面进行分析,主流的浏览器中都带有分析页面的工具或插件,这里我们选用Chrome浏览器的开发者工具(Tools→Developer tools)分析页面。

1.数据信息

在Chrome浏览器中打开页面http://books.toscrape.com,选中其中任意一本书并右击,然后选择“审查元素”,查看其HTML代码,如图1-2所示。图1-2

可以看到,每一本书的信息包裹在

元素中:书名信息在其下h3 > a元素的title属性中,如A Light in the...;书价信息在其下

元素的文本中,如

£51.77

2.链接信息

图1-3所示为第一页书籍列表页面,可以通过单击next按钮访问下一页,选中页面下方的next按钮并右击,然后选择“审查元素”,查看其HTML代码,如图1-3所示。图1-3

可以发现,下一页的URL在ul.pager > li.next > a元素的href属性中,是一个相对URL地址,如

。1.3.4 实现Spider

分析完页面后,接下来编写爬虫。在Scrapy中编写一个爬虫,即实现一个scrapy.Spider的子类。

实现爬虫的Python文件应位于exmaple/spiders目录下,在该目录下创建新文件book_spider.py。然后,在book_spider.py中实现爬虫BooksSpider,代码如下: # -*- coding: utf-8 -*- import scrapy class BooksSpider(scrapy.Spider): # 每一个爬虫的唯一标识 name = "books" # 定义爬虫爬取的起始点,起始点可以是多个,这里只有一个 start_urls = ['http://books.toscrape.com/'] def parse(self, response): # 提取数据 # 每一本书的信息在

中,我们使用 # css()方法找到所有这样的article 元素,并依次迭代 for book in response.css('article.product_pod'): # 书名信息在article > h3 > a 元素的title属性里 # 例如: A Light in the ... name = book.xpath('./h3/a/@title').extract_first() # 书价信息在

的TEXT中。 # 例如:

£51.77

price = book.css('p.price_color::text').extract_first() yield { 'name': name, 'price': price, } # 提取链接 # 下一页的url 在ul.pager > li.next > a 里面 # 例如: next_url = response.css('ul.pager li.next a::attr(href)').extract_first() if next_url: # 如果找到下一页的URL,得到绝对路径,构造新的Request 对象 next_url = response.urljoin(next_url) yield scrapy.Request(next_url, callback=self.parse)

如果上述代码中有看不懂的部分,大家不必担心,更多详细内容会在后面章节学习,这里只要先对实现一个爬虫有个整体印象即可。

下面对BooksSpider的实现做简单说明。● name属性

一个Scrapy项目中可能有多个爬虫,每个爬虫的name属性是其自身的唯一标识,在一个项目中不能有同名的爬虫,本例中的爬虫取名为'books'。● start_urls属性

一个爬虫总要从某个(或某些)页面开始爬取,我们称这样的页面为起始爬取点,start_urls属性用来设置一个爬虫的起始爬取点。在本例中只有一个起始爬取点'http://books.toscrape.com'。● parse方法

当一个页面下载完成后,Scrapy引擎会回调一个我们指定的页面解析函数(默认为parse方法)解析页面。一个页面解析函数通常需要完成以下两个任务: 提取页面中的数据(使用XPath或CSS选择器)。 提取页面中的链接,并产生对链接页面的下载请求。

页面解析函数通常被实现成一个生成器函数,每一项从页面中提取的数据以及每一个对链接页面的下载请求都由yield语句提交给Scrapy引擎。1.3.5 运行爬虫

完成代码后,运行爬虫爬取数据,在shell中执行scrapy crawl 命令运行爬虫'books',并将爬取的数据存储到csv文件中: $ scrapy crawl books -o books.csv 2016-12-27 15:19:53 [scrapy] INFO: Scrapy 1.3.3 started (bot: example) 2016-12-27 15:19:53 [scrapy] INFO: INFO: Overridden settings: {'BOT_NAME': 'example', 'SPIDER_MODULES': ['example.spiders'], 'ROBOTSTXT_OBEY': True, 'NEWSPIDER_MODULE': 'example.spiders'} 2016-12-27 15:19:53 [scrapy] INFO: Enabled extensions: ['scrapy.extensions.telnet.TelnetConsole', 'scrapy.extensions.corestats.CoreStats', 'scrapy.extensions.feedexport.FeedExporter', 'scrapy.extensions.logstats.LogStats'] 2016-12-27 15:19:53 [scrapy] INFO: Enabled downloader middlewares: ['scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware', 'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware', 'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware', 'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware', 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware', 'scrapy.downloadermiddlewares.retry.RetryMiddleware', 'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware', 'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware', 'scrapy.downloadermiddlewares.redirect.RedirectMiddleware', 'scrapy.downloadermiddlewares.cookies.CookiesMiddleware', 'scrapy.downloadermiddlewares.chunked.ChunkedTransferMiddleware', 'scrapy.downloadermiddlewares.stats.DownloaderStats'] 2016-12-27 15:19:53 [scrapy] INFO: Enabled spider middlewares: ['scrapy.spidermiddlewares.httperror.HttpErrorMiddleware', 'scrapy.spidermiddlewares.offsite.OffsiteMiddleware', 'scrapy.spidermiddlewares.referer.RefererMiddleware', 'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware', 'scrapy.spidermiddlewares.depth.DepthMiddleware'] 2016-12-27 15:19:53 [scrapy] INFO: Enabled item pipelines: [] 2016-12-27 15:19:53 [scrapy] INFO: Spider opened 2016-12-27 15:19:53 [scrapy] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min) 2016-12-27 15:19:53 [scrapy] DEBUG: Telnet console listening on 127.0.0.1:6023 2016-12-27 15:20:01 [scrapy] DEBUG: Crawled (404) (referer: None) 2016-12-27 15:20:02 [scrapy] DEBUG: Crawled (200) (referer: None) 2016-12-27 15:20:02 [scrapy] DEBUG: Scraped from <200 http://books.toscrape.com/> {'name': 'A Light in the Attic', 'price': '£51.77'} 2016-12-27 15:20:02 [scrapy] DEBUG: Scraped from <200 http://books.toscrape.com/> {'name': 'Tipping the Velvet', 'price': '£53.74'} 2016-12-27 15:20:02 [scrapy] DEBUG: Scraped from <200 http://books.toscrape.com/> {'name': 'Soumission', 'price': '£50.10'} ... <省略中间部分输出> ... 2016-12-27 15:21:30 [scrapy] DEBUG: Scraped from <200 http://books.toscrape.com/catalogue/page-50.html> {'name': '1,000 Places to See Before You Die', 'price': '£26.08'} 2016-12-27 15:21:30 [scrapy] INFO: Closing spider (finished) 2016-12-27 15:21:30 [scrapy] INFO: Stored csv feed (1000 items) in: books.csv 2016-12-27 15:21:30 [scrapy] INFO: Dumping Scrapy stats: {'downloader/request_bytes': 14957, 'downloader/request_count': 51, 'downloader/request_method_count/GET': 51, 'downloader/response_bytes': 299924, 'downloader/response_count': 51, 'downloader/response_status_count/200': 50, 'downloader/response_status_count/404': 1, 'finish_reason': 'finished', 'finish_time': datetime.datetime(2016, 12, 27, 7, 21, 30, 10396), 'item_scraped_count': 1000, 'log_count/DEBUG': 1052, 'log_count/INFO': 9, 'request_depth_max': 49, 'response_received_count': 51, 'scheduler/dequeued': 50, 'scheduler/dequeued/memory': 50, 'scheduler/enqueued': 50, 'scheduler/enqueued/memory': 50, 'start_time': datetime.datetime(2016, 12, 27, 7, 19, 53, 194334)} 2016-12-27 15:21:30 [scrapy] INFO: Spider closed (finished)

等待爬虫运行结束后,在books.csv文件中查看爬取到的数据,代码如下: $ sed -n '2,$p' books.csv | cat -n # 不显示第一行的csv 头部 1 A Light in the Attic,£51.77 2 Tipping the Velvet,£53.74 3 Soumission,£50.10 4 Sharp Objects,£47.82 5 Sapiens: A Brief History of Humankind,£54.23 6 The Requiem Red,£22.65 7 The Dirty Little Secrets of Getting Your Dream Job,£33.34 ... <省略中间部分输出> ... 995 Beyond Good and Evil,£43.38 996 Alice in Wonderland (Alice's Adventures in Wonderland #1),£55.53 997 "Ajin: Demi-Human, Volume 1 (Ajin: Demi-Human #1)",£57.06 998 A Spy's Devotion (The Regency Spies of London #1),£16.97 999 1st to Die (Women's Murder Club #1),£53.98 1000 "1,000 Places to See Before You Die",£26.08

从上述数据可以看出,我们成功地爬取到了1000本书的书名和价格信息(50页,每页20项)。1.4本章小结

本章是开始Scrapy爬虫之旅的第1章,先带大家了解了什么是网络爬虫,然后对Scrapy爬虫框架做了简单介绍,最后以一个简单的爬虫项目让大家对开发Scrapy爬虫有了初步的印象。在接下来的章节中,我们将深入学习开发Scrapy爬虫的核心基础内容。第2章编写Spider

从本章开始介绍Scrapy爬虫的基础知识部分,我们先从Scrapy爬虫程序中最核心的组件Spider讲起,在学习编写Spider之前,需要一些知识作为铺垫。本章重点讲解以下内容:

● Scrapy框架结构及工作原理。

● Request对象和Response对象。2.1Scrapy框架结构及工作原理

图2-1展示了Scrapy框架的组成结构,并从数据流的角度揭示Scrapy的工作原理。图2-1

首先,简单了解一下Scrapy框架中的各个组件,如表2-1所示。表2-1

对于用户来说,Spider是最核心的组件,Scrapy爬虫开发是围绕实现Spider展开的。

接下来,看一下在框架中的数据流,有表2-2所示的3种对象。表2-2

Request和Response是HTTP协议中的术语,即HTTP请求和HTTP响应,Scrapy框架中定义了相应的Request和Response类,这里的Item代表Spider从页面中爬取的一项数据。

最后,我们来说明以上几种对象在框架中的流动过程。

● 当SPIDER要爬取某URL地址的页面时,需使用该URL构造一个Request对象,提交给ENGINE(图2-1中的1)。

● Request对象随后进入SCHEDULER按某种算法进行排队,之后的某个时刻SCHEDULER将其出队,送往DOWNLOADER(图2-1中的2、3、4)。

● DOWNLOADER根据Request对象中的URL地址发送一次HTTP请求到网站服务器,之后用服务器返回的HTTP响应构造出一个Response对象,其中包含页面的HTML文本(图2-1中的5)。

● Response对象最终会被递送给SPIDER的页面解析函数(构造Request对象时指定)进行处理,页面解析函数从页面中提取数据,封装成Item后提交给ENGINE,Item之后被送往ITEM PIPELINES进行处理,最终可能由EXPORTER(图2-1中没有显示)以某种数据格式写入文件(csv,json);另一方面,页面解析函数还从页面中提取链接(URL),构造出新的Request对象提交给ENGINE(图2-1中的6、7、8)。

理解了框架中的数据流,也就理解了Scrapy爬虫的工作原理。如果把框架中的组件比作人体的各个器官,Request和Response对象便是血液,Item则是代谢产物。2.2Request和Response对象

通过上述讲解,大家已经了解了Request和Response对象在Scrapy框架中的重要性,下面详细介绍这两个对象。2.2.1 Request对象

Request对象用来描述一个HTTP请求,下面是其构造器方法的参数列表: Request(url[, callback, method='GET', headers, body, cookies, meta, encoding='utf-8', priority=0, dont_filter=False, errback])

下面依次介绍这些参数。

● url(必选)

请求页面的url地址,bytes或str类型,如'http://www.python.org/doc'。

● callback

页面解析函数, Callable类型,Request对象请求的页面下载完成后,由该参数指定的页面解析函数被调用。如果未传递该参数,默认调用Spider的parse方法。

● method

HTTP请求的方法,默认为'GET'。

● headers

HTTP请求的头部字典,dict类型,例如{'Accept': 'text/html', 'User-Agent':Mozilla/5.0'}。如果其中某项的值为None,就表示不发送该项HTTP头部,例如{'Cookie': None},禁止发送Cookie。

● body

HTTP请求的正文,bytes或str类型。

● cookies

Cookie信息字典,dict类型,例如{'currency': 'USD', 'country': 'UY'}。

● meta

Request的元数据字典,dict类型,用于给框架中其他组件传递信息,比如中间件Item Pipeline。其他组件可以使用Request对象的meta属性访问该元数据字典(request.meta),也用于给响应处理函数传递信息,详见Response的meta属性。

● encoding

url和body参数的编码默认为'utf-8'。如果传入的url或body参数是str类型,就使用该参数进行编码。

● priority

请求的优先级默认值为0,优先级高的请求优先下载。

● dont_filter

默认情况下(dont_filter=False),对同一个url地址多次提交下载请求,后面的请求会被去重过滤器过滤(避免重复下载)。如果将该参数置为True,可以使请求避免被过滤,强制下载。例如,在多次爬取一个内容随时间而变化的页面时(每次使用相同的url),可以将该参数置为True。

● errback

请求出现异常或者出现HTTP错误时(如404页面不存在)的回调函数。

虽然参数很多,但除了url参数外,其他都带有默认值。在构造Request对象时,通常我们只需传递一个url参数或再加一个callback参数,其他使用默认值即可,代码如下: >>> import scrapy >>> request = scrapy.Request('http://books.toscrape.com/') >>> request2 = scrapy.Request('http://quotes.toscrape.com/', callback=self.parseItem)

在实际应用中,我们几乎只调用Request的构造器创建对象,但也可以根据需求访问Request对象的属性,常用的有以下几个:

● url

● method

● headers

● body

● meta

这些属性和构造器参数相对应,这里不再重复解释。2.2.2 Response对象

Response对象用来描述一个HTTP响应,Response只是一个基类,根据响应内容的不同有如下子类:

● TextResponse

● HtmlResponse

● XmlResponse

当一个页面下载完成时,下载器依据HTTP响应头部中的Content-Type信息创建某个Response的子类对象。我们通常爬取的网页,其内容是HTML文本,创建的便是HtmlResponse对象,其中HtmlResponse和XmlResponse是TextResponse的子类。实际上,这3个子类只有细微的差别,这里以HtmlResponse为例进行讲解。

下面介绍HtmlResponse对象的属性及方法。

● url

HTTP响应的url地址,str类型。

● status

HTTP响应的状态码,int类型,例如200,404。

● headers

HTTP响应的头头部,类字典类型,可以调用get或getlist方法对其进行访问,例如: response.headers.get('Content-Type') response.headers.getlist('Set-Cookie')

● body

HTTP响应正文,bytes类型。

● text

文本形式的HTTP响应正文,str类型,它是由response.body使用response.encoding解码得到的,即 reponse.text = response.body.decode(response.encoding)

● encoding

HTTP响应正文的编码,它的值可能是从HTTP响应头部或正文中解析出来的。

● request

产生该HTTP响应的Request对象。

● meta

即response.request.meta,在构造Request对象时,可将要传递给响应处理函数的信息通过meta参数传入;响应处理函数处理响应时,通过response.meta将信息取出。

● selector

Selector对象用于在Response中提取数据(选择器相关话题在后面章节详细讲解)。

● xpath(query)

使用XPath选择器在Response中提取数据,实际上它是response.selector.xpath方法的快捷方式(选择器相关话题在后面章节详细讲解)。

● css(query)

使用CSS选择器在Response中提取数据,实际上它是response.selector.css方法的快捷方式(选择器相关话题在后面章节详细讲解)。

● urljoin(url)

用于构造绝对url。当传入的url参数是一个相对地址时,根据response.url计算出相应的绝对url。例如,response.url为http://www.example.com/a,url为b/index.html,调用response.urljoin(url)的结果为http://www.example.com/a/b/index.html。

虽然HtmlResponse对象有很多属性,但最常用的是以下的3个方法:

● xpath(query)

● css(query)

● urljoin(url)

前两个方法用于提取数据,后一个方法用于构造绝对url。2.3Spider开发流程

有了前面知识的铺垫,现在回到本章的主题“编写Spider”。实现一个Spider子类的过程很像是完成一系列填空题,Scrapy框架提出以下问题让用户在Spider子类中作答:

● 爬虫从哪个或哪些页面开始爬取?

● 对于一个已下载的页面,提取其中的哪些数据?

● 爬取完当前页面后,接下来爬取哪个或哪些页面?

上面问题的答案包含了一个爬虫最重要的逻辑,回答了这些问题,一个爬虫也就开发出来了。

接下来,我们以上一章exmaple项目中的BooksSpider为例,讲解一个Spider的开发流程。为方便阅读,再次给出BooksSpider的代码: # -*- coding: utf-8 -*- import scrapy class BooksSpider(scrapy.Spider): # 每一个爬虫的唯一标识 name = "books" # 定义爬虫爬取的起始点,起始点可以是多个,我们这里是一个 start_urls = ['http://books.toscrape.com/'] def parse(self, response): # 提取数据 # 每一本书的信息是在

中,我们使用 # css()方法找到所有这样的article 元素,并依次迭代 for book in response.css('article.product_pod'): # 书名信息在article > h3 > a 元素的title属性里 # 例如: A Light in the ... name = book.xpath('./h3/a/@title').extract_first() # 书价信息在

的TEXT中。 # 例如:

£51.77

price = book.css('p.price_color::text').extract_first() yield { 'name': name, 'price': price, } # 提取链接 # 下一页的url 在ul.pager > li.next > a 里面 # 例如: next_url = response.css('ul.pager li.next a::attr(href)').extract_first() if next_url: # 如果找到下一页的url,得到绝对路径,构造新的Request 对象 next_url = response.urljoin(next_url) yield scrapy.Request(next_url, callback=self.parse)

实现一个Spider只需要完成下面4个步骤:

步骤 01 继承scrapy.Spider。

步骤 02 为Spider取名。

步骤 03 设定起始爬取点。

步骤 04 实现页面解析函数。2.3.1 继承scrapy.Spider

Scrapy框架提供了一个Spider基类,我们编写的Spider需要继承它: import scrapy class BooksSpider(scrapy.Spider): ...

这个Spider基类实现了以下内容:

● 供Scrapy引擎调用的接口,例如用来创建Spider实例的类方法from_crawler。

● 供用户使用的实用工具函数,例如可以调用log方法将调试信息输出到日志。

● 供用户访问的属性,例如可以通过settings属性访问配置文件中的配置。

实际上,在初学Scrapy时,不必关心Spider基类的这些细节,未来有需求时再去查阅文档即可。2.3.2 为Spider命名

在一个Scrapy项目中可以实现多个Spider,每个Spider需要有一个能够区分彼此的唯一标识,Spider的类属性name便是这个唯一标识。 class BooksSpider(scrapy.Spider): name = "books" ...

执行scrapy crawl命令时就用到了这个标识,告诉Scrapy使用哪个Spider进行爬取。2.3.3 设定起始爬取点

Spider必然要从某个或某些页面开始爬取,我们称这些页面为起始爬取点,可以通过类属性start_urls来设定起始爬取点: class BooksSpider(scrapy.Spider): ... start_urls = ['http://books.toscrape.com/'] ...

start_urls通常被实现成一个列表,其中放入所有起始爬取点的url(例子中只有一个起始点)。看到这里,大家可能会想,请求页面下载不是一定要提交Request对象么?而我们仅定义了url列表,是谁暗中构造并提交了相应的Request对象呢?通过阅读Spider基类的源码可以找到答案,相关代码如下: class Spider(object_ref): ... def start_requests(self): for url in self.start_urls: yield self.make_requests_from_url(url) def make_requests_from_url(self, url): return Request(url, dont_filter=True) def parse(self, response): raise NotImplementedError ...

从代码中可以看出,Spider基类的start_requests方法帮助我们构造并提交了Request对象,对其中的原理做如下解释:

● 实际上,对于起始爬取点的下载请求是由Scrapy引擎调用Spider对象的start_requests方法提交的,由于BooksSpider类没有实现start_requests方法,因此引擎会调用Spider基类的start_requests方法。

● 在start_requests方法中,self.start_urls便是我们定义的起始爬取点列表(通过实例访问类属性),对其进行迭代,用迭代出的每个url作为参数调用make_requests_from_url方法。

● 在make_requests_from_url方法中,我们找到了真正构造Reqeust对象的代码,仅使用url和dont_filter参数构造Request对象。

● 由于构造Request对象时并没有传递callback参数来指定页面解析函数,因此默认将parse方法作为页面解析函数。此时BooksSpider必须实现parse方法,否则就会调用Spider基类的parse方法,从而抛出NotImplementedError异常(可以看作基类定义了一个抽象接口)。

● 起始爬取点可能有多个,start_requests方法需要返回一个可迭代对象(列表、生成器等),其中每一个元素是一个Request对象。这里,start_requests方法被实现成一个生成器函数(生成器对象是可迭代的),每次由yield语句返回一个Request对象。

由于起始爬取点的下载请求是由引擎调用Spider对象的start_requests方法产生的,因此我们也可以在BooksSpider中实现start_requests方法(覆盖基类Spider的start_requests方法),直接构造并提交起始爬取点的Request对象。在某些场景下使用这种方式更加灵活,例如有时想为Request添加特定的HTTP请求头部,或想为Request指定特定的页面解析函数。

以下是通过实现start_requests方法定义起始爬取点的示例代码(改写BooksSpider): class BooksSpider(scrapy.Spider): # start_urls = ['http://books.toscrape.com/'] # 实现start_requests 方法, 替代start_urls类属性 def start_requests(self): yield scrapy.Request('http://books.toscrape.com/', callback=self.parse_book, headers={'User-Agent': 'Mozilla/5.0'}, dont_filter=True) # 改用parse_book 作为回调函数 def parse_book(response): ...

到此,我们介绍完了为爬虫设定起始爬取点的两种方式:

● 定义start_urls属性。

● 实现start_requests方法。2.3.4 实现页面解析函数

页面解析函数也就是构造Request对象时通过callback参数指定的回调函数(或默认的parse方法)。页面解析函数是实现Spider中最核心的部分,它需要完成以下两项工作:

● 使用选择器提取页面中的数据,将数据封装后(Item或字典)提交给Scrapy引擎。

● 使用选择器或LinkExtractor提取页面中的链接,用其构造新的Request对象并提交给Scrapy引擎(下载链接页面)。

一个页面中可能包含多项数据以及多个链接,因此页面解析函数被要求返回一个可迭代对象(通常被实现成一个生成器函数),每次迭代返回一项数据(Item或字典)或一个Request对象。

关于如何提取数据、封装数据、提取链接等话题,我们在接下来的章节继续学习。2.4本章小结

本章先讲解了Scrapy的框架结构以及工作原理,然后又介绍了Scrapy中与页面下载相关的两个核心对象Request和Response,有了这些知识的铺垫,最后我们讲解了实现一个Spider的开发流程。关于编写Spider时涉及的一些技术细节在后面章节继续讨论。第3章使用Selector提取数据在第2章中,我们讲解了Spider开发的大体流程,让大家对Scrapy爬虫开发有了更加清晰的理解,接下来继续讲解编写爬虫时使用的一些具体技术,从页面中提取数据是Spider最重要的工作之一,这一章我们来学习相关内容。3.1Selector对象

从页面中提取数据的核心技术是HTTP文本解析,在Python中常用以下模块处理此类问题:

● BeautifulSoup

BeautifulSoup是非常流行的HTTP解析库,API简洁易用,但解析速度较慢。

● lxml

lxml是一套由C语言编写的xml解析库(libxml2),解析速度更快,API相对复杂。

Scrapy综合上述两者优点实现了Selector类,它是基于lxml库构建的,并简化了API接口。在Scrapy中使用Selector对象提取页面中的数据,使用时先通过XPath或CSS选择器选中页面中要提取的数据,然后进行提取。

下面详细介绍Selector对象的使用。3.1.1 创建对象

Selector类的实现位于scrapy.selector模块,创建Selector对象时,可将页面的HTML文档字符串传递给Selector构造器方法的text参数: >>> from scrapy.selector import Selector >>> text = ''' ... ... ...

Hello World

...

Hello Scrapy

... Hello python ...
    ...
  • C++
  • ...
  • Java
  • ...
  • Python
  • ...
... ... ... ''' ... >>> selector = Selector(text=text)

也可以使用一个Response对象构造Selector对象,将其传递给Selector构造器方法的response参数: >>> from scrapy.selector import Selector >>> from scrapy.http import HtmlResponse >>> body = ''' ... ... ...

Hello World

...

Hello Scrapy

... Hello python ...
    ...
  • C++
  • ...
  • Java
  • ...
  • Python
  • ...
... ... ... ''' ... >>> response = HtmlResponse(url='http://www.example.com', body=body, encoding='utf8') >>> selector = Selector(response=response) >>> selector 3.1.2 选中数据

调用Selector对象的xpath方法或css方法(传入XPath或CSS选择器表达式),可以选中文档中的某个或某些部分: >>> selector_list = selector.xpath('//h1') # 选中文档中所有的

>>> selector_list # 其中包含两个

对应的Selector 对象 [, ]

xpath和css方法返回一个SelectorList对象,其中包含每个被选中部分对应的Selector对象,SelectorList支持列表接口,可使用for语句

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载