帮酷LOGO
0 0 评论
文章标签:Scrapy  WEB  crawl  python-3  

介绍

Web抓取,通常称为Web爬网或Web爬虫,或者"以编程方式浏览网页集合并提取数据,"是用于处理Web上数据的强大工具。

使用一个web scraper,你可以挖掘一组产品,获取大量的文本或数据,从网站获取数据。

在本教程结束时,你将拥有一个功能齐全的python web scraper,它可以遍历Brickset上的一系列页面,并从每个页面提取有关乐高玩具的数据,将数据显示到屏幕上。

web scraper可以很容易地扩展,这样你就可以修改它,并使用它作为你自己项目的基础。

前提条件

要完成本教程,你需要一个python-3本地开发环境,你可以按照如何安装和设置Python 3的本地编程环境配置。

步骤1创建基本web scraper

Scraper有两个步骤:

你可以使用编程语言提供的模块或库来构建web scraper,但是你必须处理一些潜在的头痛问题,例如,你需要处理并发,以便一次抓取多个页面,你可能想知道如何将抓取的数据转换为不同格式,如CSV,XML或JSON ,有时你必须处理那些需要特定设置和访问模式的站点。

如果你在现有库上构建你的web scraper,那么你将可以更好地处理问题,在本教程中,使用python和Scrapy来构建我们的scraper 。

Scrapy是最流行和强大的python抓取库之一; 它处理了大量的通用的功能,所以开发人员不需要重新创建轮子。

Scrapy和大多数python包一样,位于PyPI (也称为pip )上,PyPI是python包索引,是所有已发布的python软件库。


pip install scrapy

如果在安装中遇到问题,或者想在不使用pip的情况下安装Scrapy,请查看正式安装文档

安装Scrapy,为我们的项目创建一个新文件夹,通过运行以下命令在终端中执行这个操作:


mkdir brickset-scraper

现在,导航到刚创建的新目录:


cd brickset-scraper

然后为我们的scraper scraper.py创建一个新的python文件,你可以使用touch命令在终端中创建此文件,如下所示:


touch scraper.py

或者你可以使用文本编辑器或图形文件管理器创建文件。

我们首先制作一个使用Scrapy为基础非常基本的scraper ,为此,我们创建一个python类,它是一个子类scrapy.Spider,一个基本的,此类有两个必需属性:

name--蜘蛛的名字,start_urls -你开始抓取的URL的列表 ,我们从一个网址开始,

在文本编辑器中打开scrapy.py文件,并且添加此代码以创建基本的spider :

scraper.py

import scrapy


class BrickSetSpider(scrapy.Spider):
 name ="brickset_spider"
 start_urls = ['http://brickset.com/sets/year-2016']

让我们按下面的方式来分解:

首先,importscrapy,以便我们可以使用包提供的类。

接下来,我们接受Scrapy提供的Spider类,并将一个子类make BrickSetSpider ,把子类看作它的父类的一种更特殊的形式,,子类具有定义如何跟踪URL和从查找的页中提取数据的方法和行为,

然后我们给蜘蛛命名为brickset_spider

最后,我们给抓取器一个单独的URL: http://brickset.com/sets/year-2016,如果在浏览器中打开该URL,它将带你到一个搜索结果页面,显示包含LEGO的第一页。

现在我们来测试一下web scraper,你通常运行python文件来运行文件,如python path/to/file.py ,然而,scrapy提供了它自己的命令行接口来简化启动web scraper的进程,使用以下命令启动web scraper:


scrapy runspider scraper.py

你将看到如下所示的内容:


Output2016-09-22 23:37:45 [scrapy] INFO: Scrapy 1.1.2 started (bot: scrapybot)
2016-09-22 23:37:45 [scrapy] INFO: Overridden settings: {}
2016-09-22 23:37:45 [scrapy] INFO: Enabled extensions:
['scrapy.extensions.logstats.LogStats',
 'scrapy.extensions.telnet.TelnetConsole',
 'scrapy.extensions.corestats.CoreStats']
2016-09-22 23:37:45 [scrapy] INFO: Enabled downloader middlewares:
['scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware',
 ...
 'scrapy.downloadermiddlewares.stats.DownloaderStats']
