Scrapy

来源:互联网 发布:potplayer for mac 编辑:程序博客网 时间:2024/05/16 14:48

创建项目

开始爬取数据之前,你必须先创建一个Scrapy项目。进入你想将项目代码保存的目录,并执行以下代码

MacBook-Pro:scrapy $ scrapy startproject projectname

这会创建一个以projectname命名的文件夹,文件夹中包括一个部署配置文件scrapy.cfg和一个项目的python模块(以projectname命名的另一个文件夹,文件夹下包含一个__init__.py文件)。python模块下包含4个.py文件和一个spiders文件夹,4个.py文件分别是项目的items定义文件items.py,pipelines文件pipelines.py,项目配置文件settings.py和项目中间件文件middlewares.py,spiders文件夹就是将来存在项目所有spiders的地方。Scrapy项目主要的精力就是放在spider的创建上,即我们主要的工作都是在spiders目录下完成的。

接下来让我们看看如何去写第一个spider

一个简单的spider

spider就是我们定义的一个scrapy.Spider的子类。在爬取数据时,scrapy会利用spider去从网页上爬取数据,所以一个最简单的spider必须包含两个部分:1. 最初的请求;2. 如何从请求返回的网页数据中解析需要的数据。另一个可选的部分是如何追踪网页上的链接,即是否要将页面的链接指向的页面也同时爬取或指定部门链接去爬取,可以说这一部分就是实现我们的spider可以爬取整个网站的所有数据而不必把所有链接地址加到第一部分"最初的请求"中的原因。

以下是一个spider的代码


运行spider

在项目的根目录执行以下命令

(scrapyEnv) MacBook-Pro:tutorial $ scrapy crawl quotes

这个命令会运行名字为quotes的spider。

简化start_requests方法

spider第一部分初始的请求start_requests方法也可以使用一个spider的属性start_urls来替代,它是一个包含初始请求地址的list。scrapy会在运行spider时用默认的start_requests方法和list中的地址来创建初始的请求。

以下是代码:


scrapy shell中使用selector获取数据

使用命令scrapy shell [url]启动scrapy shell,url可以指定也可以不指定

(scrapyEnv) MacBook-Pro:tutorial $ scrapy shell "http://quotes.toscrape.com/page/1/"/Users/rossi/.virtualenvs/scrapyEnv/lib/python3.6/site-packages/scrapy/spiderloader.py:37: UserWarning: There are several spiders with the same name:  QuotesSpider named 'quotes' (in tutorial.spiders.quotes_spider)  QuotesSpider named 'quotes' (in tutorial.spiders.quotes_spider1)  QuotesSpider named 'quotes' (in tutorial.spiders.temp)  QuotesSpider named 'quotes' (in tutorial.spiders.temp1)  This can cause unexpected behavior.  warnings.warn(msg, UserWarning)2017-10-15 21:58:11 [scrapy.utils.log] INFO: Scrapy 1.4.0 started (bot: tutorial)2017-10-15 21:58:11 [scrapy.utils.log] INFO: Overridden settings: {'BOT_NAME': 'tutorial', 'DUPEFILTER_CLASS': 'scrapy.dupefilters.BaseDupeFilter', 'LOGSTATS_INTERVAL': 0, 'NEWSPIDER_MODULE': 'tutorial.spiders', 'ROBOTSTXT_OBEY': True, 'SPIDER_MODULES': ['tutorial.spiders']}2017-10-15 21:58:11 [scrapy.middleware] INFO: Enabled extensions:['scrapy.extensions.corestats.CoreStats', 'scrapy.extensions.telnet.TelnetConsole', 'scrapy.extensions.memusage.MemoryUsage']2017-10-15 21:58:11 [scrapy.middleware] 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.httpproxy.HttpProxyMiddleware', 'scrapy.downloadermiddlewares.stats.DownloaderStats']2017-10-15 21:58:11 [scrapy.middleware] INFO: Enabled spider middlewares:['scrapy.spidermiddlewares.httperror.HttpErrorMiddleware', 'scrapy.spidermiddlewares.offsite.OffsiteMiddleware', 'scrapy.spidermiddlewares.referer.RefererMiddleware', 'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware', 'scrapy.spidermiddlewares.depth.DepthMiddleware']2017-10-15 21:58:11 [scrapy.middleware] INFO: Enabled item pipelines:[]2017-10-15 21:58:11 [scrapy.extensions.telnet] DEBUG: Telnet console listening on 127.0.0.1:60232017-10-15 21:58:11 [scrapy.core.engine] INFO: Spider opened2017-10-15 21:58:14 [scrapy.core.engine] DEBUG: Crawled (404) <GET http://quotes.toscrape.com/robots.txt> (referer: None)2017-10-15 21:58:15 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://quotes.toscrape.com/page/1/> (referer: None)[s] Available Scrapy objects:[s]   scrapy     scrapy module (contains scrapy.Request, scrapy.Selector, etc)[s]   crawler    <scrapy.crawler.Crawler object at 0x107ed1358>[s]   item       {}[s]   request    <GET http://quotes.toscrape.com/page/1/>[s]   response   <200 http://quotes.toscrape.com/page/1/>[s]   settings   <scrapy.settings.Settings object at 0x108cd5ac8>[s]   spider     <DefaultSpider 'default' at 0x108fc68d0>[s] Useful shortcuts:[s]   fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed)[s]   fetch(req)                  Fetch a scrapy.Request and update local objects[s]   shelp()           Shell help (print this help)[s]   view(response)    View response in a browser>>>

