爬虫抓取网页数据(WebCrawler如何调度针对不同站点的Web请求带宽?)

优采云 发布时间: 2021-11-29 09:21

  爬虫抓取网页数据(WebCrawler如何调度针对不同站点的Web请求带宽?)

  网络爬虫(Spider)是一种在互联网上爬行的机器人。当然,它通常不是物理机器人,因为网络本身也是一个虚拟的东西,所以这个“机器人”其实就是一个程序,不是爬行,而是有一定的用途,爬行的时候会采集. 一些信息。例如,谷歌有很多爬虫采集网页内容和它们之间的链接信息;另一个例子是别有用心的爬虫在互联网上采集诸如foo [at] bar [dot] com之类的东西。

  此外,还有一些定制的爬虫,专门针对某个网站。比如前段时间JavaEye的罗宾写了几篇专门对付恶意爬虫的博客,还有小众软件或者LinuxToy。网站 经常被整个网站爬下来,然后挂在另一个名字下。其实,爬虫的基本原理非常简单。只要能上网,能分析网页,现在大多数语言都有方便的Http客户端库可以抓取网页,最简单的HTML分析可以直接使用正则规则。表达式来做,所以做一个最简单的网络爬虫其实是一件很简单的事情。但是,实现一个高质量的蜘蛛是非常困难的。

  爬虫的两部分是下载网页。需要考虑的问题有很多,比如如何最大限度地利用本地带宽,如何调度不同站点的web请求,以减少其他服务器的负担。在高性能的Web Crawler系统中,DNS查询也将成为亟待优化的瓶颈。此外,还有一些“配置文件”需要遵循(例如,robots.txt)。获取网页后的分析过程也很复杂。网上奇奇怪怪的东西很多,各种HTML页面出现各种错误。几乎不可能清楚地分析所有这些;

  另外,随着AJAX的普及,如何获取Javascript动态生成的内容也成为一个大问题。此外,互联网上还有各种有意或无意出现的蜘蛛陷阱。如果一味的关注超链接,你会被困在Trap中,比如这个网站,据说谷歌之前声称互联网上Unique URL的数量已经达到1万亿,所以这个人很自豪地宣布第二万亿。:D 然而,实际上并没有多少人需要像谷歌那样制作一个通用的爬虫。通常我们构建一个爬虫来抓取特定的一种或某种类型。需要爬取的网站结构做了一些分析,事情就变得简单多了。通过分析和选择有价值的链接进行跟踪,可以避免许多不必要的链接或蜘蛛陷阱。如果网站的结构允许选择合适的路径,我们就可以按照感兴趣的事物的一定顺序再次爬取它,这样就连URL重复的判断都可以省略了。比如我们要爬下pongba博客中的博客文字,通过观察,很容易发现我们对其中两个页面感兴趣:1.文章列表页面,如主页,或者URL是/page/\d+/这样的页面,可以通过Firebug文章看到每篇文章

  2.文章内容页,每个博客都有这样一个页面,例如/2008/09/11/machine-learning-and-ai-resources/,收录了文章的完整内容,这就是我们感兴趣的,所以我们从首页开始,利用wp-pagenavi中的链接,获取其他文章列表页面。特别是我们定义了一个路径:只跟随Next Page的链接,这样我们就可以从头到尾按顺序走一遍,免去了判断和重复爬行的麻烦。另外,文章列表页上链接到具体文章的链接对应的页面就是我们真正要保存的数据页。这样,它的实用脚本语言写一个ad hoc Crawler来完成这个任务并不难,不过今天的主角是Scrapy,它是一个用Python编写的Crawler Framework,简单轻便,也很方便,官网说已经在实际生产中使用过,所以不是玩具级别的东西。不过目前还没有Release版本,你可以直接使用他们的Mercurial仓库获取安装源码。不过这个东西也可以不安装直接使用,方便随时更新。文档很详细,不再赘述。Scrapy 使用异步网络库 Twisted 来处理网络通信。结构清晰,收录各种中间件接口,可以灵活满足各种需求。整体架构如下图所示:目前还没有 Release 版本,你可以直接使用他们的 Mercurial 存储库来获取安装源代码。不过这个东西也可以不安装直接使用,方便随时更新。文档很详细,不再赘述。Scrapy 使用异步网络库 Twisted 来处理网络通信。结构清晰,收录各种中间件接口,可以灵活满足各种需求。整体架构如下图所示:目前还没有 Release 版本,你可以直接使用他们的 Mercurial 存储库来获取安装源代码。不过这个东西也可以不安装直接使用,方便随时更新。文档很详细,不再赘述。Scrapy 使用异步网络库 Twisted 来处理网络通信。结构清晰,收录各种中间件接口,可以灵活满足各种需求。整体架构如下图所示:Scrapy 使用异步网络库 Twisted 来处理网络通信。结构清晰,收录各种中间件接口,可以灵活满足各种需求。整体架构如下图所示:Scrapy 使用异步网络库 Twisted 来处理网络通信。结构清晰,收录各种中间件接口,可以灵活满足各种需求。整体架构如下图所示:

  绿线是数据流。首先从初始URL开始,Scheduler将其交给Downloader下载,下载完成后交给Spider进行分析。Spider分析的结果有两种:一种是需要进一步爬取的链接。比如之前分析的“下一页”的链接,这些东西会被发回给Scheduler;另一个是需要保存的数据,发送到Item Pipeline,就是数据的后处理(详细分析、过滤、存储等)。此外,可以在数据流通道中安装各种中间件来进行必要的处理。它看起来很复杂,但使用起来非常简单。就像Rails一样,首先新建一个项目:

  scrapy-admin.py startproject blog_crawl

  会创建一个blog_crawl目录,里面有一个scrapy-ctl.py是整个项目的控制脚本,代码全部放在子目录blog_crawl下。为了能够爬取,我们在spiders目录下新建了一个mindhacks_spider.py,定义我们的Spider如下:

  from scrapy.spider import BaseSpider class MindhacksSpider(BaseSpider):domain_name = ""start_urls = [""]def parse(self, response):return [] SPIDER = MindhacksSpider()

  我们的MindhacksSpider继承自BaseSpider(通常是直接继承自scrapy.contrib.spiders.CrawlSpider,它更通用,更方便,但为了展示数据是如何解析的,这里使用了BaseSpider),变量domain_name和start_urls很容易理解是什么意思,parse方法就是我们需要定义的回调函数。默认请求在得到响应后会调用这个回调函数。我们这里需要解析页面,返回两个结果(需要进一步爬取链接,需要保存Data),让我觉得有点奇怪的是,它的接口定义中的两个结果实际上是在一个混合列表中返回的。目前尚不清楚为什么会出现这种设计。最后不是要把他们分开吗?总之,这里我们先写一个空函数,它只返回一个空列表。另外,定义一个“全局”变量 SPIDER,它会在 Scrapy 导入这个模块时被实例化,并且会被 Scrapy 引擎自动找到。所以你可以先运行爬虫试试:

  ./scrapy-ctl.py 抓取

  会有一堆输出,可以看到爬取了,因为这是初始的URL,但是因为我们在parse函数中没有返回需要进一步爬取的URL,所以整个爬取过程只爬到了首页就结束了. 下一步是分析页面。Scrapy 提供了一个非常方便的Shell(需要IPython),可以让我们做实验。使用以下命令启动 Shell:

  ./scrapy-ctl.py 外壳

  它会启动爬虫,抓取命令行指定的页面,然后进入shell。根据提示,我们有很多现成的变量可以使用。其中之一是 hxs,它是一个 HtmlXPathSelector。mindhacks 的 HTML 页面更加标准化。直接用XPath分析非常方便。通过Firebug可以看到每个博客文章的链接都在h1下,所以在Shell中使用这个XPath表达式测试:

  在 [1]: hxs.x('//h1/a/@href').extract()Out[1]:[u'#x27;, u'#x27;, u'#x27;, u' #x27;, u'#x27;, u'#x27;, u'#x27;, u'#x27;, u'#x27;, u'#x27;]

  这正是我们需要的 URL。另外可以找到“下一页”的链接,和其他几个页面的链接放在一个div里,但是“下一页”的链接没有title属性,所以写XPath

  //div[@class="wp-pagenavi"]/a[not(@title)]

  但是,如果你往回翻一页,你会发现“上一页”其实是一样的,所以你需要确定链接上的文字是下一页的箭头u'\xbb',这可能有是用 XPath 编写的。去,不过好像这是unicode转义符,由于编码原因不清楚,所以直接在外面判断,最终解析函数如下:

  def parse(self, response):items = []hxs = HtmlXPathSelector(response)posts = hxs.x('//h1/a/@href').extract()items.extend([self.make_requests_from_url(url) .replace(callback=self.parse_post)for url in posts])page_links = hxs.x('//div[@class="wp-pagenavi"]/a[not(@title)]')for page_links 中的链接:if link.x('text()').extract()[0] == u'\xbb':url = link.x('@href').extract()[0]items.append(self .make_requests_from_url(url)) 返回项目

  前半部分是解析需要爬取的博客正文的链接,后半部分是给出“下一页”的链接。需要注意的是,这里返回的列表并非都是字符串格式的URL。Scrapy希望得到Request对象,它可以携带比字符串格式的URL更多的东西,比如cookies或者回调。功能等。可以看到我们在创建博客正文的请求时替换了回调函数,因为默认的回调函数parse是专门用来解析文章列表等页面的,parse_post定义如下:

  def parse_post(self, response):item = BlogCrawlItem()item.url = unicode(response.url)item.raw = response.body_as_unicode()return [item]

  这很简单。返回一个 BlogCrawlItem 并将捕获的数据放入其中。你可以在这里做一些分析。比如你可以通过XPath来解析文本和标题,但是我倾向于后期做这些事情,比如Item Pipeline或者Later Offline stage。BlogCrawlItem 是 Scrapy 自动为我们定义的一个空类,继承自 ScrapedItem。在 items.py 中,我在这里添加了一些东西:

  from scrapy.item import ScrapedItem class BlogCrawlItem(ScrapedItem):def __init__(self):ScrapedItem.__init__(self)self.url =''def __str__(self):return'BlogCrawlItem(url: %s)'% self.url

  定义了__str__函数,只给出了URL,因为默认的__str__函数会显示所有的数据,所以看到爬取的时候,控制台日志会输出一些东西,就是把爬取到的网页的内容输出出来。-.-bb 这样就检索到了数据,最后只剩下存储数据的功能了。我们通过添加流水线来实现它。由于Python自带标准库中对Sqlite3的支持,所以我使用Sqlite数据库来存储。数据。将 pipelines.py 的内容替换为以下代码:

  import sqlite3from os import path from scrapy.core importsignalsfrom scrapy.xlib.pydispatch import dispatcher class SQLiteStorePipeline(object):filename ='data.sqlite'def __init__(self):self.conn = Nonedispatcher.connect(self.initialize,signals .engine_started)dispatcher.connect(self.finalize,signals.engine_stopped)def process_item(self, domain, item):self.conn.execute('插入博客值(?,?,?)',(item.url, item.raw, unicode(domain)))return itemdef initialize(self):if path.exists(self.filename):self.conn = sqlite3.connect(self.filename)else:self.conn = self .create_table(self.filename)def finalize(self):if self.conn is not None:mit()self.conn.close()self.conn = Nonedef create_table(self, filename):conn = sqlite3.@ >connect(filename)conn.execute("""create table blog(url text primary key,原创文本,域文本)""")mit()return conn

  在__init__函数中,使用dispatcher将两个信号连接到指定的函数,用于初始化和关闭数据库连接(记得关闭前commit,好像不会自动commit,如果直接关闭的话,似乎所有数据都是 Dd-.-) 丢失。当数据通过管道时,将调用 process_item 函数。这里我们将原创数据直接存入数据库,不做任何处理。如有必要,您可以添加额外的管道来提取和过滤数据,但我不会在这里详细介绍。最后,在 settings.py 中列出我们的管道:

  ITEM_PIPELINES = ['blog_crawl.pipelines.SQLiteStorePipeline']

  再次运行爬虫就OK了!:D 最后总结一下:一个高质量的爬虫是一个极其复杂的工程,但是如果你有一个好的工具,做一个专用的爬虫是相对容易的。Scrapy是一个非常轻量级的爬虫框架,极大的简化了爬虫的开发过程。另外,Scrapy的文档也很详细。如果觉得我的介绍省略了一些不清楚的地方,推荐阅读他的Tutorial。注意:本文开头的图片不是蜘蛛(当然!-,-bb),它来自*敏*感*词*《攻壳机动队》中的智库立驹(非常可爱!)。

  原文来源:张驰远的博客

  原文链接:

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线