2016-09-22 23:37:45 [scrapy] INFO: Enabled spider middlewares:
['scrapy.spidermiddlewares.httperror.HttpErrorMiddleware',
 ...
 'scrapy.spidermiddlewares.depth.DepthMiddleware']
2016-09-22 23:37:45 [scrapy] INFO: Enabled item pipelines:
[]
2016-09-22 23:37:45 [scrapy] INFO: Spider opened
2016-09-22 23:37:45 [scrapy] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2016-09-22 23:37:45 [scrapy] DEBUG: Telnet console listening on 127.0.0.1:6023
2016-09-22 23:37:47 [scrapy] DEBUG: Crawled (200) <GET http://brickset.com/sets/year-2016> (referer: None)
2016-09-22 23:37:47 [scrapy] INFO: Closing spider (finished)
2016-09-22 23:37:47 [scrapy] INFO: Dumping Scrapy stats:
{'downloader/request_bytes': 224,
 'downloader/request_count': 1,
 ...
 'scheduler/enqueued/memory': 1,
 'start_time': datetime.datetime(2016, 9, 23, 6, 37, 45, 995167)}
2016-09-22 23:37:47 [scrapy] INFO: Spider closed (finished)

步骤2从页面提取数据

在写一个web scraper时,看一下HTML文件的源码,并且熟悉结构是一个好主意,在这里为了可读性,我删除了一些东西:


brickset.com/sets/year-2016<body>
 <section class="setlist">
 <article class='set'>
 <a href="https://images.brickset.com/sets/large/10251-1.jpg?201510121127" 
 class="highslide plain mainimg" onclick="return hs.expand(this)"><img 
 src="https://images.brickset.com/sets/small/10251-1.jpg?201510121127" title="10251-1: 
 Brick Bank" onError="this.src='/assets/images/spacer.png'" /></a>
 <div class="highslide-caption">
 <h1>Brick Bank</h1><div class='tags floatleft'><a href='/sets/10251-1/Brick- 
 Bank'>10251-1</a> <a href='/sets/theme-Creator-Expert'>Creator Expert</a> <a 
 class='subtheme' href='/sets/theme-Creator-Expert/subtheme-Modular- 
 Buildings'>Modular Buildings</a> <a class='year' href='/sets/theme-Creator- 
 Expert/year-2016'>2016</a> </div><div class='floatright'>&copy;2016 LEGO 
 Group</div>
 <div class="pn">
 <a href="#" onclick="return hs.previous(this)" title="Previous (left arrow 
 key)">&#171; Previous</a>
 <a href="#" onclick="return hs.next(this)" title="Next (right arrow key)">Next 
 &#187;</a>
 </div>
 </div>

...

 </article>
 </section>
</body>

抓取此页是两个步骤:

scrapy基于你提供的选择器获取数据,选择器是我们可以用来在页面上查找一个或多个元素的模式,这样我们就可以使用元素中的数据,scrapy支持CSS选择器或XPATH选择器。

现在使用CSS选择器,因为CSS是更容易的选择,并且适合查找页面上的所有集,如果查看页面的HTML,你将看到每个集合都是用类set指定的,我们只需要将该选择器传递到response对象,就像这样:

scraper.py

class BrickSetSpider(scrapy.Spider):
 name ="brickset_spider"
 start_urls = ['http://brickset.com/sets/year-2016']

 def parse(self, response):
 SET_SELECTOR = '.set'
 for brickset in response.css(SET_SELECTOR):
 pass

此代码获取页面上的所有集合,并对它进行循环以提取数据,现在从这些集合中提取数据,以便我们可以显示它。


brickset.com/sets/year-2016<h1>Brick Bank</h1><div class='tags floatleft'><a href='/sets/10251-1/Brick-Bank'>10251-1</a>

按照以下方式修改代码以查找集的名称,并且显示它:

scraper.py

class BrickSetSpider(scrapy.Spider):
 name ="brickset_spider"
 start_urls = ['http://brickset.com/sets/year-2016']

 def parse(self, response):
 SET_SELECTOR = '.set'
 for brickset in response.css(SET_SELECTOR):

 NAME_SELECTOR = 'h1 ::text'
 yield {
 'name': brickset.css(NAME_SELECTOR).extract_first(),
 }