shell启动好后,可以用CSS选择器在response对象中选择元素

>>> response.css('title')[<Selector xpath='descendant-or-self::title' data='<title>Quotes to Scrape</title>'>]>>>

可以看到其返回的是一个列表,其中包含的是Selector对象,我们把这个列表称为SelectorList。可以通过Selector对象访问到其HTML元素。

选择title的text

>>> response.css('title::text')[<Selector xpath='descendant-or-self::title/text()' data='Quotes to Scrape'>]>>>

将title的text内容读取出来

>>> response.css('title::text').extract()['Quotes to Scrape']>>>

这里使用了一个extract()方法,它的作用就是将Selector的data内容读取出来。注意这里返回的是一个list,因为选择元素或元素的属性或text时返回的也是一个list,如果你想取出第一个结果时,可以使用extract_first()方法:

>>> response.css('title::text').extract_first()'Quotes to Scrape'>>>

返回的结果就是list中的第一个结果,即text的内容。

当然对列表,我们也可以使用python的列表方法取第一个:

>>> response.css('title::text')[0].extract()'Quotes to Scrape'>>> response.css('title::text').extract()[0]'Quotes to Scrape'>>>

推荐的做法是使用extract_first()方法获取,因为它捕获了IndexError,如果没有发现匹配项,则其会返回None而不会抛出异常。

还有一个更强大的re()方法可以使用正则表达式获取数据:

>>> response.css('title::text').re(r'Quotes.*')['Quotes to Scrape']>>> response.css('title::text').re(r'Q\w+')['Quotes']>>> response.css('title::text').re(r'(\w+) to (\w+)')['Quotes', 'Scrape']>>>

为了找到适合的CSS选择器,我们可以从shell中用默认浏览器打开response的页面,命令是view(response)

>>> view(response)True>>>

除了CSS,还支持使用xpath表达式

>>> response.xpath('//title')[<Selector xpath='//title' data='<title>Quotes to Scrape</title>'>]>>> response.xpath('//title/text()')[<Selector xpath='//title/text()' data='Quotes to Scrape'>]>>> response.xpath('//title/text()').extract_first()'Quotes to Scrape'>>>

获取数据

了解了scrapy中Selector的用法之后,我们可以使用Selector在我们的spider中真正实现页面数据的获取,代码如下:


存储爬取数据

最简单的存储数据的方法是feed exports,可以使用以下命令:

(scrapyEnv) MacBook-Pro:tutorial $ scrapy crawl quotes -o quotes.json

就可以将爬取到的item数据以json的格式输出到文件quotes.json中。需要注意的是,如果是以json格式存储,scrapy会将爬取到的数据以json格式添加到指定文件中而不是覆盖文件。故如果连续两次执行这个命令,输出的文件则是一个损坏的json文件。

我们可以使用JSON Lines格式来将数据正常的添加到之前的文件中而不会出现JSON格式的那种问题。如下:

(scrapyEnv) MacBook-Pro:tutorial $ scrapy crawl quotes -o quotes.jl

追踪链接

想要追踪链接首先我们需要从网页中提取链接,这里需要再介绍scrapy支持CSS的扩展用法让你可以选取指定属性的内容。

将spider修改成支持递归的追踪链接并从该链接爬取数据的代码如下:


这里可以看到scrapy的追踪链接的机制:当你在一个回调方法里生成一个Request对象,scrapy会计划发送这个请求并关联一个回调方法当请求结束时调用。

创建请求的简单方法


这里使用了response.follow方法,它直接支持相对url,不需要再通过urljoin将相对url转为绝对。

也可以直接给response.follow传Selector而不是字符串,如:

 for href in response.css('li.next a::attr(href)'):     yield response.follow(href, callback=self.parse)

如果Selector是a元素,则response.follow还可以尽一步简写成:

for a in response.css('li.next a'):    yield response.follow(a, callback=self.parse)

另一个爬虫的例子


可以看到这个spider会从主页开始爬取作者页面和页码标识的页面,解析这些页面的回调方法分别是parse_author和parse。我们这里指定回调的时候使用的是positional arguments,它既可以适用于response.follow也可以适用于scrapy.Request。值得注意的是scrapy会自动筛选出我们已经获取过数据的页面链接,避免再次从那爬取重复的数据,

使用spider参数

当我们在运行spider时可以使用 -a选项将命令行的参数传给spider:

(scrapyEnv) MacBook-Pro:tutorial $ scrapy crawl quotes -o quotes-humor.json -a tag=humor

参数tag=humor会传给spider的__init__方法,成为spider的默认属性。

以下代码可以看出传为spider的参数会存在self.tag中,可以使用它让spider只获取指定tag的引用,用这个参数来构建URL