注:extract_first()后的末尾逗号不是错误,我们很快向这个部分添加更多的内容,所以,我们在这里留下逗号,以便稍后添加。

你将注意到代码中出现了两种情况:

我们::text附加到选择器以获取名称,那是一个CSS伪选择器,它获取a标记内部的文本而不是标记本身,因为我们只想要与选择器匹配的第一个元素,所以,我们在brickset.css(NAME_SELECTOR)返回的对象上调用extract_first()

保存文件并再次运行web scraper:


scrapy runspider scraper.py

此时,你将看到这些集的名称出现在输出中:


Output...
[scrapy] DEBUG: Scraped from <200 http://brickset.com/sets/year-2016>
{'name': 'Brick Bank'}
[scrapy] DEBUG: Scraped from <200 http://brickset.com/sets/year-2016>
{'name': 'Volkswagen Beetle'}
[scrapy] DEBUG: Scraped from <200 http://brickset.com/sets/year-2016>
{'name': 'Big Ben'}
[scrapy] DEBUG: Scraped from <200 http://brickset.com/sets/year-2016>
{'name': 'Winter Holiday Train'}
...


再次查看特定集合的HTML :


brickset.com/sets/year-2016<article class="set">
 <a class="highslide plain mainimg" href="http://images.brickset.com/sets/images/10251-1.jpg?201510121127" onclick="return hs.expand(this)">
 <img src="http://images.brickset.com/sets/small/10251-1.jpg?201510121127" title="10251-1: Brick Bank"></a>
 ...
 <div class="meta">
 <h1><a href="/sets/10251-1/Brick-Bank"><span>10251:</span> Brick Bank</a> </h1>
 ...
 <div class="col">
 <dl>
 <dt>Pieces</dt>
 <dd><a class="plain" href="/inventories/10251-1">2380</a></dd>
 <dt>Minifigs</dt>
 <dd><a class="plain" href="/minifigs/inset-10251-1">5</a></dd>
 ...
 </dl>
 </div>
 ...
 </div>
</article>

通过检查以下代码,我们可以看到一些事情:

图像存储在集合开始处的a标记的img标记的src属性中,我们可以使用另一个CSS选择器来获取这个值,就像捕获每个集的名称一样,getting的数量有点小,有一个dt标记,它包含文本Pieces,然后一个dd标记,它包含实际的块数,使用XPATH,用于遍历XML的查询语言,用于抓取这个语言,因为它太复杂了,有一个dt标记,其中包含文本Minifigs,后跟数字后面的dd标记,

因此,修改scraper以获取以下新信息:

scraper.py

class BrickSetSpider(scrapy.Spider):
 name = 'brick_spider'
 start_urls = ['http://brickset.com/sets/year-2016']

 def parse(self, response):
 SET_SELECTOR = '.set'
 for brickset in response.css(SET_SELECTOR):

 NAME_SELECTOR = 'h1 ::text'
 PIECES_SELECTOR = './/dl[dt/text() ="Pieces"]/dd/a/text()'
 MINIFIGS_SELECTOR = './/dl[dt/text() ="Minifigs"]/dd[2]/a/text()'
 IMAGE_SELECTOR = 'img ::attr(src)'
 yield {
 'name': brickset.css(NAME_SELECTOR).extract_first(),
 'pieces': brickset.xpath(PIECES_SELECTOR).extract_first(),
 'minifigs': brickset.xpath(MINIFIGS_SELECTOR).extract_first(),
 'image': brickset.css(IMAGE_SELECTOR).extract_first(),
 }

保存你的更改并再次运行web scraper:


scrapy runspider scraper.py

现在,你将看到程序输出中的新数据:


Output2016-09-22 23:52:37 [scrapy] DEBUG: Scraped from <200 http://brickset.com/sets/year-2016>
{'minifigs': '5', 'pieces': '2380', 'name': 'Brick Bank', 'image': 'http://images.brickset.com/sets/small/10251-1.jpg?201510121127'}
2016-09-22 23:52:37 [scrapy] DEBUG: Scraped from <200 http://brickset.com/sets/year-2016>
{'minifigs': None, 'pieces': '1167', 'name': 'Volkswagen Beetle', 'image': 'http://images.brickset.com/sets/small/10252-1.jpg?201606140214'}
2016-09-22 23:52:37 [scrapy] DEBUG: Scraped from <200 http://brickset.com/sets/year-2016>
{'minifigs': None, 'pieces': '4163', 'name': 'Big Ben', 'image': 'http://images.brickset.com/sets/small/10253-1.jpg?201605190256'}
2016-09-22 23:52:37 [scrapy] DEBUG: Scraped from <200 http://brickset.com/sets/year-2016>
{'minifigs': None, 'pieces': None, 'name': 'Winter Holiday Train', 'image': 'http://images.brickset.com/sets/small/10254-1.jpg?201608110306'}
2016-09-22 23:52:37 [scrapy] DEBUG: Scraped from <200 http://brickset.com/sets/year-2016>
{'minifigs': None, 'pieces': None, 'name': 'XL Creative Brick Box', 'image': '/assets/images/misc/blankbox.gif'}
2016-09-22 23:52:37 [scrapy] DEBUG: Scraped from <200 http://brickset.com/sets/year-2016>
{'minifigs': None, 'pieces': '583', 'name': 'Creative Building Set', 'image': 'http://images.brickset.com/sets/small/10702-1.jpg?201511230710'}

步骤3-爬多个页面

蜘蛛的整个点是检测和遍历到其他页面的链接,并从这些页面获取数据。

下面是HTML的例子:


brickset.com/sets/year-2016<ul class="pagelength">

 ...

 <li class="next">
 <a href="http://brickset.com/sets/year-2017/page-2">&#8250;</a>
 </li>
 <li class="last">
 <a href="http://brickset.com/sets/year-2016/page-32">&#187;</a>
 </li>
</ul>

你可以看到,有一个li标记,其中有next类,在该标记中有一个a标记,链接到下一页,我们只需要告诉web scraper,如果它存在的话,请跟随该链接。

按以下的方式修改代码:

scraper.py

class BrickSetSpider(scrapy.Spider):
 name = 'brick_spider'
 start_urls = ['http://brickset.com/sets/year-2016']

 def parse(self, response):
 SET_SELECTOR = '.set'
 for brickset in response.css(SET_SELECTOR):

 NAME_SELECTOR = 'h1 ::text'
 PIECES_SELECTOR = './/dl[dt/text() ="Pieces"]/dd/a/text()'
 MINIFIGS_SELECTOR = './/dl[dt/text() ="Minifigs"]/dd[2]/a/text()'
 IMAGE_SELECTOR = 'img ::attr(src)'
 yield {
 'name': brickset.css(NAME_SELECTOR).extract_first(),
 'pieces': brickset.xpath(PIECES_SELECTOR).extract_first(),
 'minifigs': brickset.xpath(MINIFIGS_SELECTOR).extract_first(),
 'image': brickset.css(IMAGE_SELECTOR).extract_first(),
 }

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

scraper.py

import scrapy


class BrickSetSpider(scrapy.Spider):
 name = 'brick_spider'
 start_urls = ['http://brickset.com/sets/year-2016']

 def parse(self, response):
 SET_SELECTOR = '.set'
 for brickset in response.css(SET_SELECTOR):

 NAME_SELECTOR = 'h1 ::text'
 PIECES_SELECTOR = './/dl[dt/text() ="Pieces"]/dd/a/text()'
 MINIFIGS_SELECTOR = './/dl[dt/text() ="Minifigs"]/dd[2]/a/text()'
 IMAGE_SELECTOR = 'img ::attr(src)'
 yield {
 'name': brickset.css(NAME_SELECTOR).extract_first(),
 'pieces': brickset.xpath(PIECES_SELECTOR).extract_first(),
 'minifigs': brickset.xpath(MINIFIGS_SELECTOR).extract_first(),
 'image': brickset.css(IMAGE_SELECTOR).extract_first(),
 }

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



文章标签:WEB  crawl  python-3  Scrapy  

Copyright © 2011 HelpLib All rights reserved.    知识分享协议 京ICP备05059198号-3  |  如果智培  |  酷兔